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