13 #include <sys/resource.h> 23 #define STORM_INTERVAL 2 25 struct pcmk__election {
34 time_t last_election_loss;
49 election_timer_cb(gpointer user_data)
53 crm_info(
"Declaring local node as winner after election timed out");
54 election_complete(cluster);
69 if ((cluster == NULL) || (cluster->
priv->
election == NULL)) {
79 #define ELECTION_TIMEOUT_MS 120000 120 if ((cluster != NULL) && (cluster->
priv->
election != NULL)
136 if ((cluster != NULL) && (cluster->
priv->
election != NULL)) {
158 if ((cluster != NULL) && (cluster->
priv->
election != NULL)) {
182 if ((cluster != NULL) && (cluster->
priv->
election != NULL)) {
202 get_uptime(
struct timeval *output)
204 static time_t expires = 0;
205 static struct rusage info;
207 time_t tm_now = time(NULL);
209 if (expires < tm_now) {
212 info.ru_utime.tv_sec = 0;
213 info.ru_utime.tv_usec = 0;
214 rc = getrusage(RUSAGE_SELF, &info);
220 crm_perror(LOG_ERR,
"Could not calculate the current uptime");
225 crm_debug(
"Current CPU usage is: %lds, %ldus", (
long)info.ru_utime.tv_sec,
226 (
long)info.ru_utime.tv_usec);
230 output->tv_sec = info.ru_utime.tv_sec;
231 output->tv_usec = info.ru_utime.tv_usec;
237 compare_age(
struct timeval your_age)
239 struct timeval our_age;
241 get_uptime(&our_age);
243 if (our_age.tv_sec > your_age.tv_sec) {
244 crm_debug(
"Win: %ld vs %ld (seconds)", (
long)our_age.tv_sec, (
long)your_age.tv_sec);
246 }
else if (our_age.tv_sec < your_age.tv_sec) {
247 crm_debug(
"Lose: %ld vs %ld (seconds)", (
long)our_age.tv_sec, (
long)your_age.tv_sec);
249 }
else if (our_age.tv_usec > your_age.tv_usec) {
250 crm_debug(
"Win: %ld.%06ld vs %ld.%06ld (usec)",
251 (
long)our_age.tv_sec, (
long)our_age.tv_usec, (
long)your_age.tv_sec, (
long)your_age.tv_usec);
253 }
else if (our_age.tv_usec < your_age.tv_usec) {
254 crm_debug(
"Lose: %ld.%06ld vs %ld.%06ld (usec)",
255 (
long)our_age.tv_sec, (
long)our_age.tv_usec, (
long)your_age.tv_sec, (
long)your_age.tv_usec);
281 xmlNode *vote = NULL;
283 const char *message_type = NULL;
288 crm_err(
"Cannot start an election: Local node name unknown");
295 crm_trace(
"Cannot vote yet: local node not connected to cluster");
322 election_timeout_start(cluster);
352 crm_trace(
"Election check requested, but no votes received yet");
356 voted_size = g_hash_table_size(cluster->
priv->
election->voted);
363 if (voted_size >= num_members) {
366 if (voted_size > num_members) {
367 GHashTableIter gIter;
371 crm_warn(
"Received too many votes in election");
373 while (g_hash_table_iter_next(&gIter, NULL, (gpointer *) & node)) {
375 crm_warn(
"* expected vote: %s", node->name);
379 g_hash_table_iter_init(&gIter, cluster->
priv->
election->voted);
380 while (g_hash_table_iter_next(&gIter, (gpointer *) & key, NULL)) {
386 crm_info(
"Election won by local node");
387 election_complete(cluster);
391 crm_debug(
"Election still waiting on %d of %d vote%s",
392 num_members - voted_size, num_members,
399 #define LOSS_DAMPEN 2 405 const char *election_owner;
422 parse_election_message(
const xmlNode *message,
struct vote *vote)
424 CRM_CHECK(message && vote,
return FALSE);
426 vote->election_id = -1;
427 vote->age.tv_sec = -1;
428 vote->age.tv_usec = -1;
437 if ((vote->op == NULL) || (vote->from == NULL) || (vote->version == NULL)
438 || (vote->election_owner == NULL) || (vote->election_id < 0)) {
440 crm_warn(
"Invalid %s message from %s",
441 pcmk__s(vote->op,
"election"),
442 pcmk__s(vote->from,
"unspecified node"));
455 if ((vote->age.tv_sec < 0) || (vote->age.tv_usec < 0)) {
456 crm_warn(
"Cannot count election %s from %s " 457 "because it is missing uptime", vote->op, vote->from);
462 crm_info(
"Cannot process election message from %s " 463 "because %s is not a known election op", vote->from, vote->op);
471 crm_info(
"Cannot count election %s from %s " 472 "because no peer information available", vote->op, vote->from);
481 pcmk__assert((vote->from != NULL) && (vote->op != NULL));
493 const char *message_type = NULL;
494 xmlNode *novote = NULL;
526 int log_level = LOG_INFO;
527 gboolean done = FALSE;
528 gboolean we_lose = FALSE;
529 const char *reason =
"unknown";
530 bool we_are_owner = FALSE;
533 time_t tm_now = time(NULL);
540 if (!parse_election_message(message, &vote)) {
548 we_are_owner = (our_node != NULL)
549 && pcmk__str_eq(our_node->
xml_id, vote.election_owner,
553 reason =
"Not eligible";
557 reason =
"We are not part of the cluster";
561 }
else if (we_are_owner
562 && (vote.election_id != cluster->
priv->
election->count)) {
564 reason =
"Superseded";
569 reason =
"Peer is not part of our cluster";
570 log_level = LOG_WARNING;
580 crm_warn(
"Cannot count election round %d %s from %s " 581 "because we did not start election (node ID %s did)",
582 vote.election_id, vote.op, vote.from,
583 vote.election_owner);
588 crm_debug(
"Not counting election round %d %s from %s " 589 "because no election in progress",
590 vote.election_id, vote.op, vote.from);
593 record_vote(cluster, &vote);
599 int age_result = compare_age(vote.age);
602 if (version_result < 0) {
606 }
else if (version_result > 0) {
609 }
else if (age_result < 0) {
613 }
else if (age_result > 0) {
616 }
else if (strcasecmp(cluster->
priv->
node_name, vote.from) > 0) {
617 reason =
"Host name";
621 reason =
"Host name";
629 }
else if (done == FALSE && we_lose == FALSE) {
636 if (cluster->
priv->
election->election_wins > (peers * peers)) {
637 crm_warn(
"Election storm detected: %d wins in %d seconds",
660 "Processed election round %u %s (current round %d) " 662 vote.election_id, vote.op, cluster->
priv->
election->count,
666 }
else if (we_lose == FALSE) {
681 || ((tm_now - cluster->
priv->
election->last_election_loss)
685 "Election round %d (started by node ID %s) pass: " 687 vote.election_id, vote.election_owner, vote.op,
697 char *loss_time = NULL;
699 loss_time = ctime(&(cluster->
priv->
election->last_election_loss));
705 crm_info(
"Ignoring election round %d (started by node ID %s) pass " 706 "vs %s because we lost less than %ds ago at %s",
707 vote.election_id, vote.election_owner, vote.from,
715 "Election round %d (started by node ID %s) lost: " 717 vote.election_id, vote.election_owner, vote.op,
721 send_no_vote(cluster, your_node, &vote);
735 if ((cluster != NULL) && (cluster->
priv->
election != NULL)) {
#define CRM_CHECK(expr, failure_action)
pcmk__node_status_t * pcmk__get_node(unsigned int id, const char *uname, const char *xml_id, uint32_t flags)
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.
bool election_check(pcmk_cluster_t *cluster)
void mainloop_timer_start(mainloop_timer_t *t)
guint mainloop_timer_set_period(mainloop_timer_t *t, guint period_ms)
#define PCMK__XA_ELECTION_AGE_NANO_SEC
void election_timeout_set_period(pcmk_cluster_t *cluster, guint period)
void mainloop_timer_del(mainloop_timer_t *t)
enum election_result election_count_vote(pcmk_cluster_t *cluster, const xmlNode *message, bool can_win)
void election_timeout_stop(pcmk_cluster_t *cluster)
Search for cluster nodes from membership cache.
struct mainloop_timer_s mainloop_timer_t
void election_remove(pcmk_cluster_t *cluster, const char *uname)
const char * pcmk__server_message_type(enum pcmk_ipc_server server)
char * node_name
Local node name at cluster layer.
void mainloop_timer_stop(mainloop_timer_t *t)
#define ELECTION_TIMEOUT_MS
enum pcmk_ipc_server server
Server this connection is for (if any)
Wrappers for and extensions to glib mainloop.
pcmk__election_t * election
Election state (if election is needed)
void election_fini(pcmk_cluster_t *cluster)
void pcmk__xml_free(xmlNode *xml)
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
#define PCMK__XA_CRM_TASK
unsigned int pcmk__cluster_num_active_nodes(void)
bool pcmk__cluster_is_node_active(const pcmk__node_status_t *node)
#define crm_warn(fmt, args...)
#define crm_debug(fmt, args...)
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.
void election_init(pcmk_cluster_t *cluster, void(*cb)(pcmk_cluster_t *))
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
#define crm_trace(fmt, args...)
#define do_crm_log(level, fmt, args...)
Log a message.
#define PCMK__XA_ELECTION_OWNER
Wrappers for and extensions to libxml2.
#define pcmk__new_request(server, sender_system, recipient_node, recipient_system, task, data)
#define PCMK__XA_ELECTION_AGE_SEC
#define PCMK__XA_ELECTION_ID
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite)
Functions for conducting elections.
#define pcmk__assert(expr)
enum election_result election_state(const pcmk_cluster_t *cluster)
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...)
GHashTable * pcmk__peer_cache
void election_vote(pcmk_cluster_t *cluster)
int crm_element_value_int(const xmlNode *data, const char *name, int *dest)
Retrieve the integer value of an XML attribute.
int compare_version(const char *version1, const char *version2)
#define pcmk__plural_s(i)
struct pcmk__election pcmk__election_t
mainloop_timer_t * mainloop_timer_add(const char *name, guint period_ms, bool repeat, GSourceFunc cb, void *userdata)
#define crm_log_xml_trace(xml, text)
bool pcmk__cluster_send_message(const pcmk__node_status_t *node, enum pcmk_ipc_server service, const xmlNode *data)
void election_reset(pcmk_cluster_t *cluster)
#define pcmk__assert_alloc(nmemb, size)
#define crm_info(fmt, args...)
pcmk__cluster_private_t * priv
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Node status data (may be a cluster node or a Pacemaker Remote node)
void election_clear_dampening(pcmk_cluster_t *cluster)
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.