1 /*
2 * Copyright 2013-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 General Public License version 2
7 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8 */
9
10 #include <crm_internal.h>
11
12 #include <stdbool.h>
13
14 #include <crm/cluster.h>
15 #include <crm/cluster/election_internal.h>
16 #include <crm/common/xml.h>
17
18 #include "pacemaker-attrd.h"
19
20 static char *peer_writer = NULL;
21
22 static void
23 attrd_election_cb(pcmk_cluster_t *cluster)
24 {
25 attrd_declare_winner();
26
27 /* Update the peers after an election */
28 attrd_peer_sync(NULL);
29
30 attrd_erase_removed_peer_attributes();
31
32 /* After winning an election, update the CIB with the values of all
33 * attributes as the winner knows them.
34 */
35 attrd_write_attributes(attrd_write_all);
36 }
37
38 void
39 attrd_election_init(void)
40 {
41 election_init(attrd_cluster, attrd_election_cb);
42 }
43
44 void
45 attrd_start_election_if_needed(void)
46 {
47 if ((peer_writer == NULL)
48 && (election_state(attrd_cluster) != election_in_progress)
49 && !attrd_shutting_down()) {
50
51 pcmk__info("Starting an election to determine the writer");
52 election_vote(attrd_cluster);
53 }
54 }
55
56 bool
57 attrd_election_won(void)
58 {
59 return (election_state(attrd_cluster) == election_won);
60 }
61
62 void
63 attrd_handle_election_op(const pcmk__node_status_t *peer, xmlNode *xml)
64 {
65 enum election_result rc = 0;
66 enum election_result previous = election_state(attrd_cluster);
67
68 pcmk__xe_set(xml, PCMK__XA_SRC, peer->name);
69
70 // Don't become writer if we're shutting down
|
(1) Event path: |
Condition "!attrd_shutting_down()", taking true branch. |
71 rc = election_count_vote(attrd_cluster, xml, !attrd_shutting_down());
72
|
(2) Event path: |
Switch case value "election_start". |
73 switch(rc) {
74 case election_start:
|
(3) Event path: |
Switch case default. |
|
(4) Event path: |
Condition "trace_cs == NULL", taking true branch. |
|
(5) Event path: |
Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch. |
|
(6) Event path: |
Breaking from switch. |
75 pcmk__debug("Unsetting writer (was %s) and starting new election",
76 pcmk__s(peer_writer, "unset"));
|
CID (unavailable; MK=1f25647876d8290c74cd4802ea8b5575) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(7) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(8) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
77 g_clear_pointer(&peer_writer, free);
78 election_vote(attrd_cluster);
79 break;
80
81 case election_lost:
82 /* The election API should really distinguish between "we just lost
83 * to this peer" and "we already lost previously, and we are
84 * discarding this vote for some reason", but it doesn't.
85 *
86 * In the first case, we want to tentatively set the peer writer to
87 * this peer, even though another peer may eventually win (which we
88 * will learn via attrd_check_for_new_writer()), so
89 * attrd_start_election_if_needed() doesn't start a new election.
90 *
91 * Approximate a test for that case as best as possible.
92 */
93 if ((peer_writer == NULL) || (previous != election_lost)) {
94 pcmk__str_update(&peer_writer, peer->name);
95 pcmk__debug("Election lost, presuming %s is writer for now",
96 peer_writer);
97 }
98 break;
99
100 case election_in_progress:
101 election_check(attrd_cluster);
102 break;
103
104 default:
105 pcmk__info("Ignoring election op from %s due to error", peer->name);
106 break;
107 }
108 }
109
110 bool
111 attrd_check_for_new_writer(const pcmk__node_status_t *peer, const xmlNode *xml)
112 {
113 int peer_state = 0;
114
115 pcmk__xe_get_int(xml, PCMK__XA_ATTR_WRITER, &peer_state);
116 if (peer_state == election_won) {
117 if ((election_state(attrd_cluster) == election_won)
118 && !pcmk__str_eq(peer->name, attrd_cluster->priv->node_name,
119 pcmk__str_casei)) {
120 pcmk__notice("Detected another attribute writer (%s), starting new "
121 "election",
122 peer->name);
123 election_vote(attrd_cluster);
124
125 } else if (!pcmk__str_eq(peer->name, peer_writer, pcmk__str_casei)) {
126 pcmk__notice("Recorded new attribute writer: %s (was %s)",
127 peer->name, pcmk__s(peer_writer, "unset"));
128 pcmk__str_update(&peer_writer, peer->name);
129 }
130 }
131 return (peer_state == election_won);
132 }
133
134 void
135 attrd_declare_winner(void)
136 {
137 pcmk__notice("Recorded local node as attribute writer (was %s)",
138 pcmk__s(peer_writer, "unset"));
139 pcmk__str_update(&peer_writer, attrd_cluster->priv->node_name);
140 }
141
142 void
143 attrd_remove_voter(const pcmk__node_status_t *peer)
144 {
145 election_remove(attrd_cluster, peer->name);
146 if ((peer_writer != NULL)
147 && pcmk__str_eq(peer->name, peer_writer, pcmk__str_casei)) {
148
149 g_clear_pointer(&peer_writer, free);
150 pcmk__notice("Lost attribute writer %s", peer->name);
151
152 /* Clear any election dampening in effect. Otherwise, if the lost writer
153 * had just won, the election could fizzle out with no new writer.
154 */
155 election_clear_dampening(attrd_cluster);
156
157 /* If the writer received attribute updates during its shutdown, it will
158 * not have written them to the CIB. Ensure we get a new writer so they
159 * are written out. This means that every node that sees the writer
160 * leave will start a new election, but that's better than losing
161 * attributes.
162 */
163 attrd_start_election_if_needed();
164
165 /* If an election is in progress, we need to call election_check(), in case
166 * this lost peer is the only one that hasn't voted, otherwise the election
167 * would be pending until it's timed out.
168 */
169 } else if (election_state(attrd_cluster) == election_in_progress) {
170 pcmk__debug("Checking election status upon loss of voter %s",
171 peer->name);
172 election_check(attrd_cluster);
173 }
174 }
175
176 void
177 attrd_xml_add_writer(xmlNode *xml)
178 {
179 pcmk__xe_set_int(xml, PCMK__XA_ATTR_WRITER, election_state(attrd_cluster));
180 }
181