pacemaker  1.1.24-3850484742
Scalable High-Availability cluster resource manager
election.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 
10 #include <sys/time.h>
11 #include <sys/resource.h>
12 
13 #include <crm/msg_xml.h>
14 #include <crm/common/xml.h>
15 
16 #include <crm/common/mainloop.h>
17 #include <crm/cluster/internal.h>
18 #include <crm/cluster/election.h>
19 #include <crm/crm.h>
20 
21 #define STORM_INTERVAL 2 /* in seconds */
22 
23 struct election_s {
24  enum election_result state;
25  guint count; // How many times local node has voted
26  char *name; // Descriptive name for this election
27  char *uname; // Local node's name
28  GSourceFunc cb; // Function to call if election is won
29  GHashTable *voted; // Key = node name, value = how node voted
30  mainloop_timer_t *timeout; // When to abort if all votes not received
31  int election_wins; // Track wins, for storm detection
32  bool wrote_blackbox; // Write a storm blackbox at most once
33  time_t expires; // When storm detection period ends
34  time_t last_election_loss; // When dampening period ends
35 };
36 
37 static void election_complete(election_t *e)
38 {
39  e->state = election_won;
40 
41  if(e->cb) {
42  e->cb(e);
43  }
44 
45  election_reset(e);
46 }
47 
48 static gboolean election_timer_cb(gpointer user_data)
49 {
50  election_t *e = user_data;
51 
52  crm_info("%s timed out, declaring local node as winner", e->name);
53  election_complete(e);
54  return FALSE;
55 }
56 
57 enum election_result
59 {
60  if(e) {
61  return e->state;
62  }
63  return election_error;
64 }
65 
82 election_t *
83 election_init(const char *name, const char *uname, guint period_ms, GSourceFunc cb)
84 {
85  election_t *e = NULL;
86 
87  static guint count = 0;
88 
89  CRM_CHECK(uname != NULL, return NULL);
90 
91  e = calloc(1, sizeof(election_t));
92  if (e == NULL) {
93  crm_perror(LOG_CRIT, "Cannot create election");
94  return NULL;
95  }
96 
97  e->uname = strdup(uname);
98  if (e->uname == NULL) {
99  crm_perror(LOG_CRIT, "Cannot create election");
100  free(e);
101  return NULL;
102  }
103 
104  e->name = name? crm_strdup_printf("election-%s", name)
105  : crm_strdup_printf("election-%u", count++);
106  e->cb = cb;
107  e->timeout = mainloop_timer_add(e->name, period_ms, FALSE,
108  election_timer_cb, e);
109  crm_trace("Created %s", e->name);
110  return e;
111 }
112 
122 void
124 {
125  if(e && uname && e->voted) {
126  crm_trace("Discarding %s (no-)vote from lost peer %s", e->name, uname);
127  g_hash_table_remove(e->voted, uname);
128  }
129 }
130 
136 void
138 {
139  crm_trace("Resetting election %s", e->name);
140  if(e) {
141  mainloop_timer_stop(e->timeout);
142  }
143  if (e && e->voted) {
144  crm_trace("Destroying voted cache with %d members", g_hash_table_size(e->voted));
145  g_hash_table_destroy(e->voted);
146  e->voted = NULL;
147  }
148 }
149 
158 void
160 {
161  if(e) {
162  election_reset(e);
163  crm_trace("Destroying %s", e->name);
164  mainloop_timer_del(e->timeout);
165  free(e->uname);
166  free(e->name);
167  free(e);
168  }
169 }
170 
171 static void
172 election_timeout_start(election_t *e)
173 {
174  if(e) {
175  mainloop_timer_start(e->timeout);
176  }
177 }
178 
184 void
186 {
187  if(e) {
188  mainloop_timer_stop(e->timeout);
189  }
190 }
191 
198 void
200 {
201  if(e) {
202  mainloop_timer_set_period(e->timeout, period);
203  } else {
204  crm_err("No election defined");
205  }
206 }
207 
208 static int
209 crm_uptime(struct timeval *output)
210 {
211  static time_t expires = 0;
212  static struct rusage info;
213 
214  time_t tm_now = time(NULL);
215 
216  if (expires < tm_now) {
217  int rc = 0;
218 
219  info.ru_utime.tv_sec = 0;
220  info.ru_utime.tv_usec = 0;
221  rc = getrusage(RUSAGE_SELF, &info);
222 
223  output->tv_sec = 0;
224  output->tv_usec = 0;
225 
226  if (rc < 0) {
227  crm_perror(LOG_ERR, "Could not calculate the current uptime");
228  expires = 0;
229  return -1;
230  }
231 
232  crm_debug("Current CPU usage is: %lds, %ldus", (long)info.ru_utime.tv_sec,
233  (long)info.ru_utime.tv_usec);
234  }
235 
236  expires = tm_now + STORM_INTERVAL; /* N seconds after the last _access_ */
237  output->tv_sec = info.ru_utime.tv_sec;
238  output->tv_usec = info.ru_utime.tv_usec;
239 
240  return 1;
241 }
242 
243 static int
244 crm_compare_age(struct timeval your_age)
245 {
246  struct timeval our_age;
247 
248  crm_uptime(&our_age); /* If an error occurred, our_age will be compared as {0,0} */
249 
250  if (our_age.tv_sec > your_age.tv_sec) {
251  crm_debug("Win: %ld vs %ld (seconds)", (long)our_age.tv_sec, (long)your_age.tv_sec);
252  return 1;
253  } else if (our_age.tv_sec < your_age.tv_sec) {
254  crm_debug("Lose: %ld vs %ld (seconds)", (long)our_age.tv_sec, (long)your_age.tv_sec);
255  return -1;
256  } else if (our_age.tv_usec > your_age.tv_usec) {
257  crm_debug("Win: %ld.%06ld vs %ld.%06ld (usec)",
258  (long)our_age.tv_sec, (long)our_age.tv_usec, (long)your_age.tv_sec, (long)your_age.tv_usec);
259  return 1;
260  } else if (our_age.tv_usec < your_age.tv_usec) {
261  crm_debug("Lose: %ld.%06ld vs %ld.%06ld (usec)",
262  (long)our_age.tv_sec, (long)our_age.tv_usec, (long)your_age.tv_sec, (long)your_age.tv_usec);
263  return -1;
264  }
265 
266  return 0;
267 }
268 
282 void
284 {
285  struct timeval age;
286  xmlNode *vote = NULL;
287  crm_node_t *our_node;
288 
289  if (e == NULL) {
290  crm_trace("Election vote requested, but no election available");
291  return;
292  }
293 
294  our_node = crm_get_peer(0, e->uname);
295  if ((our_node == NULL) || (crm_is_peer_active(our_node) == FALSE)) {
296  crm_trace("Cannot vote in %s yet: local node not connected to cluster",
297  e->name);
298  return;
299  }
300 
301  election_reset(e);
302  e->state = election_in_progress;
303  vote = create_request(CRM_OP_VOTE, NULL, NULL, CRM_SYSTEM_CRMD, CRM_SYSTEM_CRMD, NULL);
304 
305  e->count++;
306  crm_xml_add(vote, F_CRM_ELECTION_OWNER, our_node->uuid);
307  crm_xml_add_int(vote, F_CRM_ELECTION_ID, e->count);
308 
309  crm_uptime(&age);
310  crm_xml_add_int(vote, F_CRM_ELECTION_AGE_S, age.tv_sec);
311  crm_xml_add_int(vote, F_CRM_ELECTION_AGE_US, age.tv_usec);
312 
313  send_cluster_message(NULL, crm_msg_crmd, vote, TRUE);
314  free_xml(vote);
315 
316  crm_debug("Started %s round %d", e->name, e->count);
317  election_timeout_start(e);
318  return;
319 }
320 
336 bool
338 {
339  int voted_size = 0;
340  int num_members = 0;
341 
342  if(e == NULL) {
343  crm_trace("Election check requested, but no election available");
344  return FALSE;
345  }
346  if (e->voted == NULL) {
347  crm_trace("%s check requested, but no votes received yet", e->name);
348  return FALSE;
349  }
350 
351  voted_size = g_hash_table_size(e->voted);
352  num_members = crm_active_peers();
353 
354  /* in the case of #voted > #members, it is better to
355  * wait for the timeout and give the cluster time to
356  * stabilize
357  */
358  if (voted_size >= num_members) {
359  /* we won and everyone has voted */
361  if (voted_size > num_members) {
362  GHashTableIter gIter;
363  const crm_node_t *node;
364  char *key = NULL;
365 
366  crm_warn("Received too many votes in %s", e->name);
367  g_hash_table_iter_init(&gIter, crm_peer_cache);
368  while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) & node)) {
369  if (crm_is_peer_active(node)) {
370  crm_warn("* expected vote: %s", node->uname);
371  }
372  }
373 
374  g_hash_table_iter_init(&gIter, e->voted);
375  while (g_hash_table_iter_next(&gIter, (gpointer *) & key, NULL)) {
376  crm_warn("* actual vote: %s", key);
377  }
378 
379  }
380 
381  crm_info("%s won by local node", e->name);
382  election_complete(e);
383  return TRUE;
384 
385  } else {
386  crm_debug("%s still waiting on %d of %d votes",
387  e->name, num_members - voted_size, num_members);
388  }
389 
390  return FALSE;
391 }
392 
393 #define loss_dampen 2 /* in seconds */
394 
395 struct vote {
396  const char *op;
397  const char *from;
398  const char *version;
399  const char *election_owner;
400  int election_id;
401  struct timeval age;
402 };
403 
415 static bool
416 parse_election_message(election_t *e, xmlNode *message, struct vote *vote)
417 {
418  CRM_CHECK(message && vote, return FALSE);
419 
420  vote->election_id = -1;
421  vote->age.tv_sec = -1;
422  vote->age.tv_usec = -1;
423 
424  vote->op = crm_element_value(message, F_CRM_TASK);
425  vote->from = crm_element_value(message, F_CRM_HOST_FROM);
426  vote->version = crm_element_value(message, F_CRM_VERSION);
427  vote->election_owner = crm_element_value(message, F_CRM_ELECTION_OWNER);
428 
429  crm_element_value_int(message, F_CRM_ELECTION_ID, &(vote->election_id));
430 
431  if ((vote->op == NULL) || (vote->from == NULL) || (vote->version == NULL)
432  || (vote->election_owner == NULL) || (vote->election_id < 0)) {
433 
434  crm_warn("Invalid %s message from %s in %s ",
435  (vote->op? vote->op : "election"),
436  (vote->from? vote->from : "unspecified node"),
437  (e? e->name : "election"));
438  return FALSE;
439  }
440 
441  // Op-specific validation
442 
443  if (crm_str_eq(vote->op, CRM_OP_VOTE, TRUE)) {
444  // Only vote ops have uptime
446  F_CRM_ELECTION_AGE_US, &(vote->age));
447  if ((vote->age.tv_sec < 0) || (vote->age.tv_usec < 0)) {
448  crm_warn("Cannot count %s %s from %s because it is missing uptime",
449  (e? e->name : "election"), vote->op, vote->from);
450  return FALSE;
451  }
452 
453  } else if (!crm_str_eq(vote->op, CRM_OP_NOVOTE, TRUE)) {
454  crm_info("Cannot process %s message from %s because %s is not a known election op",
455  (e? e->name : "election"), vote->from, vote->op);
456  return FALSE;
457  }
458 
459  // Election validation
460 
461  if (e == NULL) {
462  crm_info("Cannot count %s from %s because no election available",
463  vote->op, vote->from);
464  return FALSE;
465  }
466 
467  /* If the membership cache is NULL, we REALLY shouldn't be voting --
468  * the question is how we managed to get here.
469  */
470  if (crm_peer_cache == NULL) {
471  crm_info("Cannot count %s %s from %s because no peer information available",
472  e->name, vote->op, vote->from);
473  return FALSE;
474  }
475  return TRUE;
476 }
477 
478 static void
479 record_vote(election_t *e, struct vote *vote)
480 {
481  char *voter_copy = NULL;
482  char *vote_copy = NULL;
483 
484  CRM_ASSERT(e && vote && vote->from && vote->op);
485  if (e->voted == NULL) {
486  e->voted = crm_str_table_new();
487  }
488 
489  voter_copy = strdup(vote->from);
490  vote_copy = strdup(vote->op);
491  CRM_ASSERT(voter_copy && vote_copy);
492 
493  g_hash_table_replace(e->voted, voter_copy, vote_copy);
494 }
495 
496 static void
497 send_no_vote(crm_node_t *peer, struct vote *vote)
498 {
499  // @TODO probably shouldn't hardcode CRM_SYSTEM_CRMD and crm_msg_crmd
500 
501  xmlNode *novote = create_request(CRM_OP_NOVOTE, NULL, vote->from,
503 
504  crm_xml_add(novote, F_CRM_ELECTION_OWNER, vote->election_owner);
505  crm_xml_add_int(novote, F_CRM_ELECTION_ID, vote->election_id);
506 
507  send_cluster_message(peer, crm_msg_crmd, novote, TRUE);
508  free_xml(novote);
509 }
510 
526 enum election_result
527 election_count_vote(election_t *e, xmlNode *message, bool can_win)
528 {
529  int log_level = LOG_INFO;
530  gboolean use_born_on = FALSE;
531  gboolean done = FALSE;
532  gboolean we_lose = FALSE;
533  const char *reason = "unknown";
534  bool we_are_owner = FALSE;
535  crm_node_t *our_node = NULL, *your_node = NULL;
536  time_t tm_now = time(NULL);
537  struct vote vote;
538 
539  CRM_CHECK(message != NULL, return election_error);
540  if (parse_election_message(e, message, &vote) == FALSE) {
541  return election_error;
542  }
543 
544  your_node = crm_get_peer(0, vote.from);
545  our_node = crm_get_peer(0, e->uname);
546  we_are_owner = (our_node != NULL)
547  && crm_str_eq(our_node->uuid, vote.election_owner, TRUE);
548 
549  if (is_heartbeat_cluster()) {
550  use_born_on = TRUE;
551  } else if (is_classic_ais_cluster()) {
552  use_born_on = TRUE;
553  }
554 
555  if(can_win == FALSE) {
556  reason = "Not eligible";
557  we_lose = TRUE;
558 
559  } else if (our_node == NULL || crm_is_peer_active(our_node) == FALSE) {
560  reason = "We are not part of the cluster";
561  log_level = LOG_ERR;
562  we_lose = TRUE;
563 
564  } else if (we_are_owner && (vote.election_id != e->count)) {
565  log_level = LOG_TRACE;
566  reason = "Superseded";
567  done = TRUE;
568 
569  } else if (your_node == NULL || crm_is_peer_active(your_node) == FALSE) {
570  /* Possibly we cached the message in the FSA queue at a point that it wasn't */
571  reason = "Peer is not part of our cluster";
572  log_level = LOG_WARNING;
573  done = TRUE;
574 
575  } else if (crm_str_eq(vote.op, CRM_OP_NOVOTE, TRUE)
576  || crm_str_eq(vote.from, e->uname, TRUE)) {
577  /* Receiving our own broadcast vote, or a no-vote from peer, is a vote
578  * for us to win
579  */
580  if (!we_are_owner) {
581  crm_warn("Cannot count %s round %d %s from %s because we are not election owner (%s)",
582  e->name, vote.election_id, vote.op, vote.from,
583  vote.election_owner);
584  return election_error;
585  }
586  if (e->state != election_in_progress) {
587  // Should only happen if we already lost
588  crm_debug("Not counting %s round %d %s from %s because no election in progress",
589  e->name, vote.election_id, vote.op, vote.from);
590  return e->state;
591  }
592  record_vote(e, &vote);
593  reason = "Recorded";
594  done = TRUE;
595 
596  } else {
597  // A peer vote requires a comparison to determine which node is better
598  int age_result = crm_compare_age(vote.age);
599  int version_result = compare_version(vote.version, CRM_FEATURE_SET);
600 
601  if (version_result < 0) {
602  reason = "Version";
603  we_lose = TRUE;
604 
605  } else if (version_result > 0) {
606  reason = "Version";
607 
608  } else if (age_result < 0) {
609  reason = "Uptime";
610  we_lose = TRUE;
611 
612  } else if (age_result > 0) {
613  reason = "Uptime";
614 
615  /* TODO: Check for y(our) born < 0 */
616  } else if (use_born_on && your_node->born < our_node->born) {
617  reason = "Born";
618  we_lose = TRUE;
619 
620  } else if (use_born_on && your_node->born > our_node->born) {
621  reason = "Born";
622 
623  } else if (strcasecmp(e->uname, vote.from) > 0) {
624  reason = "Host name";
625  we_lose = TRUE;
626 
627  } else {
628  reason = "Host name";
629  }
630  }
631 
632  if (e->expires < tm_now) {
633  e->election_wins = 0;
634  e->expires = tm_now + STORM_INTERVAL;
635 
636  } else if (done == FALSE && we_lose == FALSE) {
637  int peers = 1 + g_hash_table_size(crm_peer_cache);
638 
639  /* If every node has to vote down every other node, thats N*(N-1) total elections
640  * Allow some leeway before _really_ complaining
641  */
642  e->election_wins++;
643  if (e->election_wins > (peers * peers)) {
644  crm_warn("%s election storm detected: %d wins in %d seconds",
645  e->name, e->election_wins, STORM_INTERVAL);
646  e->election_wins = 0;
647  e->expires = tm_now + STORM_INTERVAL;
648  if (e->wrote_blackbox == FALSE) {
649  /* It's questionable whether a black box (from every node in the
650  * cluster) would be truly helpful in diagnosing an election
651  * storm. It's also highly doubtful a production environment
652  * would get multiple election storms from distinct causes, so
653  * saving one blackbox per process lifetime should be
654  * sufficient. Alternatives would be to save a timestamp of the
655  * last blackbox write instead of a boolean, and write a new one
656  * if some amount of time has passed; or to save a storm count,
657  * write a blackbox on every Nth occurrence.
658  */
659  crm_write_blackbox(0, NULL);
660  e->wrote_blackbox = TRUE;
661  }
662  }
663  }
664 
665  if (done) {
666  do_crm_log(log_level + 1,
667  "Processed %s round %d %s (current round %d) from %s (%s)",
668  e->name, vote.election_id, vote.op, e->count, vote.from,
669  reason);
670  return e->state;
671 
672  } else if (we_lose == FALSE) {
673  /* We track the time of the last election loss to implement an election
674  * dampening period, reducing the likelihood of an election storm. If
675  * this node has lost within the dampening period, don't start a new
676  * election, even if we win against a peer's vote -- the peer we lost to
677  * should win again.
678  *
679  * @TODO This has a problem case: if an election winner immediately
680  * leaves the cluster, and a new election is immediately called, all
681  * nodes could lose, with no new winner elected. The ideal solution
682  * would be to tie the election structure with the peer caches, which
683  * would allow us to clear the dampening when the previous winner
684  * leaves (and would allow other improvements as well).
685  */
686  if ((e->last_election_loss == 0)
687  || ((tm_now - e->last_election_loss) > (time_t) loss_dampen)) {
688 
689  do_crm_log(log_level, "%s round %d (owner node ID %s) pass: %s from %s (%s)",
690  e->name, vote.election_id, vote.election_owner, vote.op,
691  vote.from, reason);
692 
693  e->last_election_loss = 0;
695 
696  /* Start a new election by voting down this, and other, peers */
697  e->state = election_start;
698  return e->state;
699  } else {
700  char *loss_time = ctime(&e->last_election_loss);
701 
702  if (loss_time) {
703  // Show only HH:MM:SS
704  loss_time += 11;
705  loss_time[8] = '\0';
706  }
707  crm_info("Ignoring %s round %d (owner node ID %s) pass vs %s because we lost less than %ds ago at %s",
708  e->name, vote.election_id, vote.election_owner, vote.from,
709  loss_dampen, (loss_time? loss_time : "unknown"));
710  }
711  }
712 
713  e->last_election_loss = tm_now;
714 
715  do_crm_log(log_level, "%s round %d (owner node ID %s) lost: %s from %s (%s)",
716  e->name, vote.election_id, vote.election_owner, vote.op,
717  vote.from, reason);
718 
719  election_reset(e);
720  send_no_vote(your_node, &vote);
721  e->state = election_lost;
722  return e->state;
723 }
724 
730 void
732 {
733  e->last_election_loss = 0;
734 }
#define F_CRM_TASK
Definition: msg_xml.h:56
#define LOG_TRACE
Definition: logging.h:29
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:190
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
Definition: logging.c:435
A dumping ground.
void mainloop_timer_start(mainloop_timer_t *t)
Definition: mainloop.c:1262
guint mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms)
Definition: mainloop.c:1280
void mainloop_timer_del(mainloop_timer_t *t)
Definition: mainloop.c:1318
gboolean is_heartbeat_cluster(void)
Definition: cluster.c:645
gboolean crm_is_peer_active(const crm_node_t *node)
Definition: membership.c:297
uint64_t born
Definition: cluster.h:74
char * uuid
Definition: cluster.h:83
#define STORM_INTERVAL
Definition: election.c:21
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
Definition: nvpair.c:324
#define CRM_FEATURE_SET
Definition: crm.h:26
#define F_CRM_HOST_FROM
Definition: msg_xml.h:61
struct mainloop_timer_s mainloop_timer_t
Definition: mainloop.h:27
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:216
crm_node_t * crm_get_peer(unsigned int id, const char *uname)
Definition: membership.c:689
void election_timeout_stop(election_t *e)
Stop an election&#39;s timer, if running.
Definition: election.c:185
#define CRM_OP_NOVOTE
Definition: crm.h:112
guint crm_active_peers(void)
Definition: membership.c:395
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
Definition: nvpair.c:428
void mainloop_timer_stop(mainloop_timer_t *t)
Definition: mainloop.c:1271
#define F_CRM_ELECTION_AGE_S
Definition: msg_xml.h:69
Wrappers for and extensions to glib mainloop.
char version[256]
Definition: plugin.c:84
void election_clear_dampening(election_t *e)
Reset any election dampening currently in effect.
Definition: election.c:731
struct election_s election_t
Definition: election.h:52
char uname[MAX_NAME]
Definition: internal.h:81
#define crm_warn(fmt, args...)
Definition: logging.h:275
#define crm_debug(fmt, args...)
Definition: logging.h:279
election_result
Definition: election.h:55
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:393
int crm_element_value_timeval(const xmlNode *data, const char *name_sec, const char *name_usec, struct timeval *dest)
Retrieve the value of XML second/microsecond attributes as time.
Definition: nvpair.c:522
void election_vote(election_t *e)
Start a new election by offering local node&#39;s candidacy.
Definition: election.c:283
#define crm_trace(fmt, args...)
Definition: logging.h:280
#define do_crm_log(level, fmt, args...)
Log a message.
Definition: logging.h:129
enum election_result election_count_vote(election_t *e, xmlNode *message, bool can_win)
Process an election message (vote or no-vote) from a peer.
Definition: election.c:527
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
Definition: xml.c:2108
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:245
void election_timeout_set_period(election_t *e, guint period)
Change an election&#39;s timeout (restarting timer if running)
Definition: election.c:199
election_t * election_init(const char *name, const char *uname, guint period_ms, GSourceFunc cb)
Create a new election object.
Definition: election.c:83
void election_fini(election_t *e)
Free an election object.
Definition: election.c:159
#define CRM_SYSTEM_CRMD
Definition: crm.h:80
#define CRM_OP_VOTE
Definition: crm.h:111
#define F_CRM_ELECTION_AGE_US
Definition: msg_xml.h:70
#define loss_dampen
Definition: election.c:393
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:252
void election_reset(election_t *e)
Stop election timer and disregard all votes.
Definition: election.c:137
#define crm_err(fmt, args...)
Definition: logging.h:274
int compare_version(const char *version1, const char *version2)
Definition: utils.c:477
#define CRM_ASSERT(expr)
Definition: error.h:20
mainloop_timer_t * mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata)
Definition: mainloop.c:1297
bool election_check(election_t *e)
Check whether local node has won an election.
Definition: election.c:337
#define F_CRM_ELECTION_ID
Definition: msg_xml.h:68
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
gboolean send_cluster_message(crm_node_t *node, enum crm_ais_msg_types service, xmlNode *data, gboolean ordered)
Definition: cluster.c:271
#define create_request(task, xml_data, host_to, sys_to, sys_from, uuid_from)
Definition: ipc.h:25
GHashTable * crm_peer_cache
Definition: membership.c:44
#define crm_info(fmt, args...)
Definition: logging.h:277
#define F_CRM_VERSION
Definition: msg_xml.h:63
void election_remove(election_t *e, const char *uname)
Disregard any previous vote by specified peer.
Definition: election.c:123
enum election_result election_state(election_t *e)
Definition: election.c:58
gboolean is_classic_ais_cluster(void)
Definition: cluster.c:624
Functions for conducting elections.
#define F_CRM_ELECTION_OWNER
Definition: msg_xml.h:71