diff --git a/.gitignore b/.gitignore index 6e7ceb8..72b81d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,56 +1,55 @@ Makefile.in Makefile compile autom4te.cache aclocal.m4 config.guess config.status config.sub configure depcomp install-sh missing test-driver *.trs *.log *.rpm *.o .deps .version .dirstamp booth-*.tar* conf/booth*.service docs/*.8 docs/*.8.html 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 booth.spec booth.pc # cscope files cscope.* ncscope.* # ctags files tags # vim temp files .*.sw? *~ # test suite random files *.pyc __pycache__ cov* diff --git a/Makefile.am b/Makefile.am index acd4c1e..b646658 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,251 +1,250 @@ # 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 \ script/booth-keygen script/lsb script/ocf script/service-runnable.in \ - script/unit-test.py.in script/wireshark-dissector.lua \ + 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 \ - unit-tests \ contrib \ icons \ $(SPEC).in booth-rpmlintrc \ .version build-aux/git-version-gen build-aux/PKG_CHECK_VAR.m4 \ build-aux/gitlog-to-changelog build-aux/release.mk AUTOMAKE_OPTIONS = foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure depcomp \ config.guess config.sub missing install-sh \ autoheader automake autoconf test_lense.sh \ compile # Don't try to install files outside build directory for "make distcheck". AM_DISTCHECK_CONFIGURE_FLAGS = --with-ocfdir="$$dc_install_base/lib/ocf" dist_doc_DATA = AUTHORS README COPYING README.upgrade-from-v0.1 README-testing boothconfdir = ${BOOTHSYSCONFDIR} boothconf_DATA = conf/booth.conf.example boothsitedir = $(ocfdir)/resource.d/pacemaker boothsite_SCRIPTS = script/ocf/booth-site boothocfdir = $(ocfdir)/resource.d/booth boothocf_SCRIPTS = script/ocf/sharedrsc script/ocf/geostore 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 pkgconfigdir = $(datadir)/pkgconfig pkgconfig_DATA = booth.pc 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)/${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)/${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 cov* $(SPEC) dist-clean-local: rm -f autoconf automake autoheader # release/versioning BUILT_SOURCES = .version .version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: gen-ChangeLog 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. $(abs_builddir)/booth-rpmlintrc: cat $(abs_srcdir)/booth-rpmlintrc > booth-rpmlintrc $(SPEC): $(SPEC).in .version config.status $(abs_builddir)/booth-rpmlintrc rm -f $@-t $@ date="$(shell LC_ALL=C date "+%a %b %d %Y")" && \ gvgver="`cd $(abs_srcdir); build-aux/git-version-gen --fallback $(VERSION) .tarball-version .gitarchivever`" && \ if [ "$$gvgver" = "`echo $$gvgver | sed 's/-/./'`" ];then \ rpmver="$$gvgver" && \ alphatag="" && \ dirty="" && \ numcomm="0"; \ else \ gitver="`echo $$gvgver | sed 's/\(.*\)\./\1-/'`" && \ rpmver=`echo $$gitver | sed 's/-.*//g'` && \ alphatag=`echo $$gvgver | sed 's/[^-]*-\([^-]*\).*/\1/'` && \ numcomm=`echo $$gitver | sed 's/[^-]*-\([^-]*\).*/\1/'` && \ dirty="" && \ if [ "`echo $$gitver | sed 's/^.*-dirty$$//g'`" = "" ];then \ dirty="dirty"; \ fi \ fi && \ if [ "$$numcomm" = "0" ]; then numcomm=""; fi && \ if [ -n "$$numcomm" ]; then numcomm="%global numcomm $$numcomm"; fi && \ if [ "$$alphatag" = "$$gitver" ]; then alphatag=""; fi && \ if [ -n "$$alphatag" ]; then alphatag="%global alphatag $$alphatag"; fi && \ if [ -n "$$dirty" ]; then dirty="%global dirty dirty"; fi && \ 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" \ $(abs_srcdir)/$@.in > $@-t; sed -i -e "s#@uname@#$(CRM_DAEMON_USER)#g" $@-t sed -i -e "s#@gname@#$(CRM_DAEMON_GROUP)#g" $@-t if BUILD_ASCIIDOC_HTML_MAN sed -i -e "s#@bcond_html_man@#bcond_without#g" $@-t else sed -i -e "s#@bcond_html_man@#bcond_with#g" $@-t endif if IS_ASCIIDOC sed -i -e "s#@asciidoc@#asciidoc#g" $@-t else sed -i -e "s#@asciidoc@#asciidoctor#g" $@-t endif if LOGGING_LIBQB sed -i -e "s#@bcond_glue@#bcond_with#g" $@-t else sed -i -e "s#@bcond_glue@#bcond_without#g" $@-t endif if PYTHON_IS_VERSION3 sed -i -e "s#@bcond_python3@#bcond_without#g" $@-t else sed -i -e "s#@bcond_python3@#bcond_with#g" $@-t endif if RUN_BUILD_TESTS sed -i -e "s#@bcond_run_build_tests@#bcond_without#g" $@-t else sed -i -e "s#@bcond_run_build_tests@#bcond_with#g" $@-t endif chmod a-w $@-t mv $@-t $@ rm -f $@-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 $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(RPMBUILDOPTS) --nodeps -bs $(SPEC) rpm: clean $(MAKE) $(SPEC) $(TARFILE) rpmbuild $(RPMBUILDOPTS) -ba $(SPEC) gen_start_date = 2000-01-01 .PHONY: gen-ChangeLog gen-ChangeLog: if test -d $(abs_srcdir)/.git; then \ LC_ALL=C $(top_srcdir)/build-aux/gitlog-to-changelog \ --since=$(gen_start_date) > $(distdir)/cl-t; \ rm -f $(distdir)/ChangeLog; \ mv $(distdir)/cl-t $(distdir)/ChangeLog; \ fi diff --git a/README-testing b/README-testing index c5aadfe..8a97da2 100644 --- a/README-testing +++ b/README-testing @@ -1,231 +1,144 @@ 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. By default tests uses TCP port based on current PID in range from 9929 to 10937 to allow running multiple instances in parallel. It is possible to use "--single-instance" parameter or define BOOTH_RUNTESTS_SINGLE_INSTANCE environment variable to make tests use only single port (9929), but parallel instances will fail. - -=== 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/booth.spec.in b/booth.spec.in index 0319245..9f67fff 100644 --- a/booth.spec.in +++ b/booth.spec.in @@ -1,279 +1,279 @@ # keep around ready for later user @ALPHATAG@ @NUMCOMM@ @DIRTY@ %global gitver %{?numcomm:.%{numcomm}}%{?alphatag:-%{alphatag}}%{?dirty:-%{dirty}} %global gittarver %{?numcomm:-%{numcomm}}%{?alphatag:-g%{alphatag}}%{?dirty:-%{dirty}} %@bcond_html_man@ html_man %@bcond_glue@ glue %@bcond_python3@ python3 %@bcond_run_build_tests@ run_build_tests ## User and group to use for nonprivileged services (should be in sync with pacemaker) %global uname @uname@ %global gname @gname@ # Defined on RHEL 8 and Fedora %{!?__python2: %global __python2 /usr/bin/python2} %{!?__python3: %global __python3 /usr/bin/python3} %if 0%{?with_python3} %global __python_full_path %{__python3} %else %global __python_full_path %{__python2} %endif %if 0%{?suse_version} %global booth_docdir %{_defaultdocdir}/%{name} %else # newer fedora distros have _pkgdocdir, rely on that when # available %{!?_pkgdocdir: %global _pkgdocdir %%{_docdir}/%{name}-%{version}} # Directory where we install documentation %global booth_docdir %{_pkgdocdir} %endif %global test_path %{_datadir}/booth/tests %if 0%{?suse_version} %define _libexecdir %{_libdir} %define _fwdefdir %{_libexecdir}/firewalld/services %endif %define with_extra_warnings 0 %define with_debugging 0 %define without_fatal_warnings 1 %if 0%{?fedora} || 0%{?centos} || 0%{?rhel} %define pkg_group System Environment/Daemons %else %define pkg_group Productivity/Clustering/HA %endif %global release 1 Name: booth Url: https://github.com/ClusterLabs/booth Summary: Ticket Manager for Multi-site Clusters License: GPL-2.0-or-later Group: %{pkg_group} Version: @version@ Release: %{?numcomm:%{numcomm}.}%{release}%{?alphatag:.%{alphatag}}%{?dirty:.%{dirty}}%{?alphatag:.git}%{?dist} Source: https://github.com/ClusterLabs/booth/archive/v%{version}%{?gittarver}/%{name}-%{version}%{?gitver}.tar.gz Source1: %name-rpmlintrc BuildRequires: @asciidoc@ BuildRequires: autoconf BuildRequires: automake BuildRequires: pkgconfig %if 0%{?suse_version} BuildRequires: glib2-devel # SuSEFirewall2 replaced by Firewalld (fate#323460) BuildRequires: firewall-macros %else BuildRequires: pkgconfig(glib-2.0) %endif BuildRequires: libgcrypt-devel %if 0%{?fedora} || 0%{?centos} || 0%{?rhel} BuildRequires: pacemaker-libs-devel %else %if 0%{?suse_version} > 1500 BuildRequires: libpacemaker3-devel %else BuildRequires: libpacemaker-devel %endif %endif %if 0%{?with_glue} %if 0%{?fedora} || 0%{?centos} || 0%{?rhel} BuildRequires: cluster-glue-libs-devel %else BuildRequires: libglue-devel %endif %else # logging provider BuildRequires: pkgconfig(libqb) # random2range provider BuildRequires: pkgconfig(glib-2.0) # nametag provider BuildRequires: pkgconfig(libsystemd) %endif BuildRequires: libxml2-devel BuildRequires: zlib-devel %if 0%{?fedora} || 0%{?centos} || 0%{?rhel} Requires: pacemaker >= 1.1.8 %if 0%{?with_glue} Requires: cluster-glue-libs >= 1.0.6 %endif %else Requires: pacemaker-ticket-support >= 2.0 %endif # for check scriptlet %if 0%{?with_python3} BuildRequires: python3-devel %else BuildRequires: python %endif # For Fedora compatibility Provides: booth-core Provides: booth-site Provides: booth-arbitrator %description Booth manages tickets which authorize cluster sites located in geographically dispersed locations to run resources. It facilitates support of geographically distributed clustering in Pacemaker. %prep %setup -q -n %{name}-%{version}%{?gitver} %build ./autogen.sh %configure \ --with-initddir=%{_initrddir} \ --docdir=%{booth_docdir} \ %{?with_html_man:--with-html_man} \ %{!?with_glue:--without-glue} \ PYTHON=%{__python_full_path} make %install make DESTDIR=$RPM_BUILD_ROOT install docdir=%{booth_docdir} mkdir -p %{buildroot}/%{_mandir}/man8/ gzip < docs/boothd.8 > %{buildroot}/%{_mandir}/man8/booth.8.gz ln %{buildroot}/%{_mandir}/man8/booth.8.gz %{buildroot}/%{_mandir}/man8/boothd.8.gz %if %{defined _unitdir} # systemd mkdir -p %{buildroot}/%{_unitdir} cp -a conf/booth@.service %{buildroot}/%{_unitdir}/booth@.service cp -a conf/booth-arbitrator.service %{buildroot}/%{_unitdir}/booth-arbitrator.service ln -s /usr/sbin/service %{buildroot}%{_sbindir}/rcbooth-arbitrator %else # sysV init ln -s ../../%{_initddir}/booth-arbitrator %{buildroot}%{_sbindir}/rcbooth-arbitrator %endif #install test-parts mkdir -p %{buildroot}/%{test_path}/conf -cp -a unit-tests/ script/unit-test.py test %{buildroot}/%{test_path}/ +cp -a test %{buildroot}/%{test_path}/ cp -a conf/booth.conf.example %{buildroot}/%{test_path}/conf/ chmod +x %{buildroot}/%{test_path}/test/booth_path chmod +x %{buildroot}/%{test_path}/test/live_test.sh mkdir -p %{buildroot}/%{test_path}/src/ ln -s %{_sbindir}/boothd %{buildroot}/%{test_path}/src/ rm -f %{buildroot}/%{test_path}/test/*.pyc # Generate runtests.py and boothtestenv.py sed -e 's#PYTHON_SHEBANG#%{__python_full_path} -Es#g' \ -e 's#TEST_SRC_DIR#%{test_path}/test#g' \ -e 's#TEST_BUILD_DIR#%{test_path}/test#g' \ %{buildroot}/%{test_path}/test/runtests.py.in > %{buildroot}/%{test_path}/test/runtests.py chmod +x %{buildroot}/%{test_path}/test/runtests.py sed -e 's#PYTHON_SHEBANG#%{__python_full_path} -Es#g' \ -e 's#TEST_SRC_DIR#%{test_path}/test#g' \ -e 's#TEST_BUILD_DIR#%{test_path}/test#g' \ %{buildroot}/%{test_path}/test/boothtestenv.py.in > %{buildroot}/%{test_path}/test/boothtestenv.py %if 0%{?suse_version} #Firewalld rule mkdir -p $RPM_BUILD_ROOT/%{_fwdefdir} install -m 644 contrib/geo-cluster.firewalld.xml $RPM_BUILD_ROOT/%{_fwdefdir}/booth.xml #install -m 644 %{S:2} $RPM_BUILD_ROOT/%{_fwdefdir}/booth %post %firewalld_reload %endif %check %if 0%{?with_run_build_tests} echo "%%with_run_build_tests set to %with_run_build_tests; including tests" make check %else echo "%%with_run_build_tests set to %with_run_build_tests; skipping tests" %endif %files %{_sbindir}/booth %{_sbindir}/boothd %{_sbindir}/booth-keygen %{_sbindir}/geostore %{_mandir}/man8/booth.8.gz %{_mandir}/man8/boothd.8.gz %{_mandir}/man8/booth-keygen.8.gz %{_mandir}/man8/geostore.8.gz %dir /usr/lib/ocf %dir /usr/lib/ocf/resource.d %dir /usr/lib/ocf/resource.d/pacemaker %dir /usr/lib/ocf/resource.d/booth %dir /usr/lib/ocf/lib %dir /usr/lib/ocf/lib/booth %dir %{_sysconfdir}/booth %dir %attr (750, %{uname}, %{gname}) %{_var}/lib/booth/ %dir %attr (750, %{uname}, %{gname}) %{_var}/lib/booth/cores %{_sbindir}/rcbooth-arbitrator /usr/lib/ocf/resource.d/pacemaker/booth-site /usr/lib/ocf/lib/booth/geo_attr.sh /usr/lib/ocf/resource.d/booth/geostore %config %{_sysconfdir}/booth/booth.conf.example %if 0%{?suse_version} %dir %{_libexecdir}/firewalld %dir %{_fwdefdir} %{_fwdefdir}/booth.xml %endif %if %{defined _unitdir} %{_unitdir}/booth@.service %{_unitdir}/booth-arbitrator.service %exclude %{_initddir}/booth-arbitrator %else %{_initddir}/booth-arbitrator %endif %dir %{_datadir}/booth %{_datadir}/booth/service-runnable %dir %{_datadir}/pkgconfig %{_datadir}/pkgconfig/booth.pc %doc AUTHORS README COPYING %doc README.upgrade-from-v0.1 %if 0%{?with_html_man} %{booth_docdir}/booth-keygen.8.html %{booth_docdir}/boothd.8.html %{booth_docdir}/geostore.8.html %endif %package test Summary: Test scripts for Booth Group: %{pkg_group} Requires: booth Requires: gdb Requires: %{__python_full_path} %if 0%{?with_python3} Requires: python3-pexpect %else Requires: python-pexpect %endif %description test This package contains automated tests for Booth, the Cluster Ticket Manager for Pacemaker. %files test %doc %{booth_docdir}/README-testing %{test_path} %dir /usr/lib/ocf %dir /usr/lib/ocf/resource.d %dir /usr/lib/ocf/resource.d/booth /usr/lib/ocf/resource.d/booth/sharedrsc %changelog diff --git a/configure.ac b/configure.ac index c2ea2d7..eb1d1e1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,513 +1,512 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. # bootstrap / init AC_PREREQ([2.69]) AC_INIT([booth], [m4_esyscmd([build-aux/git-version-gen --fallback 1.1 .tarball-version .gitarchivever])], [users@clusterlabs.org]) AC_USE_SYSTEM_EXTENSIONS AM_INIT_AUTOMAKE([-Wno-portability subdir-objects]) AC_CONFIG_MACRO_DIR([build-aux]) AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_HEADERS([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 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 # 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 PKG_PROG_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"") # 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])]) else # Just set Automake variables (mainly PYTHON_VERSION) AM_PATH_PYTHON fi PYTHON_SHEBANG="$PYTHON ${PYTHON_OPTS--Es}" AC_SUBST([PYTHON_SHEBANG]) AM_CONDITIONAL(PYTHON_IS_VERSION3, test "x${PYTHON_VERSION%%.*}" = "x3") # Checks for header files. AC_FUNC_ALLOCA AC_HEADER_DIRENT 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_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_FUNC_VPRINTF AC_CHECK_FUNCS([alarm alphasort atexit bzero dup2 endgrent endpwent fcntl \ getcwd getpeerucred getpeereid gettimeofday memmove \ memset mkdir scandir select socket strcasecmp strchr strdup \ strerror strrchr strspn strstr \ sched_get_priority_max sched_setscheduler]) AC_CONFIG_FILES([Makefile booth.pc src/Makefile docs/Makefile conf/Makefile]) AC_CONFIG_FILES([conf/booth-arbitrator.service conf/booth@.service]) -AC_CONFIG_FILES([script/unit-test.py]) AC_CONFIG_FILES([script/service-runnable], [chmod +x script/service-runnable]) # =============================================== # Helpers # =============================================== ## PKG_CHECK_VAR wrapper that allows defining a default value ## when value from pkg-config is not detected AC_DEFUN([BOOTH_PKG_CHECK_VAR], [ varname=$1 default=$4 AC_MSG_CHECKING([for pkg-conf $2 var $3]) PKG_CHECK_VAR([$1], [$2], [$3]) AS_VAR_IF([$1], [""], [AS_VAR_IF([default], [""], AC_MSG_ERROR([not found]), [AS_VAR_COPY([$varname], [default]) && AC_MSG_RESULT([not found, using default ${!varname}])])], [AC_MSG_RESULT([yes (detected: ${!varname})])]) ]) ## 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 } ## 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" ]) AC_ARG_WITH([html_man], [ --with-html_man : Enable generating man pages in HTML.]) AM_CONDITIONAL(BUILD_ASCIIDOC_HTML_MAN, (test "x${ASCIIDOC}" != "x" || test x"${ASCIIDOCTOR}" != x"") && test "x$with_html_man" = "xyes") AC_ARG_WITH([glue], [ --without-glue : Avoid libraries from (cluster-)glue project.], [], [with_glue=yes]) AC_ARG_WITH([run_build_tests], [ --with-run-build-tests : Enable running build tests when generating RPM], [run_build_tests=yes], []) AM_CONDITIONAL([RUN_BUILD_TESTS], [test "x$run_build_tests" = "xyes"]) # figure out ocfdir automatically and allow manual override (mostly for CI) BOOTH_PKG_CHECK_VAR([OCFROOT], [resource-agents], [ocfrootdir], [/usr/lib/ocf]) AC_ARG_WITH([ocfdir], [ --with-ocfdir=DIR : path to ocfdir (default: autodetected). ], [ ocfdir="$withval" ], [ ocfdir="$OCFROOT" ]) AC_SUBST([ocfdir]) # 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 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]])]) logging_provider=libqb fi case "$logging_provider" in libplumb) LOGGER="ha_logger" ;; libqb) 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 BOOTH_PKG_CHECK_VAR([CRM_DAEMON_USER], [pacemaker], [daemon_user], [hacluster]) AC_DEFINE_UNQUOTED(CRM_DAEMON_USER,"$CRM_DAEMON_USER", User to run Booth daemon as) BOOTH_PKG_CHECK_VAR([CRM_DAEMON_GROUP], [pacemaker], [daemon_group], [haclient]) AC_DEFINE_UNQUOTED(CRM_DAEMON_GROUP,"$CRM_DAEMON_GROUP", Group to run Booth daemon as) # *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_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ struct timespec tv; clock_gettime(CLOCK_REALTIME, &tv); return 0;]])], [ac_cv_HAVE_CLOCK_GETTIME=yes], [ac_cv_HAVE_CLOCK_GETTIME=no])]) 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([LINT_FLAGS]) 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_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_OUTPUT AC_MSG_RESULT([]) AC_MSG_RESULT([$PACKAGE configuration:]) AC_MSG_RESULT([ Version = ${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([ ocf dir = ${ocfdir}]) 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([ 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([ 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/script/unit-test.py.in b/script/unit-test.py.in deleted file mode 100644 index ea071c7..0000000 --- a/script/unit-test.py.in +++ /dev/null @@ -1,650 +0,0 @@ -#!@PYTHON_SHEBANG@ -# vim: fileencoding=utf-8 -# see http://stackoverflow.com/questions/728891/correct-way-to-define-python-source-code-encoding -# NOTE: setting the encoding is needed as non-ASCII characters are contained - -import os, sys, time, signal, tempfile, socket, posix, time -import re, shutil, pexpect, logging, pprint -import random, copy, glob, traceback - - -# Don't make that much sense - function/line is write(). -# Would have to use traceback.extract_stack() manually. -# %(funcName)10.10s:%(lineno)3d %(levelname)8s -# The second ":" is to get correct syntax highlightning, -# eg. messages with ERROR etc. are red in vim. -default_log_format = '%(asctime)s: : %(message)s' -default_log_datefmt = '%b %d %H:%M:%S' - - -# Compatibility with dictionary methods not present in Python 3; -# https://www.python.org/dev/peps/pep-0469/#migrating-to-the-common-subset-of-python-2-and-3 -try: - dict.iteritems -except AttributeError: # Python 3 - iter_items = lambda d: iter(d.items()) -else: # Python 2 - iter_items = lambda d: d.iteritems() - - -# {{{ pexpect-logging glue -# needed for use as pexpect.logfile, to relay into existing logfiles -class expect_logging(): - prefix = "" - test = None - - def __init__(self, pre, inst): - self.prefix = pre - self.test = inst - - def flush(self, *arg): - pass - - def write(self, stg): - if self.test.dont_log_expect == 0: - # TODO: split by input/output, give program - if sys.version_info[0] >= 3: - stg = str(stg, 'UTF-8') - for line in re.split(r"[\r\n]+", stg): - if line == self.test.prompt: - continue - if line == "": - continue - logging.debug(" " + self.prefix + " " + line) -# }}} - - -# {{{ dictionary plus second hash -class dict_plus(dict): - def __init__(self): - self.aux = dict() - -# def aux(self): -# return self.aux -# }}} - - -class UT(): -# {{{ Members - binary = None - test_base = None - lockfile = None - - defaults = None - - this_port = None - this_site = "127.0.0.1" - this_site_id = None - running = False - - gdb = None - booth = None - prompt = "CUSTOM-GDB-PROMPT-%d-%d" % (os.getpid(), time.time()) - - dont_log_expect = 0 - current_nr = None - - udp_sock = None - - # http://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output - BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) -# }}} - - -# {{{ setup functions - @classmethod - def _filename(cls, desc): - return "/tmp/booth-unittest.%d.%s" % (os.getpid(), desc) - - - def __init__(self, bin, dir): - self.binary = os.path.realpath(bin) - self.test_base = os.path.realpath(dir) + "/" - self.defaults = self.read_test_input(self.test_base + "_defaults.txt", state="ticket") - self.lockfile = UT._filename("lock") - self.udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - - def read_test_input(self, file, state=None, m = dict()): - fo = open(file, "r") - state = None - line_nr = 0 - for line in fo.readlines(): - line_nr += 1 - - # comment? - if re.match(r"^\s*#", line): - continue - # empty line - if re.match(r"^\s*$", line): - continue - - # message resp. ticket - # We allow a comment to have something to write out to screen - res = re.match(r"^\s*(\w+)\s*:(?:\s*(#.*?\S))?\s*$", line) - if res: - state = res.group(1) - if state not in m: - m[state] = dict_plus() - if res.group(2): - m[state].aux["comment"] = res.group(2) - m[state].aux["line"] = line_nr - continue - - assert(state) - - res = re.match(r"^\s*(\S+)\s*(.*)\s*$", line) - if res: - m[state][ res.group(1) ] = res.group(2) - return m - - - def setup_log(self, **args): - global default_log_format - global default_log_datefmt - - this_test_log = logging.FileHandler( mode = "w", **args ) - this_test_log.setFormatter( - logging.Formatter(fmt = default_log_format, - datefmt = default_log_datefmt) ) - - this_test_log.emit( - logging.makeLogRecord( { - "msg": "## vim: set ft=messages : ##", - "lineno": 0, - "levelname": "None", - "level": None,} ) ) - - # in the specific files we want ALL information - this_test_log.setLevel(logging.DEBUG) - - logging.getLogger('').addHandler(this_test_log) - return this_test_log - - - def running_on_console(self): - return sys.stdout.isatty() - - - def colored_string(self, stg, color): - if self.running_on_console(): - return "\033[%dm%s\033[0m" % (30+color, stg) - return stg - - - # We want shorthand in descriptions, ie. "state" - # instead of "booth_conf->ticket[0].state". - def translate_shorthand(self, name, context): - if context == 'ticket': - return "booth_conf->ticket[0]." + name - if context == 'message': - return "msg->" + name - if context == 'inject': - return "ntohl(((struct boothc_ticket_msg *)buf)->" + name + ")" - assert(False) - - - def stop_processes(self): - if os.access(self.lockfile, os.F_OK): - os.unlink(self.lockfile) - # In case the boothd process is already dead, isalive() would still return True - # (because GDB still has it), but terminate() does fail. - # So we just quit GDB, and that might take the boothd with it - - # if not, we terminate it ourselves. - if self.gdb: - self.gdb.close( force=True ); - self.drain_booth_log() - if self.booth: - self.booth.close( force=self.booth.isalive() ) - - - def start_a_process(self, bin, env_add=(), **args): - name = re.sub(r".*/", "", bin) - # How to get stderr, too? - expct = pexpect.spawn(bin, - env=dict(os.environ, **dict({ - 'PATH': ':'.join((self.test_base + "/bin/", - os.getenv('PATH'))), - 'UNIT_TEST_PATH': self.test_base, - 'LC_ALL': 'C', - 'LANG': 'C'}, **dict(env_add))), - timeout=30, - maxread=32768, - **args) - expct.setecho(False) - expct.logfile_read = expect_logging("<- %s" % name, self) - expct.logfile_send = expect_logging(" -> %s" % name, self) - return expct - - - def start_processes(self, test): - self.booth = self.start_a_process(self.binary, - args = ["daemon", "-DS", - "-c", self.test_base + "/booth.conf", - "-s", "127.0.0.1", - "-l", self.lockfile], - env_add=( ('UNIT_TEST', test), - ('UNIT_TEST_FILE', os.path.realpath(test)), - # provide some space, so that strcpy(getenv()) works - ('UNIT_TEST_AUX', "".zfill(1024)), - )); - - logging.info("started booth with PID %d, lockfile %s" % (self.booth.pid, self.lockfile)) - self.booth.expect("BOOTH site \S+ \(build \S+\) daemon is starting", timeout=2) - #print self.booth.before; exit - - self.gdb = self.start_a_process("gdb", - args=["-quiet", - "-p", str(self.booth.pid), - # Don't use .gdbinit - "-nx", "-nh", - # Run until the defined point. - # This is necessary so that ticket state setting doesn't - # happen _before_ the call to pcmk_load_ticket() - # (which would overwrite our data) - "-ex", "break ticket_cron", - "-ex", "continue"]) - logging.info("started GDB with PID %d" % self.gdb.pid) - self.gdb.expect("(gdb)") - self.gdb.sendline("set pagination off\n") - self.gdb.sendline("set interactive-mode off\n") - self.gdb.sendline("set verbose off\n") ## sadly to late for the initial "symbol not found" messages - self.gdb.sendline("set prompt " + self.prompt + "\\n\n"); - self.sync(2000) - - # Only stop for this recipient, so that broadcasts are not seen multiple times - self.send_cmd("break booth_udp_send if to == &(booth_conf->site[1])") - self.send_cmd("break recvfrom") - # ticket_cron is still a breakpoint - - # Now we're set up. - self.this_site_id = self.query_value("local->site_id") - self.this_port = int(self.query_value("booth_conf->port")) - - # do a self-test - assert(self.check_value("local->site_id", self.this_site_id)) - - self.running = False -# }}} - - -# {{{ GDB communication - def sync(self, timeout=-1): - self.gdb.expect(self.prompt, timeout) - - answer = self.gdb.before - - self.dont_log_expect += 1 - # be careful not to use RE characters like +*.[] etc. - r = str(random.randint(2**19, 2**20)) - self.gdb.sendline("print " + r) - self.gdb.expect(r, timeout) - self.gdb.expect(self.prompt, timeout) - self.dont_log_expect -= 1 - return answer # send a command to GDB, returning the GDB answer as string. - - def drain_booth_log(self): - try: - self.booth.read_nonblocking(64*1024, 0) - except pexpect.EOF: - pass - except pexpect.TIMEOUT: - pass - finally: - pass - - def send_cmd(self, stg, timeout=-1): - # give booth a chance to get its messages out - self.drain_booth_log() - - self.gdb.sendline(stg) - return self.sync(timeout=timeout) - - def _query_value(self, which): - val = self.send_cmd("print " + which) - cleaned = re.search(r"^\$\d+ = (.*\S)\s*$", val, re.MULTILINE) - if not cleaned: - self.user_debug("query failed") - return cleaned.group(1) - - def query_value(self, which): - res = self._query_value(which) - logging.debug("query_value: «%s» evaluates to «%s»" % (which, res)) - return res - - def check_value(self, which, value): - val = self._query_value("(" + which + ") == (" + value + ")") - logging.debug("check_value: «%s» is «%s»: %s" % (which, value, val)) - if val == "1": - return True - # for easier (test) debugging we'll show the _real_ value, too. - want = self._query_value(value) - # Order is important, so that next query works!! - has = self._query_value(which) - # for informational purposes - self._query_value('state_to_string($$)') - logging.error("«%s»: got «%s», expected «%s». ERROR." % (which, has, want)) - return False - - # Send data to GDB, to inject them into the binary. - # Handles different data types - def set_val(self, name, value, numeric_conv=None): - logging.debug("setting value «%s» to «%s» (num_conv %s)" %(name, value, numeric_conv)) - res = None - # string value? - if re.match(r'^"', value): - res = self.send_cmd("print strcpy(" + name + ", " + value + ")") - elif re.match(r"^'", value): - # single-quoted; GDB only understands double quotes. - v1 = re.sub(r"^'", '', value) - v2 = re.sub(r"'$", '', v1) - # TODO: replace \\\\" etc. - v3 = re.sub(r'"', '\\"', v2) - res = self.send_cmd("print strcpy(" + name + ', "' + v3 + '")') - # numeric - elif numeric_conv: - res = self.send_cmd("set variable " + name + " = " + numeric_conv + "(" + value + ")") - else: - res = self.send_cmd("set variable " + name + " = " + value) - for r in [r"There is no member named", - r"Structure has no component named", - r"No symbol .* in current context", ]: - assert(not re.search(r, res, re.MULTILINE)) - logging.debug("set_val %s done" % name) -# }}} GDB communication - - - # there has to be some event waiting, so that boothd stops again. - def continue_debuggee(self, timeout=30): - res = None - if not self.running: - res = self.send_cmd("continue", timeout) - self.drain_booth_log() - return res - - -# {{{ High-level functions. -# Generally, GDB is attached to BOOTHD, and has it stopped. - def set_state(self, kv): - if not kv: - return - - self.current_nr = kv.aux.get("line") - #os.system("strace -f -tt -s 2000 -e write -p" + str(self.gdb.pid) + " &") - for n, v in iter_items(kv): - self.set_val( self.translate_shorthand(n, "ticket"), v) - logging.info("set state") - - - def user_debug(self, txt): - logging.error("Problem detected: %s", txt) - logging.info(self.gdb.buffer) - if not sys.stdin.isatty(): - logging.error("Not a terminal, stopping.") - else: - print("\n\nEntering interactive mode.\n\n") - self.gdb.sendline("set prompt GDB> \n") - self.gdb.setecho(True) - # can't use send_cmd, doesn't reply with expected prompt anymore. - self.gdb.interact() - #while True: - # sys.stdout.write("GDB> ") - # sys.stdout.flush() - # x = sys.stdin.readline() - # if not x: - # break - # self.send_cmd(x) - self.stop_processes() - sys.exit(1) - - - def wait_for_function(self, fn, timeout=20): - until = time.time() + timeout - while True: - stopped_at = self.continue_debuggee(timeout=3) - if not stopped_at: - self.user_debug("Not stopped at any breakpoint?") - if re.search(r"^Program received signal SIGABRT,", stopped_at, re.MULTILINE): - self.user_debug("assert() failed") - if re.search(r"^Program received signal SIGSEGV,", stopped_at, re.MULTILINE): - self.user_debug("Segfault") - if re.search(r"^Breakpoint \d+, (0x\w+ in )?%s " % fn, stopped_at, re.MULTILINE): - break - if time.time() > until: - self.user_debug("Didn't stop in function %s" % fn) - logging.info("Now in %s" % fn) - - # We break, change the data, and return the correct size. - def send_message(self, msg): - self.udp_sock.sendto('a', (socket.gethostbyname(self.this_site), self.this_port)) - - self.wait_for_function("recvfrom") - # drain input, but stop afterwards for changing data - self.send_cmd("finish") - # step over length assignment - self.send_cmd("next") - - # push message. - for (n, v) in iter_items(msg): - self.set_val( self.translate_shorthand(n, "message"), v, "htonl") - - # set "received" length - self.set_val("rv", "msg->header.length", "ntohl") - - # the next thing should run continue via wait_for_function - - def wait_outgoing(self, msg): - self.wait_for_function("booth_udp_send") - ok = True - for (n, v) in iter_items(msg): - if re.search(r"\.", n): - ok = self.check_value( self.translate_shorthand(n, "inject"), v) and ok - else: - ok = self.check_value( self.translate_shorthand(n, "ticket"), v) and ok - - if not ok: - sys.exit(1) - logging.info("out gone") - #stopped_at = self.sync() - - def merge_dicts(self, base, overlay): - return dict(base, **overlay) - - - def loop(self, fn, data): - matches = (re.match(r"^(outgoing|message)(\d+)$", k) for k in data) - loop_max = max(int(m.group(2)) for m in matches if m) - for counter in range(0, loop_max+1): # incl. last message - - kmsg = 'message%d' % counter - msg = data.get(kmsg) - - ktkt = 'ticket%d' % counter - tkt = data.get(ktkt) - - kout = 'outgoing%d' % counter - out = data.get(kout) - - kgdb = 'gdb%d' % counter - gdb = data.get(kgdb) - - - if not any([msg, out, tkt]): - continue - - logging.info("Part %d" % counter) - if tkt: - self.current_nr = tkt.aux.get("line") - comment = tkt.aux.get("comment", "") - logging.info("ticket change %s (%s:%d) %s" % (ktkt, fn, self.current_nr, comment)) - self.set_state(tkt) - if gdb: - for (k, v) in iter_items(gdb): - self.send_cmd(k + " " + v.replace("§", "\n")) - if msg: - self.current_nr = msg.aux.get("line") - comment = msg.aux.get("comment", "") - logging.info("sending %s (%s:%d) %s" % (kmsg, fn, self.current_nr, comment)) - self.send_message(self.merge_dicts(data["message"], msg)) - if kgdb in data and len(gdb) == 0: - self.user_debug("manual override") - if out: - self.current_nr = out.aux.get("line") - comment = out.aux.get("comment", "") - logging.info("waiting for %s (%s:%d) %s" % (kout, fn, self.current_nr, comment)) - self.wait_outgoing(out) - logging.info("loop ends") - - - def let_booth_go_a_bit(self): - self.drain_booth_log() - logging.debug("running: %d" % self.running) - - if not self.running: - self.gdb.sendline("continue") - time.sleep(1) - self.drain_booth_log() - # stop it - via GDB! - self.gdb.sendintr() - # If we sent the signal to booth, the next - # "print state_to_string()" or "continue" - # might catch the signal - and fail to do - # what we want/need. - # - # This additional signal seems to be unnecessary. - #posix.kill(self.gdb.pid, signal.SIGINT) - # In case it's really needed we should drain booth's signals queue, - # eg. by sending "print getpid()" twice, before the sync() call. - self.running = False - self.sync(2000) - - - def do_finally(self, data): - if not data: - return - - self.current_nr = data.aux.get("line") - # Allow debuggee to reach a stable state - self.let_booth_go_a_bit() - - ok = True - for (n, v) in iter_items(data): - ok = self.check_value( self.translate_shorthand(n, "ticket"), v) and ok - if not ok: - sys.exit(1) - - - def run(self, start_from="000", end_with="999"): - os.chdir(self.test_base) - # TODO: sorted, random order - tests = sorted(f for f in glob.glob("*") - if re.match(r"^\d\d\d_.*\.txt$", f)) - failed = 0 - for f in tests: - if f[0:3] < start_from: - continue - if f[0:3] > end_with: - continue - log = None - logfn = UT._filename(f) - if self.running_on_console(): - sys.stdout.write("\n") - self.current_nr = "setup" - try: - log = self.setup_log(filename = logfn) - - log.setLevel(logging.DEBUG) - logging.error(self.colored_string("Starting test '%s'" % f, self.BLUE) + ", logfile " + logfn) - self.start_processes(f) - - test = self.read_test_input(f, m=copy.deepcopy(self.defaults)) - logging.debug("data: %s" % pprint.pformat(test, width = 200)) - - self.set_state(test.get("ticket")) - self.loop(f, test) - self.do_finally(test.get("finally")) - - self.current_nr = "teardown" - logging.warn(self.colored_string("Finished test '%s' - OK" % f, self.GREEN)) - except: - failed += 1 - logging.error(self.colored_string("Broke in %s:%s %s" % (f, self.current_nr, sys.exc_info()), self.RED)) - for frame in traceback.format_tb(sys.exc_info()[2]): - logging.info(" - %s " % frame.rstrip()) - finally: - self.stop_processes() - if log: - log.close() - logging.getLogger("").removeHandler(log) - if self.running_on_console(): - sys.stdout.write("\n") - return failed -# }}} - - -#def traceit(frame, event, arg): -# if event == "line": -# lineno = frame.f_lineno -# print frame.f_code.co_filename, ":", "line", lineno -# return traceit - - -# {{{ main -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_UNITTEST_ROOT_USER"))): - sys.stderr.write("Must be run non-root; aborting.\n") - sys.exit(1) - - - ut = UT(sys.argv[1], sys.argv[2] + "/") - - # "master" log object needs max level - logging.basicConfig(level = logging.DEBUG, - filename = "/dev/null", - filemode = "a", - format = default_log_format, - datefmt = default_log_datefmt) - - - # make sure no old processes are active anymore - os.system("killall boothd > /dev/null 2> /dev/null") - - overview_log = ut.setup_log( filename = UT._filename('seq') ) - overview_log.setLevel(logging.WARN) - - # http://stackoverflow.com/questions/9321741/printing-to-screen-and-writing-to-a-file-at-the-same-time - console = logging.StreamHandler() - console.setFormatter(logging.Formatter(' # %(message)s')) - console.setLevel(logging.WARN) - logging.getLogger('').addHandler(console) - - - logging.info("Starting boothd unit tests.") - - #sys.settrace(traceit) - - starting = "0" - if len(sys.argv) > 3: - starting = sys.argv[3] - ending = "999" - if len(sys.argv) > 4: - ending = sys.argv[4] - ret = ut.run(starting, ending) - sys.exit(ret) -# }}} diff --git a/unit-tests/001_init-get-heartbeat.txt b/unit-tests/001_init-get-heartbeat.txt deleted file mode 100644 index fa79a06..0000000 --- a/unit-tests/001_init-get-heartbeat.txt +++ /dev/null @@ -1,25 +0,0 @@ -# vim: ft=sh et : - - -ticket: - state ST_FOLLOWER - current_term 1 - leader 0 - -# should be accepted -message0: # valid heartbeat - header.cmd OP_HEARTBEAT - header.result RLT_SUCCESS - header.from booth_conf->site[2].site_id - ticket.leader booth_conf->site[2].site_id - ticket.term_valid_for 3 - ticket.term 20 - -# nothing goes out - - -# after a delay, check final state -finally: -# should be overwritten - current_term 20 - leader booth_conf->site+2 diff --git a/unit-tests/002_bad_packets.txt b/unit-tests/002_bad_packets.txt deleted file mode 100644 index 8a70805..0000000 --- a/unit-tests/002_bad_packets.txt +++ /dev/null @@ -1,70 +0,0 @@ -# vim: ft=sh et : -# -# This test is mostly concerned with ignoring invalid packets. -# We're expecting heartbeat packets. - - -ticket: - state ST_LEADER - leader local - current_term 500 - - -# defaults -message: - header.cmd OP_HEARTBEAT - ticket.term 500 - #header.from booth_conf->site[2].site_id - header.from local->site_id - header.result 0 - - -message0: # bad result code - header.result 243521741 - -outgoing0: - state ST_LEADER - - -message1: # bad sender - header.from 71 - -outgoing1: - state ST_LEADER - - - -message2: # bad version - header.version 512 - -outgoing2: - state ST_LEADER - - -message3: # bad magic - header.version 31 - -outgoing3: - state ST_LEADER - -message4: # bad length - header.length 16 - -outgoing4: - state ST_LEADER - - -message5: # bad ticket ID - ticket.id "gibtsnich" - -outgoing5: - state ST_LEADER - - -message100: # should work - ticket.term 510 - -# no outgoing message -finally: - state ST_FOLLOWER - current_term 510 diff --git a/unit-tests/003_pacemaker.txt b/unit-tests/003_pacemaker.txt deleted file mode 100644 index 2b039a7..0000000 --- a/unit-tests/003_pacemaker.txt +++ /dev/null @@ -1,22 +0,0 @@ -# vim: ft=sh et : -# -# Checks whether Pacemaker gets correct command lines. - - -ticket: - state ST_FOLLOWER - current_term 40 - term_expires time(0) + 30 - - -message0: - header.cmd OP_HEARTBEAT - header.from booth_conf->site[2].site_id - header.result 0 - ticket.term 44 - ticket.leader booth_conf->site[2].site_id - #getenv("UNIT_TEST_AUX") "ballot 99 owner +" - - -finally: - current_term 44 diff --git a/unit-tests/010_retries.txt b/unit-tests/010_retries.txt deleted file mode 100644 index 9eaeabf..0000000 --- a/unit-tests/010_retries.txt +++ /dev/null @@ -1,62 +0,0 @@ -# vim: ft=sh et : -# -# Testing whether retries are sent, and if they're stopped again. - - -ticket: - state ST_LEADER - current_term 40 - leader local - retries 10000 # needed so that heartbeats are sent _now_ - timeout 1 - # may keep ticket all the time - term_duration 3000 - # but shall start renewal now - term_expires time(0) + 1000 - - - -outgoing0: - header.cmd OP_HEARTBEAT - ticket.term 40 -outgoing1: - header.cmd OP_HEARTBEAT - ticket.term 40 -outgoing2: - header.cmd OP_HEARTBEAT - ticket.term 40 -outgoing3: - header.cmd OP_HEARTBEAT - ticket.term 40 - -# yes, you're the leader. -message4: - header.cmd OP_HEARTBEAT - header.from booth_conf->site[2].site_id - header.result 0 - ticket.term 40 - ticket.leader local->site_id - -# doesn't stop ... there is no retry limit - -outgoing5: - header.cmd OP_HEARTBEAT -outgoing6: - header.cmd OP_HEARTBEAT -outgoing7: - header.cmd OP_HEARTBEAT -outgoing8: - header.cmd OP_HEARTBEAT -outgoing9: - header.cmd OP_HEARTBEAT -outgoing10: - header.cmd OP_HEARTBEAT - - -# Now term expires -ticket11: - term_expires time(0) - 1 - -# no outgoing message, gets to be follower -finally: - state ST_FOLLOWER diff --git a/unit-tests/020_ext-verifier.txt b/unit-tests/020_ext-verifier.txt deleted file mode 100644 index b1a5e9d..0000000 --- a/unit-tests/020_ext-verifier.txt +++ /dev/null @@ -1,53 +0,0 @@ -# vim: ft=sh et : -# -# Testing whether the external verifier (before-acquire-handler) -# is obeyed. - - -ticket: - name "tick1" - state ST_LEADER - current_term 40 - leader local - # may keep ticket all the time - term_duration 3000 - # but shall start renewal now - term_expires time(0) + 1000 - req_sent_at time(0) - 10 - - -gdb0: - call parse_extprog("test `set|grep ^BOOTH|wc -l` -ge 5", booth_conf->ticket+0) - -outgoing0: - header.cmd OP_HEARTBEAT - - -ticket1: - ext_verifier 'test "$BOOTH_TICKET" == "tick1"' - # cause re-query of the verifier - req_sent_at time(0) - 10 - -# -#gdb1: -# break ticket_broadcast_proposed_state § commands § bt § c § end - - -outgoing1: - header.cmd OP_HEARTBEAT - - -# now say that we may not have it anymore. -ticket2: - ext_verifier 'test "$BOOTH_TICKET" == "tick2FOO"' - # cause re-query of the verifier - req_sent_at time(0) - 10 - -# We just tell the others we don't have it anymore. -outgoing2: - header.cmd OP_REQ_VOTE - ticket.leader -1 - -finally: - state ST_FOLLOWER - leader NULL diff --git a/unit-tests/060_catchup_same_owner.txt b/unit-tests/060_catchup_same_owner.txt deleted file mode 100644 index 73899cd..0000000 --- a/unit-tests/060_catchup_same_owner.txt +++ /dev/null @@ -1,38 +0,0 @@ -# vim: ft=sh et : -# We've got the ticket; a peer agrees with us re. owner, but has a -# higher term number. -# We must not lose the ticket. - -ticket: - state ST_LEADER - current_term 100 - leader local - term_expires time(0) + 35 - term_duration 3000 - retries 6 - timeout 1 - hb_sent_at time(0) - 2000 - - -gdb0: - watch booth_conf->ticket[0].leader § commands § bt § c § end - -# No message0 - -outgoing0: - header.cmd OP_HEARTBEAT - header.result RLT_SUCCESS - ticket.term 100 - ticket.leader local->site_id - - -message1: # same owner - header.cmd OP_HEARTBEAT - header.result RLT_SUCCESS - header.from booth_conf->site[2].site_id - ticket.term 110 - ticket.leader local->site_id - -finally: - leader local - diff --git a/unit-tests/100_abort-after-retries.txt b/unit-tests/100_abort-after-retries.txt deleted file mode 100644 index 881d785..0000000 --- a/unit-tests/100_abort-after-retries.txt +++ /dev/null @@ -1,40 +0,0 @@ -# vim: ft=sh et : -# -# Testing whether retries are aborted at some time. - - -ticket: - state ST_STABLE - last_ack_ballot 40 - new_ballot 50 - retries 6 - timeout 1 - owner local - expiry 3000 - # but renewing is necessary - expires time(0) + 100 - next_cron time(0) + 1 - - -outgoing0: - header.cmd OP_PREPARING -outgoing1: - header.cmd OP_PREPARING -outgoing2: - header.cmd OP_PREPARING -outgoing3: - header.cmd OP_PREPARING - -# Now give cause to abort. -ticket4: - expires time(0) - 2 - retry_number 10 - timeout 2 -outgoing4: - header.cmd CMD_CATCHUP - - -# ticket must be lost -finally: - owner 0 - state ST_INIT diff --git a/unit-tests/_defaults.txt b/unit-tests/_defaults.txt deleted file mode 100644 index 46feb96..0000000 --- a/unit-tests/_defaults.txt +++ /dev/null @@ -1,55 +0,0 @@ -# vim: ft=sh et : - - -# ticket defaults, mostly set via config file. -ticket: - - name "ticket" - - ## these would matter if testing via GDB had high latencies - #expiry 60 - #timeout 10 - acquire_after 0 - - - - # defaults for all tests - state ST_INIT - next_cron.tv_sec 0 - -# time(0)+1 - # local is site[0] per convention - - leader booth_conf->site+1 - #owner booth_conf->site+1 - #expires time(0)+1 - term_expires.tv_sec time(0)+1 - #last_ack_ballot 242 - - leader 0 - #proposer 0 - #proposed_owner 0 - #new_ballot 0 - #proposal_acknowledges 0 - #retry_number 0 - - - -# defaults for input message. -# sender is a peer, and it wants something. -message: - - ticket.id "ticket" - # invalid by default - header.cmd -1 - # invalid by default - header.result 1 - # invalid by default - header.from -1 - header.version BOOTHC_VERSION - header.magic BOOTHC_MAGIC - header.length sizeof(struct boothc_ticket_msg) - ticket.leader -1 - ticket.term 0 - ticket.term_valid_for 0 - diff --git a/unit-tests/bin/crm_ticket b/unit-tests/bin/crm_ticket deleted file mode 100755 index cf69761..0000000 --- a/unit-tests/bin/crm_ticket +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -# -# "fake" crm_ticket for unit tests. -# * Ignores set,grant,revoke actions -# * returns values it gets asked for from UNIT_TEST_AUX - - - -function word_after() -{ -# Per default $1 is printed - perl -e ' - $search=shift(); - $stg=shift(); - print $'${3:-1}' if $stg =~ /$search/;' "$1" "$2" -} - - -function Get() -{ - which=$(word_after " -G ('?)(\\w+)\\1" "$1" 2) - if [[ -z "$which" ]] ; then - exit 1 - fi - - word_after "\\b$which\\b (\\S+)" "$UNIT_TEST_AUX" - # provide a newline - echo "" - exit 0 -} - - - -# include resp. supersede with per-test functions -inc="${UNIT_TEST_FILE%.txt}.sh" -if [[ -e "$inc" ]] ; then - . "$inc" -fi - - -if [[ "$*" == *" -G "* ]] ; then - Get "$*" -elif [[ "$*" == *" -g "* ]] ; then - : # grant -elif [[ "$*" == *" -r "* ]] ; then - : # revoke -elif [[ "$*" == *" -S "* ]] ; then - : # set -else - echo "not understood" >&2 - exit 1 -fi diff --git a/unit-tests/booth.conf b/unit-tests/booth.conf deleted file mode 100644 index 5a69acb..0000000 --- a/unit-tests/booth.conf +++ /dev/null @@ -1,18 +0,0 @@ -# "local" -site=127.0.0.1 - -# these should not exist, although it shouldn't matter much -# because no packets should be sent anyway -arbitrator="127.0.0.243" -site="127.0.0.244" - -# The ticket name, which corresponds to a set of resources which can be -# fail-overed among different sites. -ticket="ticket" - expire = 60 - timeout = 1 - acquire-after = 30 - weights = 1,2,3 - - # make it non-critical, put provide enough space to override if necessary. - before-acquire-handler = "//////////////////////////////////bin/true" diff --git a/unit-tests/init-catchup.txt b/unit-tests/init-catchup.txt deleted file mode 100644 index c681e89..0000000 --- a/unit-tests/init-catchup.txt +++ /dev/null @@ -1,48 +0,0 @@ -# vim: ft=sh et : - - -ticket: - state ST_INIT - last_ack_ballot 1 - new_ballot 2012 - -# No message0 -# expecting catchup query - -# outgoing packet: expect this data -outgoing0: - header.cmd CMD_CATCHUP - header.result RLT_SUCCESS - - -# ignore "bad" catchup data -message1: - header.from booth_conf->site[2].site_id - header.cmd CMR_CATCHUP - header.result 243521741 - -outgoing1: - header.cmd CMD_CATCHUP - header.result RLT_SUCCESS - - -# accept good CMR_CATCHUP -message2: - header.cmd CMR_CATCHUP - header.result RLT_SUCCESS - header.from booth_conf->site[2].site_id - ticket.ballot 2062 - ticket.prev_ballot 2052 - ticket.owner -1 - -# nothing goes out - - -# after a delay, check final state -finally: -# should be overwritten - last_ack_ballot 2052 -# should not be - a OP_PREPARING would fetch the new value - new_ballot 2012 -# too low-level -# proposal_acknowledges 5