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