This source file includes following definitions.
- attrd_cib_destroy_cb
- attrd_cib_updated_cb
- attrd_cib_connect
- attrd_cib_disconnect
- attrd_erase_cb
- attrd_erase_attrs
- attrd_cib_init
- attribute_timer_cb
- attrd_cib_callback
- add_set_attr_update
- add_unset_attr_update
- add_attr_update
- send_alert_attributes_value
- set_alert_attribute_value
- attrd_add_timer
- write_attribute
- attrd_write_attributes
- attrd_write_or_elect_attribute
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <errno.h>
13 #include <inttypes.h>
14 #include <stdbool.h>
15 #include <stdlib.h>
16 #include <glib.h>
17
18 #include <crm/msg_xml.h>
19 #include <crm/common/logging.h>
20 #include <crm/common/results.h>
21 #include <crm/common/strings_internal.h>
22 #include <crm/common/xml.h>
23
24 #include "pacemaker-attrd.h"
25
26 static int last_cib_op_done = 0;
27
28 static void write_attribute(attribute_t *a, bool ignore_delay);
29
30 static void
31 attrd_cib_destroy_cb(gpointer user_data)
32 {
33 cib_t *cib = user_data;
34
35 cib->cmds->signoff(cib);
36
37 if (attrd_shutting_down(false)) {
38 crm_info("Disconnected from the CIB manager");
39
40 } else {
41
42 crm_crit("Lost connection to the CIB manager, shutting down");
43 attrd_exit_status = CRM_EX_DISCONNECT;
44 attrd_shutdown(0);
45 }
46 }
47
48 static void
49 attrd_cib_updated_cb(const char *event, xmlNode *msg)
50 {
51 const xmlNode *patchset = NULL;
52 const char *client_name = NULL;
53
54 if (attrd_shutting_down(true)) {
55 return;
56 }
57
58 if (cib__get_notify_patchset(msg, &patchset) != pcmk_rc_ok) {
59 return;
60 }
61
62 if (cib__element_in_patchset(patchset, XML_CIB_TAG_ALERTS)) {
63 mainloop_set_trigger(attrd_config_read);
64 }
65
66 if (!attrd_election_won()) {
67
68 return;
69 }
70
71 client_name = crm_element_value(msg, F_CIB_CLIENTNAME);
72 if (!cib__client_triggers_refresh(client_name)) {
73
74 return;
75 }
76
77 if (cib__element_in_patchset(patchset, XML_CIB_TAG_NODES)
78 || cib__element_in_patchset(patchset, XML_CIB_TAG_STATUS)) {
79
80
81
82
83 if (client_name == NULL) {
84 client_name = crm_element_value(msg, F_CIB_CLIENTID);
85 }
86 crm_notice("Updating all attributes after %s event triggered by %s",
87 event, pcmk__s(client_name, "(unidentified client)"));
88
89 attrd_write_attributes(attrd_write_all);
90 }
91 }
92
93 int
94 attrd_cib_connect(int max_retry)
95 {
96 static int attempts = 0;
97
98 int rc = -ENOTCONN;
99
100 the_cib = cib_new();
101 if (the_cib == NULL) {
102 return -ENOTCONN;
103 }
104
105 do {
106 if (attempts > 0) {
107 sleep(attempts);
108 }
109 attempts++;
110 crm_debug("Connection attempt %d to the CIB manager", attempts);
111 rc = the_cib->cmds->signon(the_cib, T_ATTRD, cib_command);
112
113 } while ((rc != pcmk_ok) && (attempts < max_retry));
114
115 if (rc != pcmk_ok) {
116 crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
117 pcmk_strerror(rc), rc);
118 goto cleanup;
119 }
120
121 crm_debug("Connected to the CIB manager after %d attempts", attempts);
122
123 rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
124 if (rc != pcmk_ok) {
125 crm_err("Could not set disconnection callback");
126 goto cleanup;
127 }
128
129 rc = the_cib->cmds->add_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
130 attrd_cib_updated_cb);
131 if (rc != pcmk_ok) {
132 crm_err("Could not set CIB notification callback");
133 goto cleanup;
134 }
135
136 return pcmk_ok;
137
138 cleanup:
139 cib__clean_up_connection(&the_cib);
140 return -ENOTCONN;
141 }
142
143 void
144 attrd_cib_disconnect(void)
145 {
146 CRM_CHECK(the_cib != NULL, return);
147 the_cib->cmds->del_notify_callback(the_cib, T_CIB_DIFF_NOTIFY,
148 attrd_cib_updated_cb);
149 cib__clean_up_connection(&the_cib);
150 }
151
152 static void
153 attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
154 void *user_data)
155 {
156 do_crm_log_unlikely(((rc != pcmk_ok)? LOG_NOTICE : LOG_DEBUG),
157 "Cleared transient attributes: %s "
158 CRM_XS " xpath=%s rc=%d",
159 pcmk_strerror(rc), (char *) user_data, rc);
160 }
161
162 #define XPATH_TRANSIENT "//node_state[@uname='%s']/" XML_TAG_TRANSIENT_NODEATTRS
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 static void
179 attrd_erase_attrs(void)
180 {
181 int call_id = 0;
182 char *xpath = crm_strdup_printf(XPATH_TRANSIENT, attrd_cluster->uname);
183
184 crm_info("Clearing transient attributes from CIB " CRM_XS " xpath=%s",
185 xpath);
186
187 call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
188 the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE, xpath,
189 "attrd_erase_cb", attrd_erase_cb,
190 free);
191 }
192
193
194
195
196
197 void
198 attrd_cib_init(void)
199 {
200
201 attrd_erase_attrs();
202
203
204 attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
205
206
207 mainloop_set_trigger(attrd_config_read);
208 }
209
210 static gboolean
211 attribute_timer_cb(gpointer data)
212 {
213 attribute_t *a = data;
214 crm_trace("Dampen interval expired for %s", a->id);
215 attrd_write_or_elect_attribute(a);
216 return FALSE;
217 }
218
219 static void
220 attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data)
221 {
222 int level = LOG_ERR;
223 GHashTableIter iter;
224 const char *peer = NULL;
225 attribute_value_t *v = NULL;
226
227 char *name = user_data;
228 attribute_t *a = g_hash_table_lookup(attributes, name);
229
230 if(a == NULL) {
231 crm_info("Attribute %s no longer exists", name);
232 return;
233 }
234
235 a->update = 0;
236 if (rc == pcmk_ok && call_id < 0) {
237 rc = call_id;
238 }
239
240 switch (rc) {
241 case pcmk_ok:
242 level = LOG_INFO;
243 last_cib_op_done = call_id;
244 if (a->timer && !a->timeout_ms) {
245
246 mainloop_timer_del(a->timer);
247 a->timer = NULL;
248 }
249 break;
250
251 case -pcmk_err_diff_failed:
252 case -ETIME:
253 case -ENXIO:
254
255
256 level = LOG_WARNING;
257 break;
258 }
259
260 do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
261 call_id, a->id, pcmk_strerror(rc), rc);
262
263 g_hash_table_iter_init(&iter, a->values);
264 while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
265 do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
266 free(v->requested);
267 v->requested = NULL;
268 if (rc != pcmk_ok) {
269 a->changed = true;
270 }
271 }
272
273 if (a->changed && attrd_election_won()) {
274 if (rc == pcmk_ok) {
275
276
277
278 write_attribute(a, false);
279
280
281
282
283
284
285
286
287
288
289 } else if (a->timer) {
290
291 if (!mainloop_timer_running(a->timer)) {
292 crm_trace("Delayed re-attempted write for %s by %s",
293 name, pcmk__readable_interval(a->timeout_ms));
294 mainloop_timer_start(a->timer);
295 }
296 } else {
297
298
299
300
301 a->timer = attrd_add_timer(a->id, 2000, a);
302 mainloop_timer_start(a->timer);
303 }
304 }
305 }
306
307
308
309
310
311
312
313
314
315
316
317
318
319 static int
320 add_set_attr_update(const attribute_t *attr, const char *attr_id,
321 const char *node_id, const char *set_id, const char *value)
322 {
323 xmlNode *update = create_xml_node(NULL, XML_CIB_TAG_STATE);
324 xmlNode *child = update;
325 int rc = ENOMEM;
326
327 if (child == NULL) {
328 goto done;
329 }
330 crm_xml_add(child, XML_ATTR_ID, node_id);
331
332 child = create_xml_node(child, XML_TAG_TRANSIENT_NODEATTRS);
333 if (child == NULL) {
334 goto done;
335 }
336 crm_xml_add(child, XML_ATTR_ID, node_id);
337
338 child = create_xml_node(child, attr->set_type);
339 if (child == NULL) {
340 goto done;
341 }
342 crm_xml_add(child, XML_ATTR_ID, set_id);
343
344 child = create_xml_node(child, XML_CIB_TAG_NVPAIR);
345 if (child == NULL) {
346 goto done;
347 }
348 crm_xml_add(child, XML_ATTR_ID, attr_id);
349 crm_xml_add(child, XML_NVPAIR_ATTR_NAME, attr->id);
350 crm_xml_add(child, XML_NVPAIR_ATTR_VALUE, value);
351
352 rc = the_cib->cmds->modify(the_cib, XML_CIB_TAG_STATUS, update,
353 cib_can_create|cib_transaction);
354 rc = pcmk_legacy2rc(rc);
355
356 done:
357 free_xml(update);
358 return rc;
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372 static int
373 add_unset_attr_update(const attribute_t *attr, const char *attr_id,
374 const char *node_id, const char *set_id)
375 {
376 char *xpath = crm_strdup_printf("/" XML_TAG_CIB
377 "/" XML_CIB_TAG_STATUS
378 "/" XML_CIB_TAG_STATE
379 "[@" XML_ATTR_ID "='%s']"
380 "/" XML_TAG_TRANSIENT_NODEATTRS
381 "[@" XML_ATTR_ID "='%s']"
382 "/%s[@" XML_ATTR_ID "='%s']"
383 "/" XML_CIB_TAG_NVPAIR
384 "[@" XML_ATTR_ID "='%s' "
385 "and @" XML_NVPAIR_ATTR_NAME "='%s']",
386 node_id, node_id, attr->set_type, set_id,
387 attr_id, attr->id);
388
389 int rc = the_cib->cmds->remove(the_cib, xpath, NULL,
390 cib_xpath|cib_transaction);
391
392 free(xpath);
393 return pcmk_legacy2rc(rc);
394 }
395
396
397
398
399
400
401
402
403
404
405
406 static int
407 add_attr_update(const attribute_t *attr, const char *value, const char *node_id)
408 {
409 char *set_id = NULL;
410 char *attr_id = NULL;
411 int rc = pcmk_rc_ok;
412
413 if (attr->set_id != NULL) {
414 pcmk__str_update(&set_id, attr->set_id);
415 } else {
416 set_id = crm_strdup_printf("%s-%s", XML_CIB_TAG_STATUS, node_id);
417 }
418 crm_xml_sanitize_id(set_id);
419
420 if (attr->uuid != NULL) {
421 pcmk__str_update(&attr_id, attr->uuid);
422 } else {
423 attr_id = crm_strdup_printf("%s-%s", set_id, attr->id);
424 }
425 crm_xml_sanitize_id(attr_id);
426
427 if (value != NULL) {
428 rc = add_set_attr_update(attr, attr_id, node_id, set_id, value);
429 } else {
430 rc = add_unset_attr_update(attr, attr_id, node_id, set_id);
431 }
432 free(set_id);
433 free(attr_id);
434 return rc;
435 }
436
437 static void
438 send_alert_attributes_value(attribute_t *a, GHashTable *t)
439 {
440 int rc = 0;
441 attribute_value_t *at = NULL;
442 GHashTableIter vIter;
443
444 g_hash_table_iter_init(&vIter, t);
445
446 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
447 rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
448 a->id, at->current);
449 crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
450 a->id, at->nodename, at->current, at->nodeid, rc);
451 }
452 }
453
454 static void
455 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
456 {
457 attribute_value_t *a_v = NULL;
458 a_v = calloc(1, sizeof(attribute_value_t));
459 CRM_ASSERT(a_v != NULL);
460
461 a_v->nodeid = v->nodeid;
462 a_v->nodename = strdup(v->nodename);
463 pcmk__str_update(&a_v->current, v->current);
464
465 g_hash_table_replace(t, a_v->nodename, a_v);
466 }
467
468 mainloop_timer_t *
469 attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr)
470 {
471 return mainloop_timer_add(id, timeout_ms, FALSE, attribute_timer_cb, attr);
472 }
473
474
475
476
477
478
479
480
481
482 static void
483 write_attribute(attribute_t *a, bool ignore_delay)
484 {
485 int private_updates = 0, cib_updates = 0;
486 attribute_value_t *v = NULL;
487 GHashTableIter iter;
488 GHashTable *alert_attribute_value = NULL;
489 int rc = pcmk_ok;
490
491 if (a == NULL) {
492 return;
493 }
494
495
496 if (!stand_alone && !a->is_private) {
497
498 if (a->update && (a->update < last_cib_op_done)) {
499 crm_info("Write out of '%s' continuing: update %d considered lost",
500 a->id, a->update);
501 a->update = 0;
502
503 } else if (a->update) {
504 crm_info("Write out of '%s' delayed: update %d in progress",
505 a->id, a->update);
506 goto done;
507
508 } else if (mainloop_timer_running(a->timer)) {
509 if (ignore_delay) {
510 mainloop_timer_stop(a->timer);
511 crm_debug("Overriding '%s' write delay", a->id);
512 } else {
513 crm_info("Delaying write of '%s'", a->id);
514 goto done;
515 }
516 }
517
518
519 CRM_CHECK(the_cib != NULL, goto done);
520 the_cib->cmds->set_user(the_cib, a->user);
521 rc = the_cib->cmds->init_transaction(the_cib);
522 if (rc != pcmk_ok) {
523 crm_err("Failed to write %s (id %s, set %s): Could not initiate "
524 "CIB transaction",
525 a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
526 goto done;
527 }
528 }
529
530
531 a->changed = false;
532
533
534 a->unknown_peer_uuids = false;
535
536
537 a->force_write = FALSE;
538
539
540 alert_attribute_value = pcmk__strikey_table(NULL,
541 attrd_free_attribute_value);
542
543
544 g_hash_table_iter_init(&iter, a->values);
545 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
546 crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename,
547 CRM_GET_PEER_ANY);
548
549
550 if (peer == NULL) {
551 crm_notice("Cannot update %s[%s]=%s because peer not known",
552 a->id, v->nodename, v->current);
553 continue;
554 }
555
556
557 if (peer->id && (v->nodeid == 0)) {
558 crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
559 v->nodeid = peer->id;
560 }
561
562
563 if (stand_alone || a->is_private) {
564 private_updates++;
565 continue;
566 }
567
568
569 if (peer->uuid == NULL) {
570 a->unknown_peer_uuids = true;
571 crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
572 "(will retry if learned)",
573 a->id, v->nodename, v->current);
574 continue;
575 }
576
577
578 rc = add_attr_update(a, v->current, peer->uuid);
579 if (rc != pcmk_rc_ok) {
580 crm_err("Failed to update %s[%s]=%s (peer known as %s, UUID %s, "
581 "ID %" PRIu32 "/%" PRIu32 "): %s",
582 a->id, v->nodename, v->current, peer->uname, peer->uuid,
583 peer->id, v->nodeid, pcmk_rc_str(rc));
584 continue;
585 }
586
587 crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID "
588 "%" PRIu32 "/%" PRIu32 ")",
589 a->id, v->nodename, v->current,
590 peer->uname, peer->uuid, peer->id, v->nodeid);
591 cib_updates++;
592
593
594 set_alert_attribute_value(alert_attribute_value, v);
595
596 free(v->requested);
597 v->requested = NULL;
598 if (v->current) {
599 v->requested = strdup(v->current);
600 }
601 }
602
603 if (private_updates) {
604 crm_info("Processed %d private change%s for %s, id=%s, set=%s",
605 private_updates, pcmk__plural_s(private_updates),
606 a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
607 }
608 if (cib_updates > 0) {
609 char *id = NULL;
610
611
612 a->update = the_cib->cmds->end_transaction(the_cib, true, cib_none);
613
614 crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
615 a->update, cib_updates, pcmk__plural_s(cib_updates),
616 a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
617
618 pcmk__str_update(&id, a->id);
619 if (the_cib->cmds->register_callback_full(the_cib, a->update,
620 CIB_OP_TIMEOUT_S, FALSE, id,
621 "attrd_cib_callback",
622 attrd_cib_callback, free)) {
623
624 send_alert_attributes_value(a, alert_attribute_value);
625 }
626 }
627
628 done:
629
630 if (the_cib != NULL) {
631 the_cib->cmds->end_transaction(the_cib, false, cib_none);
632 the_cib->cmds->set_user(the_cib, NULL);
633 }
634
635 if (alert_attribute_value != NULL) {
636 g_hash_table_destroy(alert_attribute_value);
637 }
638 }
639
640
641
642
643
644
645
646 void
647 attrd_write_attributes(uint32_t options)
648 {
649 GHashTableIter iter;
650 attribute_t *a = NULL;
651
652 crm_debug("Writing out %s attributes",
653 pcmk_is_set(options, attrd_write_all)? "all" : "changed");
654 g_hash_table_iter_init(&iter, attributes);
655 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
656 if (!pcmk_is_set(options, attrd_write_all) && a->unknown_peer_uuids) {
657
658 a->changed = true;
659 } else if (a->force_write) {
660
661 a->changed = true;
662 }
663
664 if (pcmk_is_set(options, attrd_write_all) || a->changed) {
665 bool ignore_delay = pcmk_is_set(options, attrd_write_no_delay);
666
667 if (a->force_write) {
668
669 ignore_delay = true;
670 }
671 write_attribute(a, ignore_delay);
672 } else {
673 crm_trace("Skipping unchanged attribute %s", a->id);
674 }
675 }
676 }
677
678 void
679 attrd_write_or_elect_attribute(attribute_t *a)
680 {
681 if (attrd_election_won()) {
682 write_attribute(a, false);
683 } else {
684 attrd_start_election_if_needed();
685 }
686 }