1    	/*
2    	 * Copyright 2004-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU Lesser General Public License
7    	 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdbool.h>
13   	#include <sys/time.h>
14   	#include <sys/resource.h>
15   	
16   	#include <crm/crm.h>
17   	#include <crm/common/mainloop.h>
18   	#include <crm/common/xml.h>
19   	
20   	#include <crm/cluster/internal.h>
21   	#include <crm/cluster/election_internal.h>
22   	#include "crmcluster_private.h"
23   	
24   	#define STORM_INTERVAL   2      /* in seconds */
25   	
26   	struct pcmk__election {
27   	    enum election_result state;     // Current state of election
28   	    unsigned int count;             // How many times local node has voted
29   	    void (*cb)(pcmk_cluster_t *);   // Function to call if election is won
30   	    GHashTable *voted;  // Key = node name, value = how node voted
31   	    pcmk__main_loop_timer_t *timeout; // When to abort if all votes not received
32   	    int election_wins;         // Track wins, for storm detection
33   	    bool wrote_blackbox;       // Write a storm blackbox at most once
34   	    time_t expires;            // When storm detection period ends
35   	    time_t last_election_loss; // When dampening period ends
36   	};
37   	
38   	static void
39   	election_complete(pcmk_cluster_t *cluster)
40   	{
41   	    pcmk__assert((cluster != NULL) && (cluster->priv->election != NULL));
42   	    cluster->priv->election->state = election_won;
43   	    if (cluster->priv->election->cb != NULL) {
44   	        cluster->priv->election->cb(cluster);
45   	    }
46   	    election_reset(cluster);
47   	}
48   	
49   	static gboolean
50   	election_timer_cb(void *user_data)
51   	{
52   	    pcmk_cluster_t *cluster = user_data;
53   	
54   	    pcmk__info("Declaring local node as winner after election timed out");
55   	    election_complete(cluster);
56   	    return G_SOURCE_REMOVE;
57   	}
58   	
59   	/*!
60   	 * \internal
61   	 * \brief Get current state of an election
62   	 *
63   	 * \param[in] cluster  Cluster with election
64   	 *
65   	 * \return Current state of \e
66   	 */
67   	enum election_result
68   	election_state(const pcmk_cluster_t *cluster)
69   	{
70   	    if ((cluster == NULL) || (cluster->priv->election == NULL)) {
71   	        return election_error;
72   	    }
73   	    return cluster->priv->election->state;
74   	}
75   	
76   	/* The local node will be declared the winner if missing votes are not received
77   	 * within this time. The value is chosen to be the same as the default for the
78   	 * election-timeout cluster option.
79   	 */
80   	#define ELECTION_TIMEOUT_MS 120000
81   	
82   	/*!
83   	 * \internal
84   	 * \brief Track election state in a cluster
85   	 *
86   	 * Every node that wishes to participate in an election must initialize the
87   	 * election once, typically at start-up.
88   	 *
89   	 * \param[in] cluster    Cluster that election is for
90   	 * \param[in] cb         Function to call if local node wins election
91   	 */
92   	void
93   	election_init(pcmk_cluster_t *cluster, void (*cb)(pcmk_cluster_t *))
94   	{
95   	    const char *name = pcmk__s(crm_system_name, "election");
96   	
97   	    CRM_CHECK(cluster->priv->election == NULL, return);
98   	
CID (unavailable; MK=bf77ad6f79598a6146f5533ef1330eae) (#1 of 1): Resource not released (INCOMPLETE_DEALLOCATOR):
(1) Event allocation: Memory is allocated. [details]
(2) Event allocation: The field "cluster->priv->election" is allocated, but not released in the identified deallocator.
Also see events: [deallocator]
99   	    cluster->priv->election = pcmk__assert_alloc(1, sizeof(pcmk__election_t));
100  	    cluster->priv->election->cb = cb;
101  	    cluster->priv->election->timeout =
102  	        pcmk__main_loop_timer_new(name, ELECTION_TIMEOUT_MS, election_timer_cb,
103  	                                  cluster);
104  	}
105  	
106  	/*!
107  	 * \internal
108  	 * \brief Disregard any previous vote by specified peer
109  	 *
110  	 * This discards any recorded vote from a specified peer. Election users should
111  	 * call this whenever a voting peer becomes inactive.
112  	 *
113  	 * \param[in,out] cluster  Cluster with election
114  	 * \param[in]     uname    Name of peer to disregard
115  	 */
116  	void
117  	election_remove(pcmk_cluster_t *cluster, const char *uname)
118  	{
119  	    if ((cluster != NULL) && (cluster->priv->election != NULL)
120  	        && (uname != NULL) && (cluster->priv->election->voted != NULL)) {
121  	        pcmk__trace("Discarding (no-)vote from lost peer %s", uname);
122  	        g_hash_table_remove(cluster->priv->election->voted, uname);
123  	    }
124  	}
125  	
126  	/*!
127  	 * \internal
128  	 * \brief Stop election timer and disregard all votes
129  	 *
130  	 * \param[in,out] cluster  Cluster with election
131  	 */
132  	void
133  	election_reset(pcmk_cluster_t *cluster)
134  	{
135  	    if ((cluster != NULL) && (cluster->priv->election != NULL)) {
136  	        pcmk__trace("Resetting election");
137  	        pcmk__main_loop_timer_stop(cluster->priv->election->timeout);
138  	        g_clear_pointer(&cluster->priv->election->voted, g_hash_table_destroy);
139  	    }
140  	}
141  	
142  	/*!
143  	 * \internal
144  	 * \brief Free an election object
145  	 *
146  	 * Free all memory associated with an election object, stopping its
147  	 * election timer (if running).
148  	 *
149  	 * \param[in,out] cluster  Cluster with election
150  	 */
151  	void
(3) Event deallocator: Deallocator for "struct pcmk__cluster".
Also see events: [allocation][allocation]
152  	election_fini(pcmk_cluster_t *cluster)
153  	{
154  	    if ((cluster != NULL) && (cluster->priv->election != NULL)) {
155  	        election_reset(cluster);
156  	        pcmk__trace("Destroying election");
157  	        pcmk__main_loop_timer_free(cluster->priv->election->timeout);
158  	        g_clear_pointer(&cluster->priv->election, free);
159  	    }
160  	}
161  	
162  	static void
163  	election_timeout_start(pcmk_cluster_t *cluster)
164  	{
165  	    pcmk__main_loop_timer_start(cluster->priv->election->timeout);
166  	}
167  	
168  	/*!
169  	 * \internal
170  	 * \brief Stop an election's timer, if running
171  	 *
172  	 * \param[in,out] cluster  Cluster with election
173  	 */
174  	void
175  	election_timeout_stop(pcmk_cluster_t *cluster)
176  	{
177  	    if ((cluster != NULL) && (cluster->priv->election != NULL)) {
178  	        pcmk__main_loop_timer_stop(cluster->priv->election->timeout);
179  	    }
180  	}
181  	
182  	/*!
183  	 * \internal
184  	 * \brief Change an election's timeout (restarting timer if running)
185  	 *
186  	 * \param[in,out] cluster      Cluster with election
187  	 * \param[in]     interval_ms  New timer interval in milliseconds
188  	 */
189  	void
190  	election_timeout_set_interval(pcmk_cluster_t *cluster, unsigned int interval_ms)
191  	{
192  	    pcmk__main_loop_timer_t *timer = NULL;
193  	
194  	    CRM_CHECK((cluster != NULL)
195  	              && (cluster->priv->election != NULL)
196  	              && (cluster->priv->election->timeout != NULL),
197  	              return);
198  	
199  	    timer = cluster->priv->election->timeout;
200  	
201  	    if (timer->interval_ms == interval_ms) {
202  	        return;
203  	    }
204  	
205  	    timer->interval_ms = interval_ms;
206  	
207  	    if (pcmk__main_loop_timer_running(timer)) {
208  	        // Restart the timer using the new interval if it changed
209  	        pcmk__main_loop_timer_start(timer);
210  	    }
211  	}
212  	
213  	static int
214  	get_uptime(struct timeval *output)
215  	{
216  	    static time_t expires = 0;
217  	    static struct rusage info;
218  	
219  	    time_t tm_now = time(NULL);
220  	
221  	    if (expires < tm_now) {
222  	        int rc = 0;
223  	
224  	        output->tv_sec = 0;
225  	        output->tv_usec = 0;
226  	
227  	        info.ru_utime.tv_sec = 0;
228  	        info.ru_utime.tv_usec = 0;
229  	
230  	        rc = getrusage(RUSAGE_SELF, &info);
231  	        if (rc < 0) {
232  	            pcmk__err("Could not calculate the current uptime: %s",
233  	                      strerror(errno));
234  	            expires = 0;
235  	            return -1;
236  	        }
237  	
238  	        pcmk__debug("Current CPU usage is: %llds, %lldus",
239  	                    (long long) info.ru_utime.tv_sec,
240  	                    (long long) info.ru_utime.tv_usec);
241  	    }
242  	
243  	    expires = tm_now + STORM_INTERVAL;  /* N seconds after the last _access_ */
244  	    output->tv_sec = info.ru_utime.tv_sec;
245  	    output->tv_usec = info.ru_utime.tv_usec;
246  	
247  	    return 1;
248  	}
249  	
250  	static int
251  	compare_age(struct timeval your_age)
252  	{
253  	    struct timeval our_age;
254  	
255  	    get_uptime(&our_age); /* If an error occurred, our_age will be compared as {0,0} */
256  	
257  	    if (our_age.tv_sec > your_age.tv_sec) {
258  	        pcmk__debug("Win: %lld vs %lld (seconds)",
259  	                    (long long) our_age.tv_sec, (long long) your_age.tv_sec);
260  	        return 1;
261  	    } else if (our_age.tv_sec < your_age.tv_sec) {
262  	        pcmk__debug("Lose: %lld vs %lld (seconds)",
263  	                    (long long) our_age.tv_sec, (long long) your_age.tv_sec);
264  	        return -1;
265  	    } else if (our_age.tv_usec > your_age.tv_usec) {
266  	        pcmk__debug("Win: %lld.%06lld vs %lld.%06lld (usec)",
267  	                    (long long) our_age.tv_sec, (long long) our_age.tv_usec,
268  	                    (long long) your_age.tv_sec, (long long) your_age.tv_usec);
269  	        return 1;
270  	    } else if (our_age.tv_usec < your_age.tv_usec) {
271  	        pcmk__debug("Lose: %lld.%06lld vs %lld.%06lld (usec)",
272  	                    (long long) our_age.tv_sec, (long long) our_age.tv_usec,
273  	                    (long long) your_age.tv_sec, (long long) your_age.tv_usec);
274  	        return -1;
275  	    }
276  	
277  	    return 0;
278  	}
279  	
280  	/*!
281  	 * \internal
282  	 * \brief Start a new election by offering local node's candidacy
283  	 *
284  	 * Broadcast a "vote" election message containing the local node's ID,
285  	 * (incremented) election counter, and uptime, and start the election timer.
286  	 *
287  	 * \param[in,out] cluster  Cluster with election
288  	 *
289  	 * \note Any nodes agreeing to the candidacy will send a "no-vote" reply, and if
290  	 *       all active peers do so, or if the election times out, the local node
291  	 *       wins the election. (If we lose to any peer vote, we will stop the
292  	 *       timer, so a timeout means we did not lose -- either some peer did not
293  	 *       vote, or we did not call election_check() in time.)
294  	 */
295  	void
296  	election_vote(pcmk_cluster_t *cluster)
297  	{
298  	    struct timeval age;
299  	    xmlNode *vote = NULL;
300  	    pcmk__node_status_t *our_node = NULL;
301  	    const char *message_type = NULL;
302  	
303  	    CRM_CHECK((cluster != NULL) && (cluster->priv->election != NULL), return);
304  	
305  	    if (cluster->priv->node_name == NULL) {
306  	        pcmk__err("Cannot start an election: Local node name unknown");
307  	        return;
308  	    }
309  	
310  	    our_node = pcmk__get_node(0, cluster->priv->node_name, NULL,
311  	                              pcmk__node_search_cluster_member);
312  	    if (!pcmk__cluster_is_node_active(our_node)) {
313  	        pcmk__trace("Cannot vote yet: local node not connected to cluster");
314  	        return;
315  	    }
316  	
317  	    election_reset(cluster);
318  	    cluster->priv->election->state = election_in_progress;
319  	    message_type = pcmk__server_message_type(cluster->priv->server);
320  	
321  	    /* @COMPAT We use message_type as the sender and recipient system for
322  	     * backward compatibility (see T566).
323  	     */
324  	    vote = pcmk__new_request(cluster->priv->server, message_type,
325  	                             NULL, message_type, CRM_OP_VOTE, NULL);
326  	
327  	    cluster->priv->election->count++;
328  	    pcmk__xe_set(vote, PCMK__XA_ELECTION_OWNER,
329  	                 pcmk__cluster_get_xml_id(our_node));
330  	    pcmk__xe_set_int(vote, PCMK__XA_ELECTION_ID,
331  	                     cluster->priv->election->count);
332  	
333  	    // Warning: PCMK__XA_ELECTION_AGE_NANO_SEC value is actually microseconds
334  	    get_uptime(&age);
335  	    pcmk__xe_set_timeval(vote, PCMK__XA_ELECTION_AGE_SEC,
336  	                         PCMK__XA_ELECTION_AGE_NANO_SEC, &age);
337  	
338  	    pcmk__cluster_send_message(NULL, cluster->priv->server, vote);
339  	    pcmk__xml_free(vote);
340  	
341  	    pcmk__debug("Started election round %u", cluster->priv->election->count);
342  	    election_timeout_start(cluster);
343  	}
344  	
345  	/*!
346  	 * \internal
347  	 * \brief Check whether local node has won an election
348  	 *
349  	 * If all known peers have sent no-vote messages, stop the election timer, set
350  	 * the election state to won, and call any registered win callback.
351  	 *
352  	 * \param[in,out] cluster  Cluster with election
353  	 *
354  	 * \return TRUE if local node has won, FALSE otherwise
355  	 * \note If all known peers have sent no-vote messages, but the election owner
356  	 *       does not call this function, the election will not be won (and the
357  	 *       callback will not be called) until the election times out.
358  	 * \note This should be called when election_count_vote() returns
359  	 *       \c election_in_progress.
360  	 */
361  	bool
362  	election_check(pcmk_cluster_t *cluster)
363  	{
364  	    int voted_size = 0;
365  	    int num_members = 0;
366  	
367  	    CRM_CHECK((cluster != NULL) && (cluster->priv->election != NULL),
368  	              return false);
369  	
370  	    if (cluster->priv->election->voted == NULL) {
371  	        pcmk__trace("Election check requested, but no votes received yet");
372  	        return FALSE;
373  	    }
374  	
375  	    voted_size = g_hash_table_size(cluster->priv->election->voted);
376  	    num_members = pcmk__cluster_num_active_nodes();
377  	
378  	    /* in the case of #voted > #members, it is better to
379  	     *   wait for the timeout and give the cluster time to
380  	     *   stabilize
381  	     */
382  	    if (voted_size >= num_members) {
383  	        /* we won and everyone has voted */
384  	        election_timeout_stop(cluster);
385  	        if (voted_size > num_members) {
386  	            GHashTableIter gIter;
387  	            const pcmk__node_status_t *node = NULL;
388  	            char *key = NULL;
389  	
390  	            pcmk__warn("Received too many votes in election");
391  	            g_hash_table_iter_init(&gIter, pcmk__peer_cache);
392  	            while (g_hash_table_iter_next(&gIter, NULL, (void **) &node)) {
393  	                if (pcmk__cluster_is_node_active(node)) {
394  	                    pcmk__warn("* expected vote: %s", node->name);
395  	                }
396  	            }
397  	
398  	            g_hash_table_iter_init(&gIter, cluster->priv->election->voted);
399  	            while (g_hash_table_iter_next(&gIter, (void **) &key, NULL)) {
400  	                pcmk__warn("* actual vote: %s", key);
401  	            }
402  	
403  	        }
404  	
405  	        pcmk__info("Election won by local node");
406  	        election_complete(cluster);
407  	        return TRUE;
408  	
409  	    } else {
410  	        pcmk__debug("Election still waiting on %d of %d vote%s",
411  	                    (num_members - voted_size), num_members,
412  	                    pcmk__plural_s(num_members));
413  	    }
414  	
415  	    return FALSE;
416  	}
417  	
418  	#define LOSS_DAMPEN 2           /* in seconds */
419  	
420  	struct vote {
421  	    const char *op;
422  	    const char *from;
423  	    const char *version;
424  	    const char *election_owner;
425  	    int election_id;
426  	    struct timeval age;
427  	};
428  	
429  	/*!
430  	 * \internal
431  	 * \brief Unpack an election message
432  	 *
433  	 * \param[in] message  Election message XML
434  	 * \param[out] vote    Parsed fields from message
435  	 *
436  	 * \return TRUE if election message and election are valid, FALSE otherwise
437  	 * \note The parsed struct's pointer members are valid only for the lifetime of
438  	 *       the message argument.
439  	 */
440  	static bool
441  	parse_election_message(const xmlNode *message, struct vote *vote)
442  	{
443  	    CRM_CHECK(message && vote, return FALSE);
444  	
445  	    vote->election_id = -1;
446  	    vote->age.tv_sec = -1;
447  	    vote->age.tv_usec = -1;
448  	
449  	    vote->op = pcmk__xe_get(message, PCMK__XA_CRM_TASK);
450  	    vote->from = pcmk__xe_get(message, PCMK__XA_SRC);
451  	    vote->version = pcmk__xe_get(message, PCMK_XA_VERSION);
452  	    vote->election_owner = pcmk__xe_get(message, PCMK__XA_ELECTION_OWNER);
453  	
454  	    pcmk__xe_get_int(message, PCMK__XA_ELECTION_ID, &vote->election_id);
455  	
456  	    if ((vote->op == NULL) || (vote->from == NULL) || (vote->version == NULL)
457  	        || (vote->election_owner == NULL) || (vote->election_id < 0)) {
458  	
459  	        pcmk__warn("Invalid %s message from %s", pcmk__s(vote->op, "election"),
460  	                   pcmk__s(vote->from, "unspecified node"));
461  	        pcmk__log_xml_trace(message, "bad-vote");
462  	        return FALSE;
463  	    }
464  	
465  	    // Op-specific validation
466  	
467  	    if (pcmk__str_eq(vote->op, CRM_OP_VOTE, pcmk__str_none)) {
468  	        /* Only vote ops have uptime.
469  	           Warning: PCMK__XA_ELECTION_AGE_NANO_SEC value is in microseconds.
470  	         */
471  	        if ((pcmk__xe_get_timeval(message, PCMK__XA_ELECTION_AGE_SEC,
472  	                                  PCMK__XA_ELECTION_AGE_NANO_SEC,
473  	                                  &vote->age) != pcmk_rc_ok)
474  	            || (vote->age.tv_sec < 0) || (vote->age.tv_usec < 0)) {
475  	
476  	            pcmk__warn("Cannot count election %s from %s because uptime is "
477  	                       "missing or invalid",
478  	                       vote->op, vote->from);
479  	            return FALSE;
480  	        }
481  	
482  	    } else if (!pcmk__str_eq(vote->op, CRM_OP_NOVOTE, pcmk__str_none)) {
483  	        pcmk__info("Cannot process election message from %s because %s is not "
484  	                   "a known election op",
485  	                   vote->from, vote->op);
486  	        return FALSE;
487  	    }
488  	
489  	    /* If the membership cache is NULL, we REALLY shouldn't be voting --
490  	     * the question is how we managed to get here.
491  	     */
492  	    if (pcmk__peer_cache == NULL) {
493  	        pcmk__info("Cannot count election %s from %s becasue no peer "
494  	                   "information available",
495  	                   vote->op, vote->from);
496  	        return FALSE;
497  	    }
498  	    return TRUE;
499  	}
500  	
501  	static void
502  	record_vote(pcmk_cluster_t *cluster, struct vote *vote)
503  	{
504  	    pcmk__assert((vote->from != NULL) && (vote->op != NULL));
505  	
506  	    if (cluster->priv->election->voted == NULL) {
507  	        cluster->priv->election->voted = pcmk__strkey_table(free, free);
508  	    }
509  	    pcmk__insert_dup(cluster->priv->election->voted, vote->from, vote->op);
510  	}
511  	
512  	static void
513  	send_no_vote(pcmk_cluster_t *cluster, pcmk__node_status_t *peer,
514  	             struct vote *vote)
515  	{
516  	    const char *message_type = NULL;
517  	    xmlNode *novote = NULL;
518  	
519  	    message_type = pcmk__server_message_type(cluster->priv->server);
520  	    novote = pcmk__new_request(cluster->priv->server, message_type,
521  	                               vote->from, message_type, CRM_OP_NOVOTE, NULL);
522  	    pcmk__xe_set(novote, PCMK__XA_ELECTION_OWNER, vote->election_owner);
523  	    pcmk__xe_set_int(novote, PCMK__XA_ELECTION_ID, vote->election_id);
524  	
525  	    pcmk__cluster_send_message(peer, cluster->priv->server, novote);
526  	    pcmk__xml_free(novote);
527  	}
528  	
529  	/*!
530  	 * \internal
531  	 * \brief Process an election message (vote or no-vote) from a peer
532  	 *
533  	 * \param[in,out] cluster  Cluster with election
534  	 * \param[in]     message  Election message XML from peer
535  	 * \param[in]     can_win  Whether local node is eligible to win
536  	 *
537  	 * \return Election state after new vote is considered
538  	 * \note If the peer message is a vote, and we prefer the peer to win, this will
539  	 *       send a no-vote reply to the peer.
540  	 * \note The situations "we lost to this vote" from "this is a late no-vote
541  	 *       after we've already lost" both return election_lost. If a caller needs
542  	 *       to distinguish them, it should save the current state before calling
543  	 *       this function, and then compare the result.
544  	 */
545  	enum election_result
546  	election_count_vote(pcmk_cluster_t *cluster, const xmlNode *message,
547  	                    bool can_win)
548  	{
549  	    int log_level = LOG_INFO;
550  	    gboolean done = FALSE;
551  	    gboolean we_lose = FALSE;
552  	    const char *reason = NULL;
553  	    bool we_are_owner = FALSE;
554  	    pcmk__node_status_t *our_node = NULL;
555  	    pcmk__node_status_t *your_node = NULL;
556  	    time_t tm_now = time(NULL);
557  	    struct vote vote;
558  	
559  	    CRM_CHECK((cluster != NULL) && (cluster->priv->election != NULL)
560  	              && (message != NULL) && (cluster->priv->node_name != NULL),
561  	              return election_error);
562  	
563  	    if (!parse_election_message(message, &vote)) {
564  	        return election_error;
565  	    }
566  	
567  	    your_node = pcmk__get_node(0, vote.from, NULL,
568  	                               pcmk__node_search_cluster_member);
569  	    our_node = pcmk__get_node(0, cluster->priv->node_name, NULL,
570  	                              pcmk__node_search_cluster_member);
571  	    we_are_owner = (our_node != NULL)
572  	                   && pcmk__str_eq(pcmk__cluster_get_xml_id(our_node),
573  	                                   vote.election_owner, pcmk__str_none);
574  	
575  	    if (!can_win) {
576  	        reason = "Not eligible";
577  	        we_lose = TRUE;
578  	
579  	    } else if (!pcmk__cluster_is_node_active(our_node)) {
580  	        reason = "We are not part of the cluster";
581  	        log_level = LOG_ERR;
582  	        we_lose = TRUE;
583  	
584  	    } else if (we_are_owner
585  	               && (vote.election_id != cluster->priv->election->count)) {
586  	        log_level = LOG_TRACE;
587  	        reason = "Superseded";
588  	        done = TRUE;
589  	
590  	    } else if (!pcmk__cluster_is_node_active(your_node)) {
591  	        /* Possibly we cached the message in the FSA queue at a point that it wasn't */
592  	        reason = "Peer is not part of our cluster";
593  	        log_level = LOG_WARNING;
594  	        done = TRUE;
595  	
596  	    } else if (pcmk__str_eq(vote.op, CRM_OP_NOVOTE, pcmk__str_none)
597  	               || pcmk__str_eq(vote.from, cluster->priv->node_name,
598  	                               pcmk__str_casei)) {
599  	        /* Receiving our own broadcast vote, or a no-vote from peer, is a vote
600  	         * for us to win
601  	         */
602  	        if (!we_are_owner) {
603  	            pcmk__warn("Cannot count election round %d %s from %s because we "
604  	                       "did not start election (node ID %s did)",
605  	                       vote.election_id, vote.op, vote.from,
606  	                       vote.election_owner);
607  	            return election_error;
608  	        }
609  	        if (cluster->priv->election->state != election_in_progress) {
610  	            // Should only happen if we already lost
611  	            pcmk__debug("Not counting election round %d %s from %s because no "
612  	                        "election in progress",
613  	                        vote.election_id, vote.op, vote.from);
614  	            return cluster->priv->election->state;
615  	        }
616  	        record_vote(cluster, &vote);
617  	        reason = "Recorded";
618  	        done = TRUE;
619  	
620  	    } else {
621  	        // A peer vote requires a comparison to determine which node is better
622  	        int age_result = compare_age(vote.age);
623  	        int version_result = pcmk__compare_versions(vote.version,
624  	                                                    CRM_FEATURE_SET);
625  	
626  	        if (version_result < 0) {
627  	            reason = "Version";
628  	            we_lose = TRUE;
629  	
630  	        } else if (version_result > 0) {
631  	            reason = "Version";
632  	
633  	        } else if (age_result < 0) {
634  	            reason = "Uptime";
635  	            we_lose = TRUE;
636  	
637  	        } else if (age_result > 0) {
638  	            reason = "Uptime";
639  	
640  	        } else if (strcasecmp(cluster->priv->node_name, vote.from) > 0) {
641  	            reason = "Host name";
642  	            we_lose = TRUE;
643  	
644  	        } else {
645  	            reason = "Host name";
646  	        }
647  	    }
648  	
649  	    if (cluster->priv->election->expires < tm_now) {
650  	        cluster->priv->election->election_wins = 0;
651  	        cluster->priv->election->expires = tm_now + STORM_INTERVAL;
652  	
653  	    } else if (done == FALSE && we_lose == FALSE) {
654  	        int peers = 1 + g_hash_table_size(pcmk__peer_cache);
655  	
656  	        /* If every node has to vote down every other node, thats N*(N-1) total elections
657  	         * Allow some leeway before _really_ complaining
658  	         */
659  	        cluster->priv->election->election_wins++;
660  	        if (cluster->priv->election->election_wins > (peers * peers)) {
661  	            pcmk__warn("Election storm detected: %d wins in %d seconds",
662  	                       cluster->priv->election->election_wins, STORM_INTERVAL);
663  	            cluster->priv->election->election_wins = 0;
664  	            cluster->priv->election->expires = tm_now + STORM_INTERVAL;
665  	            if (!(cluster->priv->election->wrote_blackbox)) {
666  	                /* It's questionable whether a black box (from every node in the
667  	                 * cluster) would be truly helpful in diagnosing an election
668  	                 * storm. It's also highly doubtful a production environment
669  	                 * would get multiple election storms from distinct causes, so
670  	                 * saving one blackbox per process lifetime should be
671  	                 * sufficient. Alternatives would be to save a timestamp of the
672  	                 * last blackbox write instead of a boolean, and write a new one
673  	                 * if some amount of time has passed; or to save a storm count,
674  	                 * write a blackbox on every Nth occurrence.
675  	                 */
676  	                crm_write_blackbox(0, NULL);
677  	                cluster->priv->election->wrote_blackbox = true;
678  	            }
679  	        }
680  	    }
681  	
682  	    if (done) {
683  	        do_crm_log(log_level + 1,
684  	                   "Processed election round %u %s (current round %d) "
685  	                   "from %s (%s)",
686  	                   vote.election_id, vote.op, cluster->priv->election->count,
687  	                   vote.from, reason);
688  	        return cluster->priv->election->state;
689  	
690  	    } else if (we_lose == FALSE) {
691  	        /* We track the time of the last election loss to implement an election
692  	         * dampening period, reducing the likelihood of an election storm. If
693  	         * this node has lost within the dampening period, don't start a new
694  	         * election, even if we win against a peer's vote -- the peer we lost to
695  	         * should win again.
696  	         *
697  	         * @TODO This has a problem case: if an election winner immediately
698  	         * leaves the cluster, and a new election is immediately called, all
699  	         * nodes could lose, with no new winner elected. The ideal solution
700  	         * would be to tie the election structure with the peer caches, which
701  	         * would allow us to clear the dampening when the previous winner
702  	         * leaves (and would allow other improvements as well).
703  	         */
704  	        if ((cluster->priv->election->last_election_loss == 0)
705  	            || ((tm_now - cluster->priv->election->last_election_loss)
706  	                > (time_t) LOSS_DAMPEN)) {
707  	
708  	            do_crm_log(log_level,
709  	                       "Election round %d (started by node ID %s) pass: "
710  	                       "%s from %s (%s)",
711  	                       vote.election_id, vote.election_owner, vote.op,
712  	                       vote.from, reason);
713  	
714  	            cluster->priv->election->last_election_loss = 0;
715  	            election_timeout_stop(cluster);
716  	
717  	            /* Start a new election by voting down this, and other, peers */
718  	            cluster->priv->election->state = election_start;
719  	            return cluster->priv->election->state;
720  	        } else {
721  	            char *loss_time = NULL;
722  	
723  	            loss_time = ctime(&cluster->priv->election->last_election_loss);
724  	            if (loss_time) {
725  	                // Show only HH:MM:SS
726  	                loss_time += 11;
727  	                loss_time[8] = '\0';
728  	            }
729  	            pcmk__info("Ignoring election round %d (started by node ID %s) "
730  	                       "pass vs %s because we lost less than %ds ago at %s",
731  	                       vote.election_id, vote.election_owner, vote.from,
732  	                       LOSS_DAMPEN, pcmk__s(loss_time, "unknown"));
733  	        }
734  	    }
735  	
736  	    cluster->priv->election->last_election_loss = tm_now;
737  	
738  	    do_crm_log(log_level,
739  	               "Election round %d (started by node ID %s) lost: "
740  	               "%s from %s (%s)",
741  	               vote.election_id, vote.election_owner, vote.op,
742  	               vote.from, reason);
743  	
744  	    election_reset(cluster);
745  	    send_no_vote(cluster, your_node, &vote);
746  	    cluster->priv->election->state = election_lost;
747  	    return cluster->priv->election->state;
748  	}
749  	
750  	/*!
751  	 * \internal
752  	 * \brief Reset any election dampening currently in effect
753  	 *
754  	 * \param[in,out] cluster  Cluster with election
755  	 */
756  	void
757  	election_clear_dampening(pcmk_cluster_t *cluster)
758  	{
759  	    if ((cluster != NULL) && (cluster->priv->election != NULL)) {
760  	        cluster->priv->election->last_election_loss = 0;
761  	    }
762  	}
763