13 #include <sys/resource.h>
23 #define STORM_INTERVAL 2
36 time_t last_election_loss;
50 election_timer_cb(gpointer user_data)
54 crm_info(
"%s timed out, declaring local node as winner", e->name);
93 static guint count = 0;
99 crm_perror(LOG_CRIT,
"Cannot create election");
103 e->uname = strdup(uname);
104 if (e->uname == NULL) {
105 crm_perror(LOG_CRIT,
"Cannot create election");
114 election_timer_cb, e);
131 if ((e != NULL) && (uname != NULL) && (e->voted != NULL)) {
132 crm_trace(
"Discarding %s (no-)vote from lost peer %s", e->name, uname);
133 g_hash_table_remove(e->voted, uname);
146 crm_trace(
"Resetting election %s", e->name);
149 crm_trace(
"Destroying voted cache with %d members", g_hash_table_size(e->voted));
150 g_hash_table_destroy(e->voted);
210 crm_err(
"No election defined");
215 get_uptime(
struct timeval *output)
217 static time_t expires = 0;
218 static struct rusage info;
220 time_t tm_now = time(NULL);
222 if (expires < tm_now) {
225 info.ru_utime.tv_sec = 0;
226 info.ru_utime.tv_usec = 0;
227 rc = getrusage(RUSAGE_SELF, &info);
233 crm_perror(LOG_ERR,
"Could not calculate the current uptime");
238 crm_debug(
"Current CPU usage is: %lds, %ldus", (
long)info.ru_utime.tv_sec,
239 (
long)info.ru_utime.tv_usec);
243 output->tv_sec = info.ru_utime.tv_sec;
244 output->tv_usec = info.ru_utime.tv_usec;
250 compare_age(
struct timeval your_age)
252 struct timeval our_age;
254 get_uptime(&our_age);
256 if (our_age.tv_sec > your_age.tv_sec) {
257 crm_debug(
"Win: %ld vs %ld (seconds)", (
long)our_age.tv_sec, (
long)your_age.tv_sec);
259 }
else if (our_age.tv_sec < your_age.tv_sec) {
260 crm_debug(
"Lose: %ld vs %ld (seconds)", (
long)our_age.tv_sec, (
long)your_age.tv_sec);
262 }
else if (our_age.tv_usec > your_age.tv_usec) {
263 crm_debug(
"Win: %ld.%06ld vs %ld.%06ld (usec)",
264 (
long)our_age.tv_sec, (
long)our_age.tv_usec, (
long)your_age.tv_sec, (
long)your_age.tv_usec);
266 }
else if (our_age.tv_usec < your_age.tv_usec) {
267 crm_debug(
"Lose: %ld.%06ld vs %ld.%06ld (usec)",
268 (
long)our_age.tv_sec, (
long)our_age.tv_usec, (
long)your_age.tv_sec, (
long)your_age.tv_usec);
292 xmlNode *vote = NULL;
296 crm_trace(
"Election vote requested, but no election available");
302 crm_trace(
"Cannot vote in %s yet: local node not connected to cluster",
321 crm_debug(
"Started %s round %d", e->name, e->count);
322 election_timeout_start(e);
348 crm_trace(
"Election check requested, but no election available");
351 if (e->voted == NULL) {
352 crm_trace(
"%s check requested, but no votes received yet", e->name);
356 voted_size = g_hash_table_size(e->voted);
363 if (voted_size >= num_members) {
366 if (voted_size > num_members) {
367 GHashTableIter gIter;
371 crm_warn(
"Received too many votes in %s", e->name);
373 while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) & node)) {
379 g_hash_table_iter_init(&gIter, e->voted);
380 while (g_hash_table_iter_next(&gIter, (gpointer *) & key, NULL)) {
386 crm_info(
"%s won by local node", e->name);
387 election_complete(e);
391 crm_debug(
"%s still waiting on %d of %d votes",
392 e->name, num_members - voted_size, num_members);
398 #define LOSS_DAMPEN 2
404 const char *election_owner;
421 parse_election_message(
election_t *e, xmlNode *message,
struct vote *vote)
423 CRM_CHECK(message && vote,
return FALSE);
425 vote->election_id = -1;
426 vote->age.tv_sec = -1;
427 vote->age.tv_usec = -1;
436 if ((vote->op == NULL) || (vote->from == NULL) || (vote->version == NULL)
437 || (vote->election_owner == NULL) || (vote->election_id < 0)) {
439 crm_warn(
"Invalid %s message from %s in %s ",
440 (vote->op? vote->op :
"election"),
441 (vote->from? vote->from :
"unspecified node"),
442 (e? e->name :
"election"));
452 if ((vote->age.tv_sec < 0) || (vote->age.tv_usec < 0)) {
453 crm_warn(
"Cannot count %s %s from %s because it is missing uptime",
454 (e? e->name :
"election"), vote->op, vote->from);
459 crm_info(
"Cannot process %s message from %s because %s is not a known election op",
460 (e? e->name :
"election"), vote->from, vote->op);
467 crm_info(
"Cannot count %s from %s because no election available",
468 vote->op, vote->from);
476 crm_info(
"Cannot count %s %s from %s because no peer information available",
477 e->name, vote->op, vote->from);
486 char *voter_copy = NULL;
487 char *vote_copy = NULL;
489 CRM_ASSERT(e && vote && vote->from && vote->op);
490 if (e->voted == NULL) {
494 voter_copy = strdup(vote->from);
495 vote_copy = strdup(vote->op);
498 g_hash_table_replace(e->voted, voter_copy, vote_copy);
502 send_no_vote(
crm_node_t *peer,
struct vote *vote)
534 int log_level = LOG_INFO;
535 gboolean done = FALSE;
536 gboolean we_lose = FALSE;
537 const char *reason =
"unknown";
538 bool we_are_owner = FALSE;
539 crm_node_t *our_node = NULL, *your_node = NULL;
540 time_t tm_now = time(NULL);
544 if (parse_election_message(e, message, &vote) == FALSE) {
550 we_are_owner = (our_node != NULL)
551 && pcmk__str_eq(our_node->
uuid, vote.election_owner,
555 reason =
"Not eligible";
559 reason =
"We are not part of the cluster";
563 }
else if (we_are_owner && (vote.election_id != e->count)) {
565 reason =
"Superseded";
570 reason =
"Peer is not part of our cluster";
571 log_level = LOG_WARNING;
580 crm_warn(
"Cannot count %s round %d %s from %s because we are not election owner (%s)",
581 e->name, vote.election_id, vote.op, vote.from,
582 vote.election_owner);
587 crm_debug(
"Not counting %s round %d %s from %s because no election in progress",
588 e->name, vote.election_id, vote.op, vote.from);
591 record_vote(e, &vote);
597 int age_result = compare_age(vote.age);
600 if (version_result < 0) {
604 }
else if (version_result > 0) {
607 }
else if (age_result < 0) {
611 }
else if (age_result > 0) {
614 }
else if (strcasecmp(e->uname, vote.from) > 0) {
615 reason =
"Host name";
619 reason =
"Host name";
623 if (e->expires < tm_now) {
624 e->election_wins = 0;
627 }
else if (done == FALSE && we_lose == FALSE) {
634 if (e->election_wins > (peers * peers)) {
635 crm_warn(
"%s election storm detected: %d wins in %d seconds",
637 e->election_wins = 0;
639 if (e->wrote_blackbox == FALSE) {
651 e->wrote_blackbox = TRUE;
658 "Processed %s round %d %s (current round %d) from %s (%s)",
659 e->name, vote.election_id, vote.op, e->count, vote.from,
663 }
else if (we_lose == FALSE) {
677 if ((e->last_election_loss == 0)
678 || ((tm_now - e->last_election_loss) > (time_t)
LOSS_DAMPEN)) {
680 do_crm_log(log_level,
"%s round %d (owner node ID %s) pass: %s from %s (%s)",
681 e->name, vote.election_id, vote.election_owner, vote.op,
684 e->last_election_loss = 0;
691 char *loss_time = ctime(&e->last_election_loss);
698 crm_info(
"Ignoring %s round %d (owner node ID %s) pass vs %s because we lost less than %ds ago at %s",
699 e->name, vote.election_id, vote.election_owner, vote.from,
700 LOSS_DAMPEN, (loss_time? loss_time :
"unknown"));
704 e->last_election_loss = tm_now;
706 do_crm_log(log_level,
"%s round %d (owner node ID %s) lost: %s from %s (%s)",
707 e->name, vote.election_id, vote.election_owner, vote.op,
711 send_no_vote(your_node, &vote);
724 e->last_election_loss = 0;
#define CRM_CHECK(expr, failure_action)
void crm_write_blackbox(int nsig, struct qb_log_callsite *callsite)
void election_vote(election_t *e)
Start a new election by offering local node's candidacy.
void mainloop_timer_start(mainloop_timer_t *t)
guint mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms)
void mainloop_timer_del(mainloop_timer_t *t)
void election_reset(election_t *e)
Stop election timer and disregard all votes.
bool election_check(election_t *e)
Check whether local node has won an election.
gboolean crm_is_peer_active(const crm_node_t *node)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
void election_timeout_stop(election_t *e)
Stop an election's timer, if running.
struct mainloop_timer_s mainloop_timer_t
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
crm_node_t * crm_get_peer(unsigned int id, const char *uname)
Get a cluster node cache entry.
struct election_s election_t
guint crm_active_peers(void)
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
void mainloop_timer_stop(mainloop_timer_t *t)
#define F_CRM_ELECTION_AGE_S
Wrappers for and extensions to glib mainloop.
#define crm_warn(fmt, args...)
void election_timeout_set_period(election_t *e, guint period_ms)
Change an election's timeout (restarting timer if running)
#define crm_debug(fmt, args...)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
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.
#define crm_trace(fmt, args...)
#define do_crm_log(level, fmt, args...)
Log a message.
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Wrappers for and extensions to libxml2.
void free_xml(xmlNode *child)
const char * crm_xml_add_timeval(xmlNode *xml, const char *name_sec, const char *name_usec, const struct timeval *value)
Create XML attributes for seconds and microseconds.
Functions for conducting elections.
void election_remove(election_t *e, const char *uname)
Disregard any previous vote by specified peer.
#define F_CRM_ELECTION_AGE_US
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
#define crm_perror(level, fmt, args...)
Send a system error message to both the log and stderr.
#define crm_err(fmt, args...)
void election_clear_dampening(election_t *e)
Reset any election dampening currently in effect.
void election_fini(election_t *e)
Free an election object.
int compare_version(const char *version1, const char *version2)
mainloop_timer_t * mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata)
election_t * election_init(const char *name, const char *uname, guint period_ms, GSourceFunc cb)
Create a new election object.
#define F_CRM_ELECTION_ID
gboolean send_cluster_message(crm_node_t *node, enum crm_ais_msg_types service, xmlNode *data, gboolean ordered)
Send an XML message via the cluster messaging layer.
#define create_request(task, xml_data, host_to, sys_to, sys_from, uuid_from)
GHashTable * crm_peer_cache
#define crm_info(fmt, args...)
enum election_result election_count_vote(election_t *e, xmlNode *vote, bool can_win)
Process an election message (vote or no-vote) from a peer.
enum election_result election_state(election_t *e)
Get current state of an election.
#define F_CRM_ELECTION_OWNER