diff --git a/.gitignore b/.gitignore index a9a260a..3a80ce1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,49 +1,50 @@ Makefile.in Makefile +compile +autom4te.cache aclocal.m4 config.guess -config.log config.status config.sub configure depcomp install-sh missing -booth.tar.bz2 +test-driver +*.trs +*.log *.rpm -Doxyfile -src/.deps -src/Makefile.in +*.o +.deps +.dirstamp + +booth-*.tar* + +conf/booth*.service +docs/*.8 +script/service-runnable +script/unit-test.py src/b_config.h.in src/b_config.h src/booth_config.h src/boothd src/stamp-h1 src/stamp-h2 +test/boothtestenv.py +test/runtests.py # cscope files cscope.* ncscope.* # ctags files tags -autom4te.cache -*.o - -test-driver -tools/.deps/ -tools/booth_resource_monitord - # vim temp files .*.sw? *~ +# test suite random files *.pyc +__pycache__ -/docs/boothd.8* -!/docs/boothd.8.txt - -doxygen - -/conf/booth*.service diff --git a/Makefile.am b/Makefile.am index 8346dbb..5716bd0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,174 +1,202 @@ # Copyright (c) 2009 Red Hat, Inc. # # Authors: Andrew Beekhof # Steven Dake (sdake@redhat.com) # # This software licensed under BSD license, the text of which follows: # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # - Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # - Neither the name of the MontaVista Software, Inc. nor the names of its # contributors may be used to endorse or promote products derived from this # software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF # THE POSSIBILITY OF SUCH DAMAGE. SPEC = $(PACKAGE_NAME).spec TARFILE = $(PACKAGE_NAME)-$(VERSION).tar.gz EXTRA_DIST = autogen.sh conf/booth.conf.example \ - $(bootharbitrator_SCRIPTS) $(boothsite_SCRIPTS) \ - $(boothnoarch_SCRIPTS) + script/booth-keygen script/lsb script/ocf script/service-runnable.in \ + script/unit-test.py.in script/wireshark-dissector.lua \ + test/arbtests.py test/assertions.py test/booth_path test/boothrunner.py \ + test/boothtestenv.py.in test/clientenv.py test/clienttests.py test/live_test.sh \ + test/runtests.py.in test/serverenv.py test/servertests.py test/sitetests.py \ + test/utils.py AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ - autoheader automake autoconf test_lense.sh + autoheader automake autoconf test_lense.sh \ + compile dist_doc_DATA = AUTHORS README COPYING README.upgrade-from-v0.1 boothconfdir = ${BOOTHSYSCONFDIR} boothconf_DATA = conf/booth.conf.example -boothsitedir = /usr/lib/ocf/resource.d/pacemaker +boothsitedir = $(ocfdir)/resource.d/pacemaker boothsite_SCRIPTS = script/ocf/booth-site -boothocfdir = /usr/lib/ocf/resource.d/booth +boothocfdir = $(ocfdir)/resource.d/booth boothocf_SCRIPTS = script/ocf/sharedrsc script/ocf/geostore -boothocflibdir = /usr/lib/ocf/lib/booth +boothocflibdir = $(ocfdir)/lib/booth boothocflib_DATA = script/ocf/geo_attr.sh bootharbitratordir = ${INITDDIR} bootharbitrator_SCRIPTS = script/lsb/booth-arbitrator boothnoarchdir = $(datadir)/$(PACKAGE_NAME) nodist_boothnoarch_SCRIPTS = script/service-runnable sbin_SCRIPTS = script/booth-keygen TESTS = test/runtests.py SUBDIRS = src docs conf coverity: cov-build --dir=cov make cov-analyze --dir cov --concurrency --wait-for-license cov-format-errors --dir cov install-exec-local: $(INSTALL) -d $(DESTDIR)/${boothconfdir} $(INSTALL) -d $(DESTDIR)/${bootharbitratordir} $(INSTALL) -d $(DESTDIR)/${boothsitedir} $(INSTALL) -d $(DESTDIR)/${boothocfdir} - $(INSTALL) -d $(DESTDIR)/${SOCKETDIR} $(INSTALL) -d $(DESTDIR)/${BOOTH_LIB_DIR} -m 750 -chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) $(DESTDIR)/${BOOTH_LIB_DIR} $(INSTALL) -d $(DESTDIR)/${BOOTH_CORE_DIR} -m 750 -chown $(CRM_DAEMON_USER):$(CRM_DAEMON_GROUP) $(DESTDIR)/${BOOTH_CORE_DIR} install-exec-hook: ln -sf ${sbindir}/boothd $(DESTDIR)/${sbindir}/booth ln -sf ${sbindir}/boothd $(DESTDIR)/${sbindir}/geostore uninstall-local: rmdir $(DESTDIR)/${boothconfdir} || :; rmdir $(DESTDIR)/${bootharbitratordir} || :; rmdir $(DESTDIR)/${boothsitedir} || :; - rmdir $(DESTDIR)/${SOCKETDIR} || :; rmdir $(DESTDIR)/${BOOTH_CORE_DIR} || :; rmdir $(DESTDIR)/${BOOTH_LIB_DIR} || :; test: check lint: for dir in src; do make -C $$dir lint; done +clean-local: + rm -rf test/*.pyc test/__pycache__ test/runtests.py test/boothtestenv.py + dist-clean-local: rm -f autoconf automake autoheader dist-hook: echo $(VERSION) > $(distdir)/.tarball-version +test/runtests.py: test/runtests.py.in test/boothtestenv.py + rm -f $@-t $@ + mkdir -p ${abs_top_builddir}/test + sed \ + -e 's#PYTHON_SHEBANG#${PYTHON_SHEBANG}#g' \ + -e 's#TEST_SRC_DIR#${abs_top_srcdir}/test#g' \ + -e 's#TEST_BUILD_DIR#${abs_top_builddir}/test#g' \ + $< > $@-t; + chmod a-w $@-t + chmod u+x $@-t + mv $@-t $@ + +test/boothtestenv.py: test/boothtestenv.py.in + rm -f $@-t $@ + mkdir -p ${abs_top_builddir}/test + sed \ + -e 's#TEST_SRC_DIR#${abs_top_srcdir}/test#g' \ + -e 's#TEST_BUILD_DIR#${abs_top_builddir}/test#g' \ + $< > $@-t; + chmod a-w $@-t + mv $@-t $@ + ## make rpm/srpm section. $(SPEC): $(SPEC).in rm -f $@-t $@ date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ if [ -f .tarball-version ]; then \ gitver="$(shell cat .tarball-version)" && \ rpmver=$$gitver && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ else \ gitver="$(shell git describe --tags --abbrev=4 --match='v*' HEAD 2>/dev/null)" && \ rpmver=`echo $$gitver | sed -e "s/^v//" -e "s/-.*//g"` && \ alphatag=`echo $$gitver | sed -e "s/.*-//" -e "s/^g//"` && \ vtag=`echo $$gitver | sed -e "s/-.*//g"` && \ numcomm=`git rev-list $$vtag..HEAD | wc -l` && \ git update-index --refresh > /dev/null 2>&1 || true && \ dirty=`git diff-index --name-only HEAD 2>/dev/null`; \ fi && \ if [ -n "$$dirty" ]; then dirty="dirty"; else dirty=""; fi && \ if [ "$$numcomm" = "0" ]; then \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#%glo.*alpha.*##g" \ -e "s#%glo.*numcomm.*##g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ else \ sed \ -e "s#@version@#$$rpmver#g" \ -e "s#@alphatag@#$$alphatag#g" \ -e "s#@numcomm@#$$numcomm#g" \ -e "s#@dirty@#$$dirty#g" \ -e "s#@date@#$$date#g" \ $< > $@-t; \ fi; \ if [ -z "$$dirty" ]; then sed -i -e "s#%glo.*dirty.*##g" $@-t; fi chmod a-w $@-t mv $@-t $@ $(TARFILE): $(MAKE) dist RPMBUILDOPTS = --define "_sourcedir $(abs_builddir)" \ --define "_specdir $(abs_builddir)" \ --define "_builddir $(abs_builddir)" \ --define "_srcrpmdir $(abs_builddir)" \ --define "_rpmdir $(abs_builddir)" srpm: clean autoreconf -if $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(WITH_LIST) $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean autoreconf -if $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(WITH_LIST) $(RPMBUILDOPTS) -ba $(SPEC) diff --git a/README-testing b/README-testing index 9788aea..71994f5 100644 --- a/README-testing +++ b/README-testing @@ -1,217 +1,226 @@ There's a booth-test package which contains two types of tests. It installs the necessary files into `/usr/share/booth/tests`. === Live tests (booth operation) BEWARE: Run this with _test_ clusters only! The live testing utility tests booth operation using the given `booth.conf`: $ /usr/share/booth/tests/test/live_test.sh booth.conf It is possible to run only specific tests. Run the script without arguments to see usage and the list of tests and netem network emulation functions. There are some restrictions on how booth.conf is formatted. There may be several tickets defined and all of them will be tested, one after another (they will be tested separately). The tickets must have expire and timeout parameters configured. Example booth.conf: ------------ transport="UDP" port="9929" arbitrator="10.2.12.53" arbitrator="10.2.13.82" site="10.2.12.101" site="10.2.13.101" site="10.121.187.99" ticket="ticket-A" expire = 30 timeout = 3 retries = 3 before-acquire-handler = /usr/share/booth/service-runnable d-src1 ------------ A split brain condition is also tested. For that to work, all sites need `iptables` installed. The supplied script `booth_path` is used to manipulate iptables rules. ==== Pacemaker configuration This is a sample pacemaker configuration for a single-node cluster: primitive booth ocf:pacemaker:booth-site primitive d-src1 ocf:heartbeat:Dummy rsc_ticket global-d-src1 ticket-A: d-src1 Additionally, you may also add an ocf:booth:sharedrsc resource to also check that the ticket is granted always to only one site: primitive shared ocf:booth:sharedrsc \ params dir="10.2.13.82:/var/tmp/boothtestdir" rsc_ticket global-shared ticket-A: shared Please adjust to your environment. ==== Network environment emulation To introduce packet loss or network delays, set the NETEM_ENV environment variable. There are currently three netem network emulation settings supported: - loss: all servers emulate packet loss (30% by default) - single_loss: the first site in the configuration emulates packet loss (30% by default) - net_delay: all servers emulate packet delay (100ms by default with random variation of 10%) The settings can be supplied by adding ':' to the emulator name. For instance: # NETEM_ENV=loss:50 /usr/share/booth/tests/test/live_test.sh booth.conf It is not necessary to run the test script on one of the sites. Just copy the script and make the test `booth.conf` available locally: $ scp testsite:/usr/share/booth/tests/test/live_test.sh . $ scp testsite:/etc/booth/booth.conf . $ sh live_test.sh booth.conf You need at least two sites and one arbitrator. The configuration can contain just one ticket. It is not necessary to configure the `before-acquire-handler`. Notes: - (BEWARE!) the supplied configuration files is copied to /etc/booth/booth.conf to all sites/arbitrators thus overwriting any existing configuration - the utility uses ssh to manage booth at all sites/arbitrators and logs in as user `root` - it is required that ssh public authentication works without providing the passphrase (otherwise it is impractical) - the log file is ./test_booth.log (it is actually a shell trace, with timestamps if you're running bash) - in case one of the tests fail, hb_report is created If you want to open a bug report, please attach all hb_reports and `test_booth.log`. === Simple tests (commandline, config file) Run (as non-root) + # make check + +or + + # make test/runtests.py # python test/runtests.py to run the tests written in python. +It is also possible to run the tests as a root when +"--allow-root-user" parameter is used or if +the BOOTH_RUNTESTS_ROOT_USER environment variable is defined. + === Unit tests These use gdb and pexpect to set boothd state to some configured value, injecting some input and looking at the output. # python script/unit-test.py src/boothd unit-tests/ Or, if using the 'booth-test' RPM, # python unit-test.py src/boothd unit-tests/ This must (currently?) be run as a non-root user; another optional argument is the test to start from, eg. '003'. Basically, boothd is started with the config file `unit-tests/booth.conf`, and gdb gets attached to it. Then, some ticket state is set, incoming messages are delivered, and outgoing messages and the state is compared to expected values. `unit-tests/_defaults.txt` has default values for the initial state and message data. Each test file consists of headers and key/value pairs: -------------------- ticket: state ST_STABLE message0: # optional comment for the log file header.cmd OP_ACCEPTING ticket.id "asdga" outgoing0: header.cmd OP_PREPARING last_ack_ballot 42 finally: new_ballot 1234 -------------------- A few details to the the above example: * Ticket states in RAM (`ticket`, `finally`) are written in host-endianness. * Message data (`messageN`, `outgoingN`) are automatically converted via `htonl` resp. `ntohl`. They are delivered/checked in the order defined by the integer `N` component. * Strings are done via `strcpy()` * `ticket` and `messageN` are assignment chunks * `finally` and `outgoingN` are compare chunks * In `outgoingN` you can check _both_ message data (keys with a `.` in them) and ticket state * Symbolic names are useable, GDB translates them for us * The test scripts in `unit-tests/` need to be named with 3 digits, an underscore, some text, and `.txt` * The "fake" `crm_ticket` script gets the current test via `UNIT_TEST`; test scripts can pass additional information via `UNIT_TEST_AUX`. ==== Tips and Hints There's another special header: `gdb__N__`. These lines are sent to GDB after injecting a message, but before waiting for an outgoing line. Values that contain `§` are sent as multiple lines to GDB. This means that a stanza like -------------------- gdb0: watch booth_conf->ticket[0].owner § commands § bt § c § end -------------------- will cause a watchpoint to be set, and when it is triggered a backtrace (`bt`) is written to the log file. This makes it easy to ask for additional data or check for a call-chain when hitting bugs that can be reproduced via such a unit-test. # vim: set ft=asciidoc : diff --git a/configure.ac b/configure.ac index 9c72bff..69a8912 100644 --- a/configure.ac +++ b/configure.ac @@ -1,623 +1,625 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # bootstrap / init AC_PREREQ([2.61]) AC_INIT([booth], [1.0], [users@clusterlabs.org]) AM_INIT_AUTOMAKE([-Wno-portability subdir-objects]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADER([src/b_config.h src/booth_config.h]) AC_CANONICAL_HOST AC_LANG([C]) AC_SUBST(WITH_LIST, [""]) dnl Fix default variables - "prefix" variable if not specified if test "$prefix" = "NONE"; then prefix="/usr" dnl Fix "localstatedir" variable if not specified if test "$localstatedir" = "\${prefix}/var"; then localstatedir="/var" fi dnl Fix "sysconfdir" variable if not specified if test "$sysconfdir" = "\${prefix}/etc"; then sysconfdir="/etc" fi dnl Fix "libdir" variable if not specified if test "$libdir" = "\${exec_prefix}/lib"; then if test -e /usr/lib64; then libdir="/usr/lib64" else libdir="/usr/lib" fi fi fi +if test "x${prefix}" != "x/usr"; then + ocfdir="${prefix}/usr/lib/ocf" +else + ocfdir="${prefix}/lib/ocf" +fi +AC_SUBST([ocfdir]) + if test "$srcdir" = "."; then AC_MSG_NOTICE([building in place srcdir:$srcdir]) AC_DEFINE([BUILDING_IN_PLACE], 1, [building in place]) else AC_MSG_NOTICE([building out of tree srcdir:$srcdir]) fi # 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 AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET AC_PROG_RANLIB AC_PATH_PROGS(PKGCONFIG, pkg-config) AC_PATH_PROGS(ASCIIDOC, asciidoc) AC_PATH_PROGS(ASCIIDOCTOR, asciidoctor) AC_PATH_PROGS(A2X, a2x) AM_CONDITIONAL(IS_ASCIIDOC, test x"${ASCIIDOC}" != x"") AM_CONDITIONAL(IS_A2X, test x"${A2X}" != x"") AM_CONDITIONAL(BUILD_ASCIIDOC, test x"${A2X}" != x"" || test x"${ASCIIDOCTOR}" != x"") AM_CONDITIONAL(BUILD_ASCIIDOC_HTML_MAN, (test "x${ASCIIDOC}" != "x" || test x"${ASCIIDOCTOR}" != x"") && test "x$with_html_man" = "xyes") # libgcrypt or mhash for hmac libgcrypt_installed="yes" AC_CHECK_HEADERS(gcrypt.h, , [libgcrypt_installed="no"],) AC_CHECK_LIB(gcrypt, gcry_md_open, , [libgcrypt_installed="no"]) AM_CONDITIONAL(BUILD_AUTH_C, test "x${libgcrypt_installed}" = "xyes") if test "x$libgcrypt_installed" = "xno"; then mhash_installed="yes" AC_CHECK_HEADERS(mhash.h, , [mhash_installed="no"],) AC_CHECK_LIB(mhash, mhash_init, , [mhash_installed="no"]) AM_CONDITIONAL(BUILD_AUTH_C, test "x${mhash_installed}" = "xyes") fi AC_CHECK_LIB([xml2], xmlReadDoc) PKG_CHECK_MODULES(XML, [libxml-2.0]) PKG_CHECK_MODULES(GLIB, [glib-2.0]) PKG_CHECK_MODULES([PCMK], [pacemaker-service],, [PKG_CHECK_MODULES([PCMK], [pcmk-service])]) # Python casing, prefer 3.3+ to 2.{6...} if test "x$PYTHON" = "x"; then AM_PATH_PYTHON([3.3],, [AM_PATH_PYTHON([2.6])]) fi PYTHON_SHEBANG="$PYTHON ${PYTHON_OPTS--Es}" -AC_ARG_VAR([PYTHON_SHEBANG], [Python invocation used in shebangs]) +AC_SUBST([PYTHON_SHEBANG]) # Checks for header files. AC_FUNC_ALLOCA AC_HEADER_DIRENT AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([arpa/inet.h fcntl.h limits.h netdb.h netinet/in.h stdint.h \ stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h \ sys/time.h syslog.h unistd.h sys/types.h getopt.h malloc.h \ sys/sockio.h utmpx.h]) AC_CHECK_HEADERS(heartbeat/glue_config.h) AC_CHECK_HEADERS(mhash.h) AC_CHECK_HEADER([zlib.h], [AC_SUBST(ZLIB_LIBS, ["-lz"])], [AC_MSG_ERROR([zlib development files required])]) saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PCMK_CFLAGS} ${GLIB_CFLAGS}" AC_CHECK_HEADER([crm/services.h], [], [AC_MSG_ERROR([crm/services.h header required])]) CPPFLAGS="${saved_CPPFLAGS}" # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_UID_T AC_C_INLINE AC_TYPE_INT16_T AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_INT8_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_HEADER_TIME AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T AC_C_VOLATILE # Checks for library functions. AC_FUNC_CLOSEDIR_VOID AC_FUNC_ERROR_AT_LINE AC_REPLACE_FNMATCH AC_FUNC_FORK AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_FUNC_REALLOC AC_FUNC_SELECT_ARGTYPES AC_TYPE_SIGNAL AC_FUNC_VPRINTF AC_CHECK_FUNCS([alarm alphasort atexit bzero dup2 endgrent endpwent fcntl \ getcwd getpeerucred getpeereid gettimeofday inet_ntoa memmove \ memset mkdir scandir select socket strcasecmp strchr strdup \ strerror strrchr strspn strstr \ sched_get_priority_max sched_setscheduler]) AC_CONFIG_FILES([Makefile src/Makefile docs/Makefile conf/Makefile]) AC_CONFIG_FILES([conf/booth-arbitrator.service conf/booth@.service]) -AC_CONFIG_FILES([script/unit-test.py test/runtests.py], - dnl Following required at least for "make check" - [chmod +x test/runtests.py]) +AC_CONFIG_FILES([script/unit-test.py]) AC_CONFIG_FILES([script/service-runnable], [chmod +x script/service-runnable]) # =============================================== # Helpers # =============================================== ## helper for CC stuff cc_supports_flag() { local CFLAGS="-Werror $@" AC_MSG_CHECKING(whether $CC supports "$@") AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int main(){return 0;}]])], [RC=0; AC_MSG_RESULT(yes)],[RC=1; AC_MSG_RESULT(no)]) return $RC } ## extract header or define try_extract_header_define() { AC_MSG_CHECKING(if $2 in $1 exists) Cfile=$srcdir/extract_define.$2.${$} printf "#include \n" > ${Cfile}.c printf "#include <%s>\n" $1 >> ${Cfile}.c printf "int main(int argc, char **argv) {\n" >> ${Cfile}.c printf "#ifdef %s\n" $2 >> ${Cfile}.c printf "printf(\"%%s\", %s);\n" $2 >> ${Cfile}.c printf "#endif \n return 0; }\n" >> ${Cfile}.c $CC $CFLAGS ${Cfile}.c -o ${Cfile} 2>/dev/null value= if test -x ${Cfile}; then value=`${Cfile} 2>/dev/null` fi if test x"${value}" == x""; then value=$3 AC_MSG_RESULT(default: $value) else AC_MSG_RESULT($value) fi printf $value rm -rf ${Cfile}.c ${Cfile} ${Cfile}.dSYM ${Cfile}.gcno } ## cleanup AC_MSG_NOTICE(Sanitizing prefix: ${prefix}) case $prefix in NONE) prefix=/usr/local;; esac AC_MSG_NOTICE(Sanitizing exec_prefix: ${exec_prefix}) case $exec_prefix in dnl For consistency with Corosync, map NONE->$prefix NONE) exec_prefix=$prefix;; prefix) exec_prefix=$prefix;; esac ## local defines PACKAGE_FEATURES="" LINT_FLAGS="-weak -unrecog +posixlib +ignoresigns -fcnuse \ -badflag -D__gnuc_va_list=va_list -D__attribute\(x\)=" # local options AC_ARG_ENABLE([fatal-warnings], [ --enable-fatal-warnings : enable fatal warnings. ], [ default="no" ]) AC_ARG_ENABLE([debug], [ --enable-debug : enable debug build. ], [ default="no" ]) AC_ARG_ENABLE([user-flags], [ --enable-user-flags : rely on user environment. ], [ default="no" ]) AC_ARG_ENABLE([coverage], [ --enable-coverage : coverage analysis of the codebase. ], [ default="no" ]) AC_ARG_WITH([initddir], [ --with-initddir=DIR : path to init script directory. ], [ INITDDIR="$withval" ], [ INITDDIR="$sysconfdir/init.d" ]) test -s .git_info && GIT_VER="`cat .git_info`" AC_ARG_WITH([build-version], [ --with-build-version=STR : build version ], [ BOOTH_BUILD_VERSION="$withval" ], [ BOOTH_BUILD_VERSION="${GIT_VER:-$PACKAGE_VERSION}" ]) AC_ARG_WITH([html_man], [ --without-html_man : Avoid generating man pages in HTML.], [], [with_html_man=yes]) AC_ARG_WITH([glue], [ --without-glue : Avoid libraries from (cluster-)glue project.], [], [with_glue=yes]) # figure out logging provider logging_provider="" if test "x$logging_provider" = "x" && test "x$with_glue" = "xyes"; then AC_CHECK_LIB([plumb], [cl_log], [logging_provider="libplumb"]) fi if test "x$logging_provider" = "x" && test "x$with_glue" = "xno"; then AC_CHECK_LIB([qb], [qb_log_real_], [logging_provider="libqb"]) fi case "$logging_provider" in libplumb) LOGGER="ha_logger" ;; libqb) PKG_CHECK_MODULES([LIBQB], [libqb]) AC_DEFINE([LOGGING_LIBQB], [], [use libqb as a logging provider]) PKG_CHECK_MODULES([LIBQB1], [libqb >= 1.0], [AC_DEFINE([LOGGING_LIBQB_MAJOR], [1], [libqb major version lower bound])], [AC_MSG_WARN([[syslog identifier will not get changed]])]) LOGGER="logger -t booth-script" ;; *) AC_MSG_ERROR([logging provider required (libplumb, or libqb when --without-glue)]) ;; esac AM_CONDITIONAL([LOGGING_LIBQB], [test "x$logging_provider" = "xlibqb"]) AC_SUBST([LOGGER]) # figure out range2random provider range2random_provider="" if test "x$range2random_provider" = "x" && test "x$with_glue" = "xyes"; then AC_CHECK_LIB([plumb], [get_next_random], [range2random_provider="libplumb"]) AC_CHECK_DECL([cl_rand_from_interval], [], [range2random_provider=""], [#include ]) fi if test "x$range2random_provider" = "x" && test "x$with_glue" = "xno"; then AC_CHECK_LIB([glib-2.0], [g_random_int_range], [range2random_provider="glib"]) fi case "$range2random_provider" in libplumb) ;; glib) PKG_CHECK_MODULES([GLIB], [glib-2.0]) AC_DEFINE([RANGE2RANDOM_GLIB], [], [use glib as a range2random provider]) ;; *) AC_MSG_ERROR([range2random provider required (libplumb, or glib when --without-glue)]) ;; esac AM_CONDITIONAL([RANGE2RANDOM_GLIB], [test "x$range2random_provider" = "xglib"]) # figure out nametag/distinguished-role provider nametag_provider="" if test "x$nametag_provider" = "x" && test "x$with_glue" != "xno"; then AC_CHECK_LIB([plumbgpl], [set_proc_title], [nametag_provider="libplumbgpl"]) fi if test "x$nametag_provider" = "x" && test "x$with_glue" = "xno"; then AC_SEARCH_LIBS([sd_notify], [systemd systemd-daemon], [nametag_provider="libsystemd"]) fi NOTIFY_ACCESS_SWITCH='# ' case "$nametag_provider" in libplumbgpl) ;; libsystemd) PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd],, [ PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd-daemon]) ]) AC_DEFINE([NAMETAG_LIBSYSTEMD], [], [use libsystemd as a nametag provider]) NOTIFY_ACCESS_SWITCH= ;; *) AC_MSG_ERROR([nametag provider required (libplumbgpl, or libsystemd when --without-glue)]) ;; esac AM_CONDITIONAL([NAMETAG_LIBSYSTEMD], [test "x$nametag_provider" = "xlibsystemd"]) AC_SUBST([NOTIFY_ACCESS_SWITCH]) # figure out if "coredump nursing" supported and desired coredump_nursing="no" if test "x$with_glue" != "xno"; then AC_CHECK_LIB([plumb], [cl_enable_coredumps], [coredump_nursing="libplumb"]) fi if test "x$coredump_nursing" != "xno"; then AC_DEFINE(COREDUMP_NURSING, [], [eligible for coredump nursing]) fi AM_CONDITIONAL([COREDUMP_NURSING], [test "x$coredump_nursing" != "xno"]) # define CRM daemon user & group CRM_DAEMON_USER=`try_extract_header_define glue_config.h HA_CCMUSER hacluster` AC_DEFINE_UNQUOTED(CRM_DAEMON_USER,"$CRM_DAEMON_USER", User to run Booth daemon as) AC_SUBST(CRM_DAEMON_USER) CRM_DAEMON_GROUP=`try_extract_header_define glue_config.h HA_APIGROUP haclient` AC_DEFINE_UNQUOTED(CRM_DAEMON_GROUP,"$CRM_DAEMON_GROUP", Group to run Booth daemon as) AC_SUBST(CRM_DAEMON_GROUP) # OS detection # THIS SECTION MUST DIE! CP=cp OS_LDL="-ldl" have_linux="no" case "$host_os" in *linux*) AC_DEFINE_UNQUOTED([BOOTH_LINUX], [1], [Compiling for Linux platform]) OS_CFLAGS="" OS_CPPFLAGS="-D_GNU_SOURCE" OS_LDFLAGS="" OS_DYFLAGS="-rdynamic" DARWIN_OPTS="" have_linux="yes" ;; darwin*) AC_DEFINE_UNQUOTED([BOOTH_DARWIN], [1], [Compiling for Darwin platform]) CP=rsync OS_CFLAGS="" OS_CPPFLAGS="" OS_LDFLAGS="" OS_DYFLAGS="" DARWIN_OPTS="-dynamiclib -bind_at_load \ -current_version ${SONAME} \ -compatibility_version ${SONAME} -install_name \$(libdir)/\$(@)" AC_DEFINE_UNQUOTED([MAP_ANONYMOUS], [MAP_ANON], [Shared memory define for Darwin platform]) AC_DEFINE_UNQUOTED([PATH_MAX], [4096], [Number of chars in a path name including nul]) AC_DEFINE_UNQUOTED([NAME_MAX], [255], [Number of chars in a file name]) ;; *bsd*) AC_DEFINE_UNQUOTED([BOOTH_BSD], [1], [Compiling for BSD platform]) AC_DEFINE_UNQUOTED([MAP_ANONYMOUS], [MAP_ANON], [Shared memory define for Darwin platform]) OS_CFLAGS="" OS_CPPFLAGS="-I/usr/local/include" OS_LDFLAGS="-L/usr/local/lib" OS_DYFLAGS="-export-dynamic" DARWIN_OPTS="" OS_LDL="" case "$host_os" in *freebsd[[234567]]*) ;; *freebsd*) AC_DEFINE_UNQUOTED([BOOTH_FREEBSD_GE_8], [1], [Compiling for FreeBSD >= 8 platform]) ;; esac ;; *solaris*) AC_DEFINE_UNQUOTED([BOOTH_SOLARIS], [1], [Compiling for Solaris platform]) AC_DEFINE_UNQUOTED([TS_CLASS], [1], [Prevent being scheduled RR]) AC_DEFINE_UNQUOTED([_SEM_SEMUN_UNDEFINED], [1], [The semun structure is undefined]) CP=rsync OS_CFLAGS="" OS_CPPFLAGS="-D_REENTRANT" OS_LDFLAGS="" OS_DYFLAGS="-Wl,-z,lazyload" DARWIN_OPTS="" SOLARIS_OPTS=" " ;; *) AC_MSG_ERROR([Unsupported OS? hmmmm]) ;; esac AC_SUBST(CP) # *FLAGS handling goes here 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 -U_FORTIFY_SOURCE" PACKAGE_FEATURES="$PACKAGE_FEATURES debug" else OPT_CFLAGS="-O3" fi # gdb flags if test "x${GCC}" = xyes; then GDB_FLAGS="-ggdb3" else GDB_FLAGS="-g" fi dnl Check for POSIX clock_gettime dnl AC_CACHE_CHECK([have clock_gettime],ac_cv_HAVE_CLOCK_GETTIME,[ AC_TRY_COMPILE([ #include ], [ struct timespec tv; clock_gettime(CLOCK_REALTIME, &tv); return 0;], ac_cv_HAVE_CLOCK_GETTIME=yes,ac_cv_HAVE_CLOCK_GETTIME=no,ac_cv_HAVE_CLOCK_GETTIME=cross)]) AM_CONDITIONAL(BUILD_TIMER_C, test x"$ac_cv_HAVE_CLOCK_GETTIME" = x"yes") # extra warnings EXTRA_WARNINGS="" WARNLIST=" all shadow missing-prototypes missing-declarations strict-prototypes declaration-after-statement pointer-arith write-strings 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 if test "x${enable_coverage}" = xyes && \ cc_supports_flag -ftest-coverage && \ cc_supports_flag -fprofile-arcs ; then AC_MSG_NOTICE([Enabling Coverage (enable -O0 by default)]) OPT_CFLAGS="-O0" COVERAGE_CFLAGS="-ftest-coverage -fprofile-arcs" COVERAGE_LDFLAGS="-ftest-coverage -fprofile-arcs" PACKAGE_FEATURES="$PACKAGE_FEATURES coverage" else COVERAGE_CFLAGS="" COVERAGE_LDFLAGS="" fi if test "x${enable_ansi}" = xyes && \ cc_supports_flag -std=iso9899:199409 ; then AC_MSG_NOTICE([Enabling ANSI Compatibility]) ANSI_CPPFLAGS="-ansi -D_GNU_SOURCE -DANSI_ONLY" PACKAGE_FEATURES="$PACKAGE_FEATURES ansi" else ANSI_CPPFLAGS="" fi if test "x${enable_fatal_warnings}" = xyes && \ cc_supports_flag -Werror ; then AC_MSG_NOTICE([Enabling Fatal Warnings (-Werror)]) WERROR_CFLAGS="-Werror" PACKAGE_FEATURES="$PACKAGE_FEATURES fatal-warnings" else WERROR_CFLAGS="" fi # don't add addtional cflags if test "x${enable_user_flags}" = xyes; then OPT_CFLAGS="" GDB_FLAGS="" EXTRA_WARNINGS="" fi # final build of *FLAGS CFLAGS="$ENV_CFLAGS $OPT_CFLAGS $GDB_FLAGS $OS_CFLAGS \ $COVERAGE_CFLAGS $EXTRA_WARNINGS $WERROR_CFLAGS $NSS_CFLAGS" CPPFLAGS="$ENV_CPPFLAGS $ANSI_CPPFLAGS $OS_CPPFLAGS $GLIB_CFLAGS $RESMON_CFLAGS $XML_CFLAGS" LDFLAGS="$ENV_LDFLAGS $COVERAGE_LDFLAGS $OS_LDFLAGS" LIBS="$LIBS $XML_LIBS" # substitute what we need: AC_SUBST([INITDDIR]) AC_SUBST([OS_DYFLAGS]) AC_SUBST([OS_LDL]) AM_CONDITIONAL(BUILD_DARWIN, test -n "${DARWIN_OPTS}") AM_CONDITIONAL(BUILD_SOLARIS, test -n "${SOLARIS_OPTS}") AC_SUBST([DARWIN_OPTS]) AC_SUBST([SOLARIS_OPTS]) AM_CONDITIONAL(BUILD_HTML_DOCS, test -n "${GROFF}") AC_SUBST([LINT_FLAGS]) -AC_DEFINE_UNQUOTED([SOCKETDIR], "$(eval echo ${SOCKETDIR})", [Socket directory]) - BOOTH_LIB_DIR=${localstatedir}/lib/booth BOOTH_CORE_DIR=${localstatedir}/lib/booth/cores BOOTHSYSCONFDIR=${sysconfdir}/booth AC_SUBST([HAVE_LOG_CIB_DIFF]) AC_SUBST([HAVE_XML_LOG_PATCHSET]) AC_SUBST([BOOTH_LIB_DIR]) AC_SUBST([BOOTH_CORE_DIR]) AC_SUBST([BOOTHSYSCONFDIR]) AC_SUBST([BOOTH_BUILD_VERSION]) AC_DEFINE_UNQUOTED([BOOTH_LIB_DIR], "$(eval echo ${BOOTH_LIB_DIR})", [booth lib directory]) AC_DEFINE_UNQUOTED([BOOTH_CORE_DIR], "$(eval echo ${BOOTH_CORE_DIR})", [booth working directory]) AC_DEFINE_UNQUOTED([BOOTHSYSCONFDIR], "$(eval echo ${BOOTHSYSCONFDIR})", [booth config directory]) AC_DEFINE_UNQUOTED([PACKAGE_FEATURES], "${PACKAGE_FEATURES}", [booth built-in features]) AC_DEFINE_UNQUOTED([BOOTH_BUILD_VERSION], "${BOOTH_BUILD_VERSION}", [booth build version]) AC_OUTPUT AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${VERSION} (Build: ${BOOTH_BUILD_VERSION})]) AC_MSG_RESULT([ Prefix = ${prefix}]) AC_MSG_RESULT([ Executables = ${sbindir}]) AC_MSG_RESULT([ Man pages = ${mandir}]) AC_MSG_RESULT([ Doc dir = ${docdir}]) AC_MSG_RESULT([ Libraries = ${libdir}]) AC_MSG_RESULT([ Header files = ${includedir}]) AC_MSG_RESULT([ Arch-independent files = ${datadir}]) AC_MSG_RESULT([ State information = ${localstatedir}]) AC_MSG_RESULT([ System configuration = ${sysconfdir}]) AC_MSG_RESULT([ System init.d directory = ${INITDDIR}]) AC_MSG_RESULT([ booth config dir = ${BOOTHSYSCONFDIR}]) -AC_MSG_RESULT([ SOCKETDIR = ${SOCKETDIR}]) AC_MSG_RESULT([ Features = ${PACKAGE_FEATURES}]) AC_MSG_RESULT([ Logging provider = ${logging_provider}]) AC_MSG_RESULT([ Range2random provider = ${range2random_provider}]) AC_MSG_RESULT([ Nametag provider = ${nametag_provider}]) AC_MSG_RESULT([ Coredump nursing = ${coredump_nursing}]) AC_MSG_RESULT([ Working directory = ${BOOTH_CORE_DIR}]) AC_MSG_RESULT([ HA group name = ${CRM_DAEMON_GROUP}]) AC_MSG_RESULT([ HA user name = ${CRM_DAEMON_USER}]) AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE build info:]) AC_MSG_RESULT([ Library SONAME = ${SONAME}]) LIB_MSG_RESULT(m4_shift(local_soname_list))dnl AC_MSG_RESULT([ Default optimization = ${OPT_CFLAGS}]) AC_MSG_RESULT([ Default debug options = ${GDB_CFLAGS}]) AC_MSG_RESULT([ Extra compiler warnings = ${EXTRA_WARNING}]) AC_MSG_RESULT([ Env. defined CFLAG = ${ENV_CFLAGS}]) AC_MSG_RESULT([ Env. defined CPPFLAGS = ${ENV_CPPFLAGS}]) AC_MSG_RESULT([ Env. defined LDFLAGS = ${ENV_LDFLAGS}]) AC_MSG_RESULT([ OS defined CFLAGS = ${OS_CFLAGS}]) AC_MSG_RESULT([ OS defined CPPFLAGS = ${OS_CPPFLAGS}]) AC_MSG_RESULT([ OS defined LDFLAGS = ${OS_LDFLAGS}]) AC_MSG_RESULT([ OS defined LDL = ${OS_LDL}]) AC_MSG_RESULT([ OS defined DYFLAGS = ${OS_DYFLAGS}]) AC_MSG_RESULT([ ANSI defined CPPFLAGS = ${ANSI_CPPFLAGS}]) AC_MSG_RESULT([ Coverage CFLAGS = ${COVERAGE_CFLAGS}]) AC_MSG_RESULT([ Coverage LDFLAGS = ${COVERAGE_LDFLAGS}]) AC_MSG_RESULT([ Fatal War. CFLAGS = ${WERROR_CFLAGS}]) AC_MSG_RESULT([ Final CFLAGS = ${CFLAGS}]) AC_MSG_RESULT([ Final CPPFLAGS = ${CPPFLAGS}]) AC_MSG_RESULT([ Final LDFLAGS = ${LDFLAGS}]) diff --git a/docs/Makefile.am b/docs/Makefile.am index 8fbc76f..11af73c 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -1,56 +1,58 @@ # # docs: booth manual pages # # Copyright (C) 2014 Dejan Muhamedagic # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = boothd.8.txt booth-keygen.8.txt fsm-full.dot fsm-netfail.dot fsm-normal.dot geostore.8.txt + asciiman = boothd.8.txt booth-keygen.8.txt geostore.8.txt doc_DATA = $(generated_docs) generated_docs = generated_mans = HTML_GENERATOR = MANPAGE_GENERATOR = if BUILD_ASCIIDOC_HTML_MAN generated_docs += $(ascii:%.txt=%.html) $(asciiman:%.txt=%.html) endif if BUILD_ASCIIDOC generated_mans += $(asciiman:%.8.txt=%.8) $(generated_mans): $(asciiman) man8_MANS = $(generated_mans) endif if IS_ASCIIDOC -HTML_GENERATOR += $(ASCIIDOC) --unsafe --backend=xhtml11 +HTML_GENERATOR += $(ASCIIDOC) --unsafe --backend=xhtml11 --destination-dir=$(abs_builddir)/ else -HTML_GENERATOR += $(ASCIIDOCTOR) -b xhtml5 +HTML_GENERATOR += $(ASCIIDOCTOR) -b xhtml5 --destination-dir=$(abs_builddir)/ endif if IS_A2X -MANPAGE_GENERATOR += $(A2X) -f manpage +MANPAGE_GENERATOR += $(A2X) -f manpage --destination-dir=$(abs_builddir)/ else -MANPAGE_GENERATOR += $(ASCIIDOCTOR) -b manpage +MANPAGE_GENERATOR += $(ASCIIDOCTOR) -b manpage --destination-dir=$(abs_builddir)/ endif %.html: %.txt $(HTML_GENERATOR) $< %.8: %.8.txt $(MANPAGE_GENERATOR) $< clean-local: -rm -rf $(generated_docs) $(generated_mans) diff --git a/src/Makefile.am b/src/Makefile.am index 455c152..8598725 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,57 +1,58 @@ MAINTAINERCLEANFILES = Makefile.in AM_CFLAGS = -fPIC -Werror -funsigned-char -Wno-pointer-sign AM_CPPFLAGS = -I$(top_builddir)/include sbin_PROGRAMS = boothd -boothd_SOURCES = config.c main.c raft.c ticket.c transport.c \ +boothd_SOURCES = config.c main.c raft.c ticket.c transport.c \ pacemaker.c handler.c request.c attr.c manual.c -noinst_HEADERS = booth.h pacemaker.h \ - config.h log.h raft.h ticket.h transport.h handler.h request.h attr.h manual.h +noinst_HEADERS = \ + attr.h booth.h handler.h log.h pacemaker.h request.h timer.h \ + auth.h config.h inline-fn.h manual.h raft.h ticket.h transport.h if BUILD_TIMER_C -boothd_SOURCES += timer.c +boothd_SOURCES += timer.c endif if BUILD_AUTH_C -boothd_SOURCES += auth.c +boothd_SOURCES += auth.c endif boothd_LDFLAGS = $(OS_DYFLAGS) -L./ boothd_LDADD = -lm $(GLIB_LIBS) $(ZLIB_LIBS) $(PCMK_LIBS) boothd_CFLAGS = $(GLIB_CFLAGS) $(PCMK_CFLAGS) if !LOGGING_LIBQB boothd_LDADD += -lplumb else boothd_LDADD += $(LIBQB_LIBS) boothd_SOURCES += alt/logging_libqb.c noinst_HEADERS += alt/logging_libqb.h endif if !RANGE2RANDOM_GLIB boothd_LDADD += -lplumb else boothd_LDADD += $(GLIB_LIBS) boothd_SOURCES += alt/range2random_glib.c noinst_HEADERS += alt/range2random_glib.h endif if !NAMETAG_LIBSYSTEMD boothd_LDADD += -lplumbgpl else boothd_LDADD += $(LIBSYSTEMD_LIBS) boothd_SOURCES += alt/nametag_libsystemd.c noinst_HEADERS += alt/nametag_libsystemd.h endif if COREDUMP_NURSING boothd_LDADD += -lplumb endif lint: -splint $(INCLUDES) $(LINT_FLAGS) $(CFLAGS) *.c diff --git a/test/boothtestenv.py b/test/boothtestenv.py.in similarity index 86% rename from test/boothtestenv.py rename to test/boothtestenv.py.in index 7efef27..12d9807 100644 --- a/test/boothtestenv.py +++ b/test/boothtestenv.py.in @@ -1,73 +1,71 @@ import os import subprocess import time import tempfile import unittest from assertions import BoothAssertions class BoothTestEnvironment(unittest.TestCase, BoothAssertions): - test_src_path = os.path.abspath(os.path.dirname(__file__)) - dist_path = os.path.join(test_src_path, '..' ) - src_path = os.path.join(dist_path, 'src' ) - boothd_path = os.path.join(src_path, 'boothd') - conf_path = os.path.join(dist_path, 'conf' ) - example_config_path = os.path.join(conf_path, 'booth.conf.example') + abs_test_src_path = os.path.abspath('TEST_SRC_DIR') + example_config_path = os.path.join(abs_test_src_path, '../conf/booth.conf.example') + abs_test_build_path = os.path.abspath('TEST_BUILD_DIR') + boothd_path = os.path.join(abs_test_build_path, '../src/boothd') def setUp(self): if not self._testMethodName.startswith('test_'): raise RuntimeError("unexpected test method name: " + self._testMethodName) self.test_name = self._testMethodName[5:] self.test_path = os.path.join(self.test_run_path, self.test_name) os.makedirs(self.test_path) # Give all users permisions for temp directory so boothd running as "hacluster" # can delete the lock file if os.geteuid() == 0: os.chmod(self.test_path, 0o777) self.ensure_boothd_not_running() def ensure_boothd_not_running(self): # Need to redirect STDERR in case we're not root, in which # case netstat's -p option causes a warning. However we only # want to kill boothd processes which we own; -p will list the # pid for those and only those, which is exactly what we want # here. subprocess.call("netstat -tpln 2>&1 | perl -lne 'm,LISTEN\s+(\d+)/boothd, and kill 15, $1'", shell=True) def get_tempfile(self, identity): tf = tempfile.NamedTemporaryFile( prefix='%s.%d.' % (identity, time.time()), dir=self.test_path, delete=False ) return tf.name def init_log(self): self.log_file = self.get_tempfile('log') os.putenv('HA_debugfile', self.log_file) # See cluster-glue/lib/clplumbing/cl_log.c def read_log(self): if not os.path.exists(self.log_file): return '' l = open(self.log_file) msgs = ''.join(l.readlines()) l.close() return msgs def check_return_code(self, pid, return_code, expected_exitcode): if return_code is None: print("pid %d still running" % pid) if expected_exitcode is not None: self.fail("expected exit code %d, not long-running process" % expected_exitcode) else: print("pid %d exited with code %d" % (pid, return_code)) if expected_exitcode is None: msg = "should not exit" else: msg = "should exit with code %s" % expected_exitcode msg += "\nLog follows (see %s)" % self.log_file msg += "\nN.B. expect mlockall/setscheduler errors when running tests non-root" msg += "\n-----------\n%s" % self.read_log() self.assertEqual(return_code, expected_exitcode, msg) diff --git a/test/runtests.py.in b/test/runtests.py.in index 45100ab..687495c 100644 --- a/test/runtests.py.in +++ b/test/runtests.py.in @@ -1,77 +1,80 @@ -#!@PYTHON_SHEBANG@ +#!PYTHON_SHEBANG import os import shutil import sys import tempfile import time import unittest +sys.path.append('TEST_SRC_DIR') +sys.path.append('TEST_BUILD_DIR') + from clienttests import ClientConfigTests from sitetests import SiteConfigTests #from arbtests import ArbitratorConfigTests if __name__ == '__main__': # Likely assumption for the root exclusion is the amount of risk # associated with what naturally accompanies root privileges: # - accidental overwrite (eventually also deletion) of unrelated, # legitimate and perhaps vital files # - accidental termination of unrelated, legitimate and perhaps # vital processes # - and so forth, possibly amplified with awkward parallel test # suite run scenarios (containers partly sharing state, etc.) # # Nonetheless, there are cases like self-contained CI runs where # all these concerns are absent, so allow opt-in relaxing of this. # Alternatively, the config generator could inject particular # credentials for a booth proces to use, but that might come too # late to address the above concerns reliably. if (os.geteuid() == 0 and "--allow-root-user" not in sys.argv and not(os.environ.get("BOOTH_RUNTESTS_ROOT_USER"))): sys.stderr.write("Must be run non-root; aborting.\n") sys.exit(1) tmp_path = '/tmp/booth-tests' if not os.path.exists(tmp_path): os.makedirs(tmp_path) test_run_path = tempfile.mkdtemp(prefix='%d.' % time.time(), dir=tmp_path) if os.geteuid() == 0: # Give all users at least rx permisions for temp directory so hacluster running booth # can delete lock file os.chmod(test_run_path, 0o755) suite = unittest.TestSuite() testclasses = [ SiteConfigTests, #ArbitratorConfigTests, ClientConfigTests, ] for testclass in testclasses: testclass.test_run_path = test_run_path suite.addTests(unittest.TestLoader().loadTestsFromTestCase(testclass)) runner_args = { #'verbosity' : 2, } major, minor, micro, releaselevel, serial = sys.version_info if major > 2 or (major == 2 and minor >= 7): # New in 2.7 runner_args['buffer'] = True runner_args['failfast'] = True pass if os.geteuid() != 0: # not root, so safe # needed because old instances might still use the UDP port. os.system("killall boothd") runner = unittest.TextTestRunner(**runner_args) result = runner.run(suite) if result.wasSuccessful(): shutil.rmtree(test_run_path) sys.exit(0) else: print("Left %s for debugging" % test_run_path) sys.exit(1)