Hello community, here is the log from the commit of package booth for openSUSE:Factory checked in at 2017-12-01 15:54:31 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/booth (Old) and /work/SRC/openSUSE:Factory/.booth.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "booth" Fri Dec 1 15:54:31 2017 rev:43 rq:547071 version:1.0 Changes: -------- --- /work/SRC/openSUSE:Factory/booth/booth.changes 2017-06-23 09:18:58.593248682 +0200 +++ /work/SRC/openSUSE:Factory/.booth.new/booth.changes 2017-12-01 15:55:01.741034009 +0100 @@ -1,0 +2,12 @@ +Fri Dec 1 10:50:28 UTC 2017 - [email protected] + +- Tickets: added manual tickets, which allow handling 2-site setup + (fate#322100) +- Debug mode: fixed the interaction with resource agents (bsc#1046790) +- Patch file bug-1045067_booth-fix-booth-grant-cmd.patch has been removed + after the code being merged to upstream: + * Clinet commands: fixed local IP addresses for booth grant, list, + and peers commands (bsc#1045067) +- Upstream version cs: d4cb8cbdaf87e46f636c3d06730b902b79bdcb9c + +------------------------------------------------------------------- Old: ---- booth-1.0+20170619.766d618.tar.bz2 bug-1045067_booth-fix-booth-grant-cmd.patch New: ---- booth-1.0+20171123.d4cb8cb.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ booth.spec ++++++ --- /var/tmp/diff_new_pack.7fmU46/_old 2017-12-01 15:55:02.425009396 +0100 +++ /var/tmp/diff_new_pack.7fmU46/_new 2017-12-01 15:55:02.429009252 +0100 @@ -21,7 +21,7 @@ %bcond_with glue # local commit: -%global commit 1.0+20170619.766d618 +%global commit 1.0+20171123.d4cb8cb %global uname hacluster %global gname haclient @@ -39,7 +39,6 @@ Url: https://github.com/ClusterLabs/booth Source: %{name}-%{commit}.tar.bz2 Source1: %{name}-rpmlintrc -Patch1: bug-1045067_booth-fix-booth-grant-cmd.patch BuildRequires: asciidoc BuildRequires: autoconf BuildRequires: automake @@ -78,7 +77,6 @@ %prep %setup -q -n %{name}-%{commit} -%patch1 -p1 %build autoreconf -fvi ++++++ booth-1.0+20170619.766d618.tar.bz2 -> booth-1.0+20171123.d4cb8cb.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/.git_info new/booth-1.0+20171123.d4cb8cb/.git_info --- old/booth-1.0+20170619.766d618/.git_info 2017-06-22 10:32:51.935559567 +0200 +++ new/booth-1.0+20171123.d4cb8cb/.git_info 2017-12-01 10:44:10.863914778 +0100 @@ -1 +1 @@ -v1.0-114-g766d618 +v1.0-131-gd4cb8cb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/README-testing new/booth-1.0+20171123.d4cb8cb/README-testing --- old/booth-1.0+20170619.766d618/README-testing 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/README-testing 2017-11-23 14:07:59.000000000 +0100 @@ -16,9 +16,9 @@ emulation functions. There are some restrictions on how booth.conf is formatted. -There could be several tickets defined, but only the first ticket -is used for testing. This ticket must have expire and timeout -parameters configured. +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: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/docs/boothd.8.txt new/booth-1.0+20171123.d4cb8cb/docs/boothd.8.txt --- old/booth-1.0+20170619.766d618/docs/boothd.8.txt 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/docs/boothd.8.txt 2017-11-23 14:07:59.000000000 +0100 @@ -74,6 +74,9 @@ 'immediate grant': Don't wait for unreachable sites to relinquish the ticket. See the 'Booth ticket management' section below for more details. + For manual tickets this option allows to grant a ticket + which is currently granted. See the 'Manual tickets' section + below for more details. + This option may be DANGEROUS. It makes booth grant the ticket even though it cannot ascertain that unreachable sites don't @@ -97,12 +100,11 @@ Report version information. *-S*:: - 'systemd' mode: don't fork. This is like '-D' but without the debug output. + 'systemd' mode: don't fork. + Disables daemonizing, the process will remain in the foreground. *-D*:: - Debug output/don't daemonize. - Increases the debug output level; booth daemon remains - in the foreground. + Increases the debug output level. *-l* 'lockfile':: Use another lock file. By default, the lock file name is @@ -367,6 +369,14 @@ Note that there can be no guarantee on whether an attribute value is up to date, i.e. if it actually reflects the current state. +*'mode'*:: + Specifies if the ticket is manual or automatic. ++ +By default all tickets are automatic (that is, they are fully +controlled by Raft algorithm). Assign the strings "manual" or +"MANUAL" to define the ticket as manually controlled. + + One example of a booth configuration file: ----------------------- @@ -517,6 +527,49 @@ 'systemctl' requires the configuration name, even for the default name 'booth'. +----------------------- + + +MANUAL TICKETS +----------------------- +Manual tickets allow users to create and manage tickets which are +subsequently handled by booth without using the Raft algorithm. +Granting and revoking manual tickets is fully controlled +by the administrator. It is possible to define a number of manual and +normal tickets in one GEO cluster. + +Automatic ticket management provided by Raft algorithm isn't applied +to manually controlled tickets. In particular, there is no elections, +automatic failover procedures, and term expiration. + +However, 'booth' controls if a ticket is currently being granted to +any site and warns the user approprietly. + +Tickets which were manually granted to a site, will remain there until +they are manually revoked. Even if a site becomes offline, the ticket +will not be moved to another site. This behavior allows administrators +to make sure that some services will remain in a particular site and +will not be moved to another site, possibly located in a different +geographical location. + +Also, configuring only manual tickets in a GEO cluster, allows to have +just two sites in a cluster, without a need of having an arbitrator. +This is possible because there is no automatic elections and no voting +performed for manual tickets. + +Manual tickets are defined in a configuration files by adding a 'mode' +ticket parameter and setting it to 'manual' or 'MANUAL': + +ticket="manual-ticket" + [...] + mode = manual + [...] + +Manual tickets can be granted and revoked by using normal 'grant' and +'revoke' commands, with the usual flags and parameters. The only +difference is that specyfiyng '-F' flag during 'grant' command, forced +a site to become a leader of the specified ticket, even if the ticket +is granted to another site. EXIT STATUS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/Makefile.am new/booth-1.0+20171123.d4cb8cb/src/Makefile.am --- old/booth-1.0+20170619.766d618/src/Makefile.am 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/Makefile.am 2017-11-23 14:07:59.000000000 +0100 @@ -8,10 +8,10 @@ sbin_PROGRAMS = boothd boothd_SOURCES = config.c main.c raft.c ticket.c transport.c \ - pacemaker.c handler.c request.c attr.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 + config.h log.h raft.h ticket.h transport.h handler.h request.h attr.h manual.h if BUILD_TIMER_C boothd_SOURCES += timer.c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/booth.h new/booth-1.0+20171123.d4cb8cb/src/booth.h --- old/booth-1.0+20170619.766d618/src/booth.h 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/booth.h 2017-11-23 14:07:59.000000000 +0100 @@ -295,7 +295,8 @@ int tcp_fd; int udp_fd; - /* 0-based, used for indexing into per-ticket weights */ + /* 0-based, used for indexing into per-ticket weights. + * -1 for no_leader. */ int index; uint64_t bitmask; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/config.c new/booth-1.0+20171123.d4cb8cb/src/config.c --- old/booth-1.0+20170619.766d618/src/config.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/config.c 2017-11-23 14:07:59.000000000 +0100 @@ -259,6 +259,7 @@ tk->term_duration = def->term_duration; tk->retries = def->retries; memcpy(tk->weight, def->weight, sizeof(tk->weight)); + tk->mode = def->mode; if (tkp) *tkp = tk; @@ -331,6 +332,15 @@ return i; } +/* returns TICKET_MODE_AUTO if failed to parse the ticket mode. */ +static ticket_mode_e retrieve_ticket_mode(const char *input) +{ + if (strcasecmp(input, "manual") == 0) { + return TICKET_MODE_MANUAL; + } + + return TICKET_MODE_AUTO; +} /* scan val for time; time is [0-9]+(ms)?, i.e. either in seconds * or milliseconds @@ -546,6 +556,7 @@ defaults.timeout = DEFAULT_TICKET_TIMEOUT; defaults.retries = DEFAULT_RETRIES; defaults.acquire_after = 0; + defaults.mode = TICKET_MODE_AUTO; error = ""; @@ -808,6 +819,11 @@ continue; } + if (strcmp(key, "mode") == 0) { + current_tk->mode = retrieve_ticket_mode(val); + continue; + } + if (strcmp(key, "weights") == 0) { if (parse_weights(val, current_tk->weight) < 0) goto err; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/config.h new/booth-1.0+20171123.d4cb8cb/src/config.h --- old/booth-1.0+20170619.766d618/src/config.h 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/config.h 2017-11-23 14:07:59.000000000 +0100 @@ -57,6 +57,11 @@ GRANT_MANUAL, } grant_type_e; +typedef enum { + TICKET_MODE_AUTO = 1, + TICKET_MODE_MANUAL, +} ticket_mode_e; + struct toktab { const char *str; int val; @@ -107,6 +112,15 @@ /** Node weights. */ int weight[MAX_NODES]; + + /* Mode operation of the ticket. + * Set to MANUAL to make sure that the ticket will be manipulated + * only by manual commands of the administrator. In such a case + * automatic elections will be disabled. + * Manual tickets do not have to be renewed every some time. + * The leader will continue to send heartbeat messages to other sites. + */ + ticket_mode_e mode; /** @} */ @@ -129,6 +143,27 @@ /** Is the ticket granted? */ int is_granted; + + /** Which site considered itself a leader. + * For manual tickets it is possible, that + * more than one site will act as a leader. + * This array is used for tracking that situation + * and notifying the user about the issue. + * + * Possible values for every site: + * 0: the site does not claim to be the leader + * 1: the site considers itself a leader and + * is sending or used to send heartbeat messages + * + * The site will be marked as '1' until this site + * receives revoke confirmation. + * + * If more than one site has '1', the geo cluster is + * considered to have multiple leadership and proper + * warning are generated. + */ + int sites_where_granted[MAX_NODES]; + /** Timestamp of leadership expiration */ timetype term_expires; /** End of election period */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/main.c new/booth-1.0+20171123.d4cb8cb/src/main.c --- old/booth-1.0+20170619.766d618/src/main.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/main.c 2017-11-23 14:07:59.000000000 +0100 @@ -93,6 +93,7 @@ static const struct booth_site _no_leader = { .addr_string = "none", .site_id = NO_ONE, + .index = -1, }; struct booth_site *const no_leader = (struct booth_site*) &_no_leader; @@ -942,6 +943,8 @@ " -s <site> Connect/grant to a different site\n" " -F Try to grant the ticket immediately\n" " even if not all sites are reachable\n" + " For manual tickets:\n" + " grant a manual ticket even if it has been already granted\n" " -w Wait forever for the outcome of the request\n" " -C Wait until the ticket is committed to the CIB (grant only)\n" " -h Print this help\n" @@ -1116,12 +1119,14 @@ strcat(cp, BOOTH_DEFAULT_CONF_EXT); } break; + case 'D': debug_level++; - enable_stderr = 1; - /* Fall through */ + break; + case 'S': daemonize = 0; + enable_stderr = 1; break; case 'l': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/manual.c new/booth-1.0+20171123.d4cb8cb/src/manual.c --- old/booth-1.0+20170619.766d618/src/manual.c 1970-01-01 01:00:00.000000000 +0100 +++ new/booth-1.0+20171123.d4cb8cb/src/manual.c 2017-11-23 14:07:59.000000000 +0100 @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 Chris Kowalczyk <[email protected]> + * + * 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 software 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. + */ + +#include "manual.h" + +#include "transport.h" +#include "ticket.h" +#include "config.h" +#include "log.h" +#include "request.h" + +/* For manual tickets, manual_selection function is an equivalent + * of new_election function used for assigning automatic tickets. + * The workflow here is much simplier, as no voting is performed, + * and the current node doesn't have to wait for any responses + * from other sites. + */ +int manual_selection(struct ticket_config *tk, + struct booth_site *preference, int update_term, cmd_reason_t reason) +{ + if (local->type != SITE) + return 0; + + tk_log_debug("starting manual selection (caused by %s %s)", + state_to_string(reason), + reason == OR_AGAIN ? state_to_string(tk->election_reason) : "" ); + + // Manual selection is done without any delay, the leader is assigned + set_leader(tk, local); + set_state(tk, ST_LEADER); + + // Manual tickets never expire, we don't specify expiration time + + // Make sure that election_end field is empty + time_reset(&tk->election_end); + + // Make sure that delay commit is empty, as manual tickets don't + // wait for any kind of confirmation from other nodes + time_reset(&tk->delay_commit); + + save_committed_tkt(tk); + + // Inform others about the new leader + ticket_broadcast(tk, OP_HEARTBEAT, OP_ACK, RLT_SUCCESS, 0); + tk->ticket_updated = 0; + + return 0; +} + +/* This function is called for manual tickets that were + * revoked from another site, which this site doesn't + * consider as a leader. + */ +int process_REVOKE_for_manual_ticket ( + struct ticket_config *tk, + struct booth_site *sender, + struct boothc_ticket_msg *msg) +{ + int rv; + + // For manual tickets, we may end up having two leaders. + // If one of them is revoked, it will send information + // to all members of the GEO cluster. + + // We may find ourselves here if this particular site + // has not been following the leader which had been revoked + // (and which had sent this message). + + // We send the ACK, to satisfy the requestor. + rv = send_msg(OP_ACK, tk, sender, msg); + + // Mark this ticket as not granted to the sender anymore. + mark_ticket_as_revoked(tk, sender); + + if (tk->state == ST_LEADER) { + tk_log_warn("%s wants to revoke ticket, " + "but this site is itself a leader", + site_string(sender)); + + // Because another leader is presumably stepping down, + // let's notify other sites that now we are the only leader. + ticket_broadcast(tk, OP_HEARTBEAT, OP_ACK, RLT_SUCCESS, 0); + } else { + tk_log_warn("%s wants to revoke ticket, " + "but this site is not following it", + site_string(sender)); + } + + return rv; +} + + + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/manual.h new/booth-1.0+20171123.d4cb8cb/src/manual.h --- old/booth-1.0+20170619.766d618/src/manual.h 1970-01-01 01:00:00.000000000 +0100 +++ new/booth-1.0+20171123.d4cb8cb/src/manual.h 2017-11-23 14:07:59.000000000 +0100 @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 Chris Kowalczyk <[email protected]> + * + * 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 software 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. + */ + +#ifndef _MANUAL_H +#define _MANUAL_H + +#include "booth.h" + +struct ticket_config; + +int manual_selection(struct ticket_config *tk, + struct booth_site *new_leader, int update_term, cmd_reason_t reason); + +int process_REVOKE_for_manual_ticket ( + struct ticket_config *tk, + struct booth_site *sender, + struct boothc_ticket_msg *msg); + + +#endif /* _MANUAL_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/pacemaker.c new/booth-1.0+20171123.d4cb8cb/src/pacemaker.c --- old/booth-1.0+20170619.766d618/src/pacemaker.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/pacemaker.c 2017-11-23 14:07:59.000000000 +0100 @@ -513,7 +513,7 @@ if (tk->is_granted) { log_warn("%s: granted here, assume it belonged to us", tk->name); - tk->leader = local; + set_leader(tk, local); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/raft.c new/booth-1.0+20171123.d4cb8cb/src/raft.c --- old/booth-1.0+20170619.766d618/src/raft.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/raft.c 2017-11-23 14:07:59.000000000 +0100 @@ -30,7 +30,7 @@ #include "ticket.h" #include "request.h" #include "log.h" - +#include "manual.h" inline static void clear_election(struct ticket_config *tk) @@ -370,6 +370,8 @@ tk_log_warn("different leader %s wants to update " "our ticket, sending reject", site_string(leader)); + + mark_ticket_as_granted(tk, sender); return send_reject(sender, tk, RLT_TERM_OUTDATED, msg); } @@ -399,10 +401,18 @@ /* assume that our ack got lost */ rv = send_msg(OP_ACK, tk, sender, msg); } else if (tk->leader != sender) { - tk_log_error("%s wants to revoke ticket, " - "but it is not granted there (ignoring)", - site_string(sender)); - return -1; + if (!is_manual(tk)) { + tk_log_error("%s wants to revoke ticket, " + "but it is not granted there (ignoring)", + site_string(sender)); + return -1; + } else { + rv = process_REVOKE_for_manual_ticket(tk, sender, msg); + + // Ticket data stored in this site is not modified. This means + // that this site will still follow another leader (the one which + // has not been revoked) or be a leader itself. + } } else if (tk->state != ST_FOLLOWER) { tk_log_error("unexpected ticket revoke from %s " "(in state %s) (ignoring)", @@ -413,8 +423,7 @@ tk_log_info("%s revokes ticket", site_string(tk->leader)); save_committed_tkt(tk); - reset_ticket(tk); - set_leader(tk, no_leader); + reset_ticket_and_set_no_leader(tk); ticket_write(tk); rv = send_msg(OP_ACK, tk, sender, msg); } @@ -959,6 +968,8 @@ tk_log_warn("unexpected message %s, from %s", state_to_string(cmd), site_string(sender)); + mark_ticket_as_granted(tk, sender); + if (ticket_seems_ok(tk)) send_reject(sender, tk, RLT_TERM_STILL_VALID, msg); rv = -EINVAL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/ticket.c new/booth-1.0+20171123.d4cb8cb/src/ticket.c --- old/booth-1.0+20170619.766d618/src/ticket.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/ticket.c 2017-11-23 14:07:59.000000000 +0100 @@ -40,6 +40,7 @@ #include "raft.h" #include "handler.h" #include "request.h" +#include "manual.h" #define TK_LINE 256 @@ -182,15 +183,27 @@ static void ext_prog_failed(struct ticket_config *tk, int start_election) { - /* Give it to somebody else. - * Just send a VOTE_FOR message, so the - * others can start elections. */ - if (leader_and_valid(tk)) { - save_committed_tkt(tk); - reset_ticket(tk); - ticket_write(tk); - if (start_election) { - ticket_broadcast(tk, OP_VOTE_FOR, OP_REQ_VOTE, RLT_SUCCESS, OR_LOCAL_FAIL); + if (!is_manual(tk)) { + /* Give it to somebody else. + * Just send a VOTE_FOR message, so the + * others can start elections. */ + if (leader_and_valid(tk)) { + save_committed_tkt(tk); + reset_ticket(tk); + ticket_write(tk); + if (start_election) { + ticket_broadcast(tk, OP_VOTE_FOR, OP_REQ_VOTE, RLT_SUCCESS, OR_LOCAL_FAIL); + } + } + } else { + /* There is not much we can do now because + * the manual ticket cannot be relocated. + * Just warn the user. */ + if (tk->leader == local) { + save_committed_tkt(tk); + reset_ticket(tk); + ticket_write(tk); + log_error("external test failed on the specified machine, cannot acquire a manual ticket"); } } } @@ -299,7 +312,12 @@ return RLT_EXT_FAILED; } - rv = new_election(tk, local, 1, reason); + if (is_manual(tk)) { + rv = manual_selection(tk, local, 1, reason); + } else { + rv = new_election(tk, local, 1, reason); + } + return rv ? RLT_SYNC_FAIL : 0; } @@ -314,8 +332,18 @@ if (tk->leader == local) return RLT_SUCCESS; - if (is_owned(tk)) - return RLT_OVERGRANT; + if (is_owned(tk)) { + if (is_manual(tk) && (options & OPT_IMMEDIATE)) { + /* -F flag has been used while granting a manual ticket. + * The ticket will be granted and may end up being granted + * on multiple sites */ + tk_log_warn("manual ticket forced to be granted! be aware that " + "you may end up having two sites holding the same manual " + "ticket! revoke the ticket from the unnecessary site!"); + } else { + return RLT_OVERGRANT; + } + } set_future_time(&tk->delay_commit, tk->term_duration + tk->acquire_after); @@ -340,8 +368,7 @@ tk_log_info("revoking ticket"); save_committed_tkt(tk); - reset_ticket(tk); - set_leader(tk, no_leader); + reset_ticket_and_set_no_leader(tk); ticket_write(tk); ticket_broadcast(tk, OP_REVOKE, OP_ACK, RLT_SUCCESS, OR_ADMIN); } @@ -367,20 +394,32 @@ char timeout_str[64]; char pending_str[64]; char *data, *cp; - int i, alloc; + int i, alloc, site_index; time_t ts; + int multiple_grant_warning_length = 0; *pdata = NULL; *len = 0; - alloc = booth_conf->ticket_count * (BOOTH_NAME_LEN * 2 + 128); + alloc = booth_conf->ticket_count * (BOOTH_NAME_LEN * 2 + 128 + 16); + + foreach_ticket(i, tk) { + multiple_grant_warning_length = number_sites_marked_as_granted(tk); + + if (multiple_grant_warning_length > 1) { + // 164: 55 + 45 + 2*number_of_multiple_sites + some margin + alloc += 164 + BOOTH_NAME_LEN * (1+multiple_grant_warning_length); + } + } + data = malloc(alloc); if (!data) return -ENOMEM; cp = data; foreach_ticket(i, tk) { - if (is_time_set(&tk->term_expires)) { + if ((!is_manual(tk)) && is_time_set(&tk->term_expires)) { + /* Manual tickets doesn't have term_expires defined */ ts = wall_ts(&tk->term_expires); strftime(timeout_str, sizeof(timeout_str), "%F %T", localtime(&ts)); @@ -407,19 +446,55 @@ if (is_owned(tk)) { cp += snprintf(cp, alloc - (cp - data), - ", expires: %s%s\n", + ", expires: %s%s", timeout_str, pending_str); - } else { - cp += snprintf(cp, alloc - (cp - data), "\n"); } + if (is_manual(tk)) { + cp += snprintf(cp, + alloc - (cp - data), + " [manual mode]"); + } + + cp += snprintf(cp, alloc - (cp - data), "\n"); + if (alloc - (cp - data) <= 0) { free(data); return -ENOMEM; } } + foreach_ticket(i, tk) { + multiple_grant_warning_length = number_sites_marked_as_granted(tk); + + if (multiple_grant_warning_length > 1) { + cp += snprintf(cp, + alloc - (cp - data), + "\nWARNING: The ticket %s is granted to multiple sites: ", // ~55 characters + tk->name); + + for(site_index=0; site_index<booth_conf->site_count; ++site_index) { + if (tk->sites_where_granted[site_index] > 0) { + cp += snprintf(cp, + alloc - (cp - data), + "%s", + site_string(&(booth_conf->site[site_index]))); + + if (--multiple_grant_warning_length > 0) { + cp += snprintf(cp, + alloc - (cp - data), + ", "); + } + } + } + + cp += snprintf(cp, + alloc - (cp - data), + ". Revoke the ticket from the faulty sites.\n"); // ~45 characters + } + } + *pdata = data; *len = cp - data; @@ -455,12 +530,20 @@ tk->voted_for = NULL; } +void reset_ticket_and_set_no_leader(struct ticket_config *tk) +{ + mark_ticket_as_revoked_from_leader(tk); + reset_ticket(tk); + + tk->leader = no_leader; + tk_log_debug("ticket leader set to no_leader"); +} static void log_reacquire_reason(struct ticket_config *tk) { int valid; const char *where_granted = "\0"; - char buff[64]; + char buff[75]; valid = is_time_set(&tk->term_expires) && !is_past(&tk->term_expires); @@ -606,9 +689,12 @@ goto reply_now; } - if ((cmd == CMD_GRANT) && is_owned(tk)) { + /* Perform the initial check before granting + * an already granted non-manual ticket */ + if ((!is_manual(tk) && (cmd == CMD_GRANT) && is_owned(tk))) { log_warn("client wants to grant an (already granted!) ticket %s", msg->ticket.id); + rv = RLT_OVERGRANT; goto reply_now; } @@ -726,12 +812,15 @@ if (tk->ticket_updated >= 2) return 0; - if (tk->ticket_updated < 1) { - tk->ticket_updated = 1; - get_time(&now); - copy_time(&now, &tk->last_renewal); - set_future_time(&tk->term_expires, tk->term_duration); - rv = ticket_broadcast(tk, OP_UPDATE, OP_ACK, RLT_SUCCESS, 0); + /* for manual tickets, we don't set time expiration */ + if (!is_manual(tk)) { + if (tk->ticket_updated < 1) { + tk->ticket_updated = 1; + get_time(&now); + copy_time(&now, &tk->last_renewal); + set_future_time(&tk->term_expires, tk->term_duration); + rv = ticket_broadcast(tk, OP_UPDATE, OP_ACK, RLT_SUCCESS, 0); + } } if (tk->ticket_updated < 2) { @@ -907,6 +996,7 @@ tk->lost_leader = tk->leader; save_committed_tkt(tk); + mark_ticket_as_revoked_from_leader(tk); reset_ticket(tk); set_state(tk, ST_FOLLOWER); if (local->type == SITE) { @@ -938,21 +1028,39 @@ break; case ST_FOLLOWER: - /* leader/ticket lost? and we didn't vote yet */ - tk_log_debug("leader: %s, voted_for: %s", - site_string(tk->leader), - site_string(tk->voted_for)); - if (!tk->leader) { - if (!tk->voted_for || !tk->in_election) { - disown_ticket(tk); - if (!new_election(tk, NULL, 1, OR_AGAIN)) { + if (!is_manual(tk)) { + /* leader/ticket lost? and we didn't vote yet */ + tk_log_debug("leader: %s, voted_for: %s", + site_string(tk->leader), + site_string(tk->voted_for)); + if (!tk->leader) { + if (!tk->voted_for || !tk->in_election) { + disown_ticket(tk); + if (!new_election(tk, NULL, 1, OR_AGAIN)) { + ticket_activate_timeout(tk); + } + } else { + /* we should restart elections in case nothing + * happens in the meantime */ + tk->in_election = 0; ticket_activate_timeout(tk); } + } + } else { + /* for manual tickets, also try to acquire ticket on grant + * in the Follower state (because we may end up having + * two Leaders) */ + if (has_extprog_exited(tk)) { + rv = acquire_ticket(tk, OR_ADMIN); + if (rv != 0) { /* external program failed */ + tk->outcome = rv; + foreach_tkt_req(tk, notify_client); + } } else { - /* we should restart elections in case nothing - * happens in the meantime */ - tk->in_election = 0; - ticket_activate_timeout(tk); + /* Otherwise, just send ACKs if needed */ + if (tk->acks_expected) { + handle_resends(tk); + } } } break; @@ -1010,9 +1118,11 @@ } /* Has an owner, has an expiry date, and expiry date in the past? - * Losing the ticket must happen in _every_ state. + * For automatic tickets, losing the ticket must happen + * in _every_ state. */ - if (is_owned(tk) && is_time_set(&tk->term_expires) + if ((!is_manual(tk)) && + is_owned(tk) && is_time_set(&tk->term_expires) && is_past(&tk->term_expires)) { ticket_lost(tk); goto out; @@ -1159,56 +1269,72 @@ { timetype near_future, tv, next_vote; - /* At least every hour, perhaps sooner (default) */ - ticket_next_cron_in(tk, 3600*TIME_RES); set_future_time(&near_future, 10); - switch (tk->state) { - case ST_LEADER: - assert(tk->leader == local); - - get_next_election_time(tk, &next_vote); + if (!is_manual(tk)) { + /* At least every hour, perhaps sooner (default) */ + tk_log_debug("ticket will be woken up after up to one hour"); + ticket_next_cron_in(tk, 3600*TIME_RES); + + switch (tk->state) { + case ST_LEADER: + assert(tk->leader == local); + + get_next_election_time(tk, &next_vote); + + /* If timestamp is in the past, wakeup in + * near future */ + if (!is_time_set(&next_vote)) { + tk_log_debug("next ts unset, wakeup soon"); + ticket_next_cron_at(tk, &near_future); + } else if (is_past(&next_vote)) { + int tdiff = time_left(&next_vote); + tk_log_debug("next ts in the past " intfmt(tdiff)); + ticket_next_cron_at(tk, &near_future); + } else { + ticket_next_cron_at(tk, &next_vote); + } + break; - /* If timestamp is in the past, wakeup in - * near future */ - if (!is_time_set(&next_vote)) { - tk_log_debug("next ts unset, wakeup soon"); - ticket_next_cron_at(tk, &near_future); - } else if (is_past(&next_vote)) { - int tdiff = time_left(&next_vote); - tk_log_debug("next ts in the past " intfmt(tdiff)); - ticket_next_cron_at(tk, &near_future); - } else { - ticket_next_cron_at(tk, &next_vote); - } - break; + case ST_CANDIDATE: + assert(is_time_set(&tk->election_end)); + ticket_next_cron_at(tk, &tk->election_end); + break; - case ST_CANDIDATE: - assert(is_time_set(&tk->election_end)); - ticket_next_cron_at(tk, &tk->election_end); - break; + case ST_INIT: + case ST_FOLLOWER: + /* If there is (or should be) some owner, check on it later on. + * If no one is interested - don't care. */ + if (is_owned(tk)) { + interval_add(&tk->term_expires, tk->acquire_after, &tv); + ticket_next_cron_at(tk, &tv); + } + break; - case ST_INIT: - case ST_FOLLOWER: - /* If there is (or should be) some owner, check on it later on. - * If no one is interested - don't care. */ - if (is_owned(tk)) { - interval_add(&tk->term_expires, tk->acquire_after, &tv); - ticket_next_cron_at(tk, &tv); + default: + tk_log_error("unknown ticket state: %d", tk->state); } - break; - default: - tk_log_error("unknown ticket state: %d", tk->state); - } - - if (tk->next_state) { - /* we need to do something soon here */ - if (!tk->acks_expected) { - ticket_next_cron_at(tk, &near_future); - } else { - ticket_activate_timeout(tk); + if (tk->next_state) { + /* we need to do something soon here */ + if (!tk->acks_expected) { + ticket_next_cron_at(tk, &near_future); + } else { + ticket_activate_timeout(tk); + } } + } else { + /* At least six minutes, to make sure that multi-leader situations + * will be solved promptly. + */ + tk_log_debug("manual ticket will be woken up after up to six minutes"); + ticket_next_cron_in(tk, 60*TIME_RES); + + /* For manual tickets, no earlier timeout could be set in a similar + * way as it is done in a switch above for automatic tickets. + * The reason is that term's timeout is INF and no Raft-based elections + * are performed. + */ } if (ANYDEBUG) { @@ -1229,6 +1355,24 @@ } +int is_manual(struct ticket_config *tk) +{ + return (tk->mode == TICKET_MODE_MANUAL) ? 1 : 0; +} + +int number_sites_marked_as_granted(struct ticket_config *tk) +{ + int i, result = 0; + + for(i=0; i<booth_conf->site_count; ++i) { + result += tk->sites_where_granted[i]; + } + + return result; +} + + + /* Given a state (in host byte order), return a human-readable (char*). * An array is used so that multiple states can be printed in a single printf(). */ char *state_to_string(uint32_t state_ho) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/ticket.h new/booth-1.0+20171123.d4cb8cb/src/ticket.h --- old/booth-1.0+20170619.766d618/src/ticket.h 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/ticket.h 2017-11-23 14:07:59.000000000 +0100 @@ -39,8 +39,36 @@ #define foreach_node(i_,n_) for(i_=0; (n_=booth_conf->site+i_, i_<booth_conf->site_count); i_++) #define set_leader(tk, who) do { \ + if (who == NULL) { \ + mark_ticket_as_revoked_from_leader(tk); \ + } \ + \ tk->leader = who; \ tk_log_debug("ticket leader set to %s", ticket_leader_string(tk)); \ + \ + if (tk->leader) { \ + mark_ticket_as_granted(tk, tk->leader); \ + } \ +} while(0) + +#define mark_ticket_as_granted(tk, who) do { \ + if (is_manual(tk) && (who->index > -1)) { \ + tk->sites_where_granted[who->index] = 1; \ + tk_log_debug("manual ticket marked as granted to %s", ticket_leader_string(tk)); \ + } \ +} while(0) + +#define mark_ticket_as_revoked(tk, who) do { \ + if (is_manual(tk) && who && (who->index > -1)) { \ + tk->sites_where_granted[who->index] = 0; \ + tk_log_debug("manual ticket marked as revoked from %s", site_string(who)); \ + } \ +} while(0) + +#define mark_ticket_as_revoked_from_leader(tk) do { \ + if (tk->leader) { \ + mark_ticket_as_revoked(tk, tk->leader); \ + } \ } while(0) #define set_state(tk, newst) do { \ @@ -69,6 +97,7 @@ int ticket_recv(void *buf, struct booth_site *source); void reset_ticket(struct ticket_config *tk); +void reset_ticket_and_set_no_leader(struct ticket_config *tk); void update_ticket_state(struct ticket_config *tk, struct booth_site *sender); int setup_ticket(void); int check_max_len_valid(const char *s, int max); @@ -103,6 +132,9 @@ void add_random_delay(struct ticket_config *tk); void schedule_election(struct ticket_config *tk, cmd_reason_t reason); +int is_manual(struct ticket_config *tk); +int number_sites_marked_as_granted(struct ticket_config *tk); + int check_attr_prereq(struct ticket_config *tk, grant_type_e grant_type); static inline void ticket_next_cron_at(struct ticket_config *tk, timetype *when) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/src/transport.c new/booth-1.0+20171123.d4cb8cb/src/transport.c --- old/booth-1.0+20170619.766d618/src/transport.c 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/src/transport.c 2017-11-23 14:07:59.000000000 +0100 @@ -237,16 +237,38 @@ BOOTH_IPADDR_LEN); } - /* First try with exact addresses, then optionally with subnet matching. */ + /* Try to find the exact address or the address with subnet matching. + * The function find_address will be called for each address received + * from NLMSG_DATA above. + * The exact match will be prefered. If no exact match is found, + * the function find_address will try to return another, most similar + * address (with the longest possible number of same bytes). */ if (ifa->ifa_prefixlen > address_bits_matched) { find_address(ipaddr, ifa->ifa_family, ifa->ifa_prefixlen, fuzzy_allowed, &me, &address_bits_matched); + if (me) { log_debug("found myself at %s (%d bits matched)", site_string(me), address_bits_matched); } } + /* If the previous NLMSG_DATA calls have already allowed us + * to find an address with address_bits_matched matching bits, + * then no other better non-exact address can bo found. + * But we can still try to find an exact match, so let us + * call the function find_address with disabled searching of + * similar addresses (fuzzy_allowed == 0) */ + else if (ifa->ifa_prefixlen == address_bits_matched) { + find_address(ipaddr, + ifa->ifa_family, ifa->ifa_prefixlen, + 0 /* fuzzy_allowed */, &me, &address_bits_matched); + + if (me) { + log_debug("found myself at %s (exact match)", + site_string(me)); + } + } } h = NLMSG_NEXT(h, status); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/test/boothrunner.py new/booth-1.0+20171123.d4cb8cb/test/boothrunner.py --- old/booth-1.0+20170619.766d618/test/boothrunner.py 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/test/boothrunner.py 2017-11-23 14:07:59.000000000 +0100 @@ -31,6 +31,9 @@ def set_debug(self): self.args += [ '-D' ] + def set_foreground(self): + self.args += [ '-S' ] + def all_args(self): return [ self.boothd_path ] + self.args + self.final_args diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/test/live_test.sh new/booth-1.0+20171123.d4cb8cb/test/live_test.sh --- old/booth-1.0+20170619.766d618/test/live_test.sh 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/test/live_test.sh 2017-11-23 14:07:59.000000000 +0100 @@ -287,7 +287,7 @@ local h rc=0 local tmpf for h in $sites $arbitrators; do - rsync -q -e "ssh $SSH_OPTS" $cnf root@$h:$run_cnf + rsync -q -e "ssh $SSH_OPTS" $1 root@$h:$run_cnf rc=$((rc|$?)) if [ -n "$authfile" ]; then tmpf=`mktemp` @@ -398,6 +398,13 @@ /^ticket.*'$tkt'/ {n=1} ' $cnf } +get_mode() { + awk ' +n && /^[[:space:]]*mode/ {print $NF; exit} +n && (/^$/ || /^ticket.*/) {exit} +/^ticket.*'$tkt'/ {n=1} +' $cnf +} set_site_attr() { local site @@ -440,7 +447,7 @@ } n && (/^$/ || /^ticket.*/) {exit} /^ticket.*'$tkt'/ {n=1} -' $cnf +' $1 } wait_exp() { sleep $T_expire @@ -588,17 +595,25 @@ check_booth_consistency() { local tlist tlist_validate rc rc_lead maxdiff tlist=`forall_withname booth list 2>/dev/null | grep $tkt` - tlist_validate=`echo "$tlist" | - sed 's/[^:]*: //;s/commit:.*//;s/NONE/none/'` - maxdiff=`echo "$tlist" | max_booth_time_diff` - test "$maxdiff" -eq 0 - rc=$? + + # Check time consistency + ticket_times=$(echo "$tlist" | booth_list_fld 3) + if [[ $ticket_times == *"INF"* ]]; then + rc=0 + else + maxdiff=`echo "$tlist" | max_booth_time_diff` + test "$maxdiff" -eq 0 + rc=$? + fi + + # Check leader consistency echo "$tlist" | booth_leader_consistency rc_lead=$? if [ $rc_lead -ne 0 ]; then echo "$tlist" | booth_leader_consistency_2 rc_lead=$(($rc_lead + $?)) # rc_lead=2 if the prev test failed fi + rc=$(($rc | $rc_lead<<1)) test $rc -eq 0 && return cat<<EOF | logmsg @@ -668,7 +683,7 @@ TEST=$1 start_time=`date` start_ts=`date +%s` - echo -n "Testing: $1... " + echo -n "Testing: $1 (ticket: $tkt)... " can_run_test $1 || return 0 echo "==================================================" | logmsg echo "starting booth test $1 ..." | logmsg @@ -706,7 +721,7 @@ esac end_time=`date` end_ts=`date +%s` - echo "finished booth test $1 ($usrmsg)" | logmsg + echo "finished booth test $1 ($tkt): $usrmsg" | logmsg echo "==================================================" | logmsg is_function recover_$1 && recover_$1 reset_netem_env @@ -1202,13 +1217,6 @@ : ${port:=9929} site_cnt=`echo $internal_sites | wc -w` arbitrator_cnt=`echo $internal_arbitrators | wc -w` -tkt=`get_tkt < $cnf` -eval `get_tkt_settings` - -MIN_TIMEOUT=`awk -v tm=$T_timeout 'BEGIN{ - if (tm >= 2) print tm; - else print 2*tm; - }'` if [ "$1" = "__netem__" ]; then shift 1 @@ -1222,15 +1230,6 @@ usage 1 } -[ -z "$T_expire" ] && { - echo set $tkt expire time in $cnf - usage 1 -} - -if [ -z "$T_renewal_freq" ]; then - T_renewal_freq=$((T_expire/2)) -fi - exec 2>$logf BASH_XTRACEFD=2 PS4='+ `date +"%T"`: ' @@ -1244,22 +1243,8 @@ authfile=`get_value authfile < $cnf` run_site 1 'test -f '"$authfile"' || booth-keygen '"$authfile" -sync_conf || exit -reboot_test -all_booth_status || { - start_booth - all_booth_status || { - echo "some booth servers couldn't be started" - exit 1 - } -} -revoke_ticket - -ABSPATH=`get_prog_abspath` - -dump_conf | logmsg - TESTS="$@" +MANUAL_TESTS="$@" : ${TESTS:="grant longgrant grant_noarb grant_elsewhere grant_site_lost grant_site_reappear revoke @@ -1268,9 +1253,99 @@ failover split_leader split_follower split_edge external_prog_failed attr_prereq_ok attr_prereq_fail"} +: ${MANUAL_TESTS:="grant longgrant grant_noarb grant_elsewhere +grant_site_lost +restart_granted reload_granted +split_leader split_follower split_edge + "} + +#get total number od lines in the file +conf_file_size=$(grep -c $ $cnf) + +#get line numbers for all tickets +ticket_line_numbers=$(grep -n ticket $cnf | cut -d: -f1) +read -a TICKET_LINES<<< $ticket_line_numbers + +#save the part of config located before ticket definitions +sed -n "1,$((${TICKET_LINES[0]}-1))p" $cnf > ${cnf}_main.config + +#create a separate file for every ticket data +number_of_tickets=0 +for i in $(seq 0 1 $((${#TICKET_LINES[@]}-1))); do + ticket_line_start=${TICKET_LINES[i]} + ticket_line_end=$((${TICKET_LINES[i+1]}-1)) + if [ ${ticket_line_end} -lt 0 ]; then + # for the last ticket + ticket_line_end=${conf_file_size} + fi + sed -n "${ticket_line_start},${ticket_line_end}p" $cnf > ${cnf}_${number_of_tickets}.ticket + number_of_tickets=$((number_of_tickets+1)) +done + + master_rc=0 # updated in runtest -for t in $TESTS; do - runtest $t + +for i in `seq 0 $(($number_of_tickets-1))` +do + cat ${cnf}_main.config > booth_${i}.conf + cat ${cnf}_${i}.ticket >> booth_${i}.conf + + tkt=`get_tkt < booth_${i}.conf` + + if [ -z "$tkt" ]; then + echo "Skipping empty ticket.." + continue + fi + + sync_conf booth_${i}.conf || exit + reboot_test + all_booth_status || { + start_booth + all_booth_status || { + echo "some booth servers couldn't be started" + exit 1 + } + } + + ABSPATH=`get_prog_abspath` + + dump_conf | logmsg + + + eval `get_tkt_settings booth_${i}.conf` + + MIN_TIMEOUT=`awk -v tm=$T_timeout 'BEGIN{ + if (tm >= 2) print tm; + else print 2*tm; + }'` + + [ -z "$T_expire" ] && { + echo set $tkt expire time in $cnf + usage 1 + } + + if [ -z "$T_renewal_freq" ]; then + T_renewal_freq=$((T_expire/2)) + fi + + revoke_ticket + + T_mode=`get_mode` + T_mode_lowercase=$(echo "$T_mode" | tr '[:upper:]' '[:lower:]') + + if [[ $T_mode_lowercase == *"manual"* ]]; then + echo "Running tests for manual tickets.." + + for t in $MANUAL_TESTS; do + runtest $t + done + else + echo "Running tests for automatic Raft tickets.." + + for t in $TESTS; do + runtest $t + done + fi done exit $master_rc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/test/serverenv.py new/booth-1.0+20171123.d4cb8cb/test/serverenv.py --- old/booth-1.0+20170619.766d618/test/serverenv.py 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/test/serverenv.py 2017-11-23 14:07:59.000000000 +0100 @@ -33,7 +33,7 @@ def run_booth(self, expected_exitcode, expected_daemon, config_text=None, config_file=None, lock_file=True, - args=[], debug=False): + args=[], debug=False, foreground=False): ''' Runs boothd. Defaults to using a temporary lock file and the standard config file path. There are four possible types of @@ -42,7 +42,7 @@ - boothd exits non-zero without launching a daemon (setup phase failed, e.g. due to invalid configuration file) - boothd exits zero after launching a daemon (successful operation) - - boothd does not exit (running in foreground / debug mode) + - boothd does not exit (running in foreground mode) - boothd does not exit (setup phase hangs, e.g. while attempting to connect to peer during ticket_catchup()) @@ -61,13 +61,15 @@ an integer, or False if booth is not expected to terminate within the timeout expected_daemon - True iff a daemon is expected to be launched (this includes - running the server in debug / foreground mode via -D; even - though in this case the server's not technically not a daemon, + True iff a daemon is expected to be launched (this means + running the server in foreground mode via -S; + even though in this case the server's not technically not a daemon, we still want to treat it like one by checking the lockfile before and after we kill it) debug True means pass the -D parameter + foreground + True means pass the -S parameter Returns a (pid, return_code, stdout, stderr, runner) tuple, where return_code/stdout/stderr are None iff pid is still running. @@ -97,6 +99,9 @@ if debug: runner.set_debug() + if foreground: + runner.set_foreground() + runner.show_args() (pid, return_code, stdout, stderr) = runner.run() self.check_return_code(pid, return_code, expected_exitcode) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/booth-1.0+20170619.766d618/test/servertests.py new/booth-1.0+20171123.d4cb8cb/test/servertests.py --- old/booth-1.0+20170619.766d618/test/servertests.py 2017-06-19 12:23:29.000000000 +0200 +++ new/booth-1.0+20171123.d4cb8cb/test/servertests.py 2017-11-23 14:07:59.000000000 +0100 @@ -86,6 +86,16 @@ def test_debug_mode(self): (pid, ret, stdout, stderr, runner) = \ self.run_booth(config_text=self.working_config, debug=True, + expected_exitcode=0, expected_daemon=True) + + def test_foreground_mode(self): + (pid, ret, stdout, stderr, runner) = \ + self.run_booth(config_text=self.working_config, foreground=True, + expected_exitcode=None, expected_daemon=True) + + def test_debug_and_foreground_mode(self): + (pid, ret, stdout, stderr, runner) = \ + self.run_booth(config_text=self.working_config, debug=True, foreground=True, expected_exitcode=None, expected_daemon=True) def test_missing_transport(self):
