This source file includes following definitions.
- send_attrd_message
- attribute_timer_cb
- free_attribute_value
- free_attribute
- cache_remote_node
- add_attribute_value_xml
- clear_attribute_value_seen
- create_attribute
- attrd_client_peer_remove
- attrd_client_update
- attrd_client_clear_failure
- attrd_client_refresh
- build_query_reply
- attrd_client_query
- attrd_peer_clear_failure
- process_peer_sync_response
- attrd_broadcast_protocol
- attrd_peer_message
- attrd_peer_sync
- attrd_peer_remove
- attrd_lookup_or_create_value
- broadcast_unseen_local_values
- broadcast_local_value
- attrd_peer_update
- write_or_elect_attribute
- attrd_election_cb
- attrd_peer_change_cb
- attrd_cib_callback
- write_attributes
- build_update_element
- set_alert_attribute_value
- send_alert_attributes_value
- write_attribute
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/types.h>
13 #include <regex.h>
14 #include <glib.h>
15
16 #include <crm/msg_xml.h>
17 #include <crm/cluster.h>
18 #include <crm/cib.h>
19 #include <crm/common/xml_internal.h>
20 #include <crm/cluster/internal.h>
21 #include <crm/cluster/election_internal.h>
22 #include <crm/cib/internal.h>
23
24 #include "pacemaker-attrd.h"
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 #define ATTRD_PROTOCOL_VERSION "3"
49
50 int last_cib_op_done = 0;
51 GHashTable *attributes = NULL;
52
53 void write_attribute(attribute_t *a, bool ignore_delay);
54 void write_or_elect_attribute(attribute_t *a);
55 void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
56 void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
57 void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
58
59 static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
60
61 static gboolean
62 send_attrd_message(crm_node_t * node, xmlNode * data)
63 {
64 crm_xml_add(data, F_TYPE, T_ATTRD);
65 crm_xml_add(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
66 attrd_xml_add_writer(data);
67 return send_cluster_message(node, crm_msg_attrd, data, TRUE);
68 }
69
70 static gboolean
71 attribute_timer_cb(gpointer data)
72 {
73 attribute_t *a = data;
74 crm_trace("Dampen interval expired for %s", a->id);
75 write_or_elect_attribute(a);
76 return FALSE;
77 }
78
79 static void
80 free_attribute_value(gpointer data)
81 {
82 attribute_value_t *v = data;
83
84 free(v->nodename);
85 free(v->current);
86 free(v->requested);
87 free(v);
88 }
89
90 void
91 free_attribute(gpointer data)
92 {
93 attribute_t *a = data;
94 if(a) {
95 free(a->id);
96 free(a->set);
97 free(a->uuid);
98 free(a->user);
99
100 mainloop_timer_del(a->timer);
101 g_hash_table_destroy(a->values);
102
103 free(a);
104 }
105 }
106
107
108
109
110
111
112
113 static void
114 cache_remote_node(const char *node_name)
115 {
116
117
118
119 crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
120
121 if (dup && (dup->uuid == NULL)) {
122 reap_crm_member(0, node_name);
123 }
124
125
126 CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
127 }
128
129
130
131
132
133
134
135
136
137
138
139
140 static xmlNode *
141 add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
142 bool force_write)
143 {
144 xmlNode *xml = create_xml_node(parent, __func__);
145
146 crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
147 crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
148 crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
149 crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
150 crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
151 if (v->nodeid > 0) {
152 crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
153 }
154 if (v->is_remote != 0) {
155 crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
156 }
157 crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
158 crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
159 crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
160 crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
161
162 return xml;
163 }
164
165 static void
166 clear_attribute_value_seen(void)
167 {
168 GHashTableIter aIter;
169 GHashTableIter vIter;
170 attribute_t *a;
171 attribute_value_t *v = NULL;
172
173 g_hash_table_iter_init(&aIter, attributes);
174 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
175 g_hash_table_iter_init(&vIter, a->values);
176 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
177 v->seen = FALSE;
178 crm_trace("Clear seen flag %s[%s] = %s.", a->id, v->nodename, v->current);
179 }
180 }
181 }
182
183 static attribute_t *
184 create_attribute(xmlNode *xml)
185 {
186 int dampen = 0;
187 const char *value = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
188 attribute_t *a = calloc(1, sizeof(attribute_t));
189
190 a->id = crm_element_value_copy(xml, PCMK__XA_ATTR_NAME);
191 a->set = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
192 a->uuid = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID);
193 a->values = pcmk__strikey_table(NULL, free_attribute_value);
194
195 crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &a->is_private);
196
197 a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
198 crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
199
200 if(value) {
201 dampen = crm_get_msec(value);
202 crm_trace("Created attribute %s with delay %dms (%s)", a->id, dampen, value);
203 } else {
204 crm_trace("Created attribute %s with no delay", a->id);
205 }
206
207 if(dampen > 0) {
208 a->timeout_ms = dampen;
209 a->timer = mainloop_timer_add(a->id, a->timeout_ms, FALSE, attribute_timer_cb, a);
210 } else if (dampen < 0) {
211 crm_warn("Ignoring invalid delay %s for attribute %s", value, a->id);
212 }
213
214 g_hash_table_replace(attributes, a->id, a);
215 return a;
216 }
217
218
219
220
221
222
223
224
225
226
227 void
228 attrd_client_peer_remove(pcmk__client_t *client, xmlNode *xml)
229 {
230
231 const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
232 char *host_alloc = NULL;
233
234 if (host == NULL) {
235 int nodeid = 0;
236
237 crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, &nodeid);
238 if (nodeid > 0) {
239 crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL);
240 char *host_alloc = NULL;
241
242 if (node && node->uname) {
243
244 host = node->uname;
245 } else {
246
247 host_alloc = get_node_name(nodeid);
248 host = host_alloc;
249 }
250 crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, host);
251 }
252 }
253
254 if (host) {
255 crm_info("Client %s is requesting all values for %s be removed",
256 pcmk__client_name(client), host);
257 send_attrd_message(NULL, xml);
258 free(host_alloc);
259 } else {
260 crm_info("Ignoring request by client %s to remove all peer values without specifying peer",
261 pcmk__client_name(client));
262 }
263 }
264
265
266
267
268
269
270
271
272
273 void
274 attrd_client_update(xmlNode *xml)
275 {
276 attribute_t *a = NULL;
277 char *host = crm_element_value_copy(xml, PCMK__XA_ATTR_NODE_NAME);
278 const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
279 const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
280 const char *regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN);
281
282
283 if ((attr == NULL) && regex) {
284 GHashTableIter aIter;
285 regex_t *r_patt = calloc(1, sizeof(regex_t));
286
287 crm_debug("Setting %s to %s", regex, value);
288 if (regcomp(r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
289 crm_err("Bad regex '%s' for update", regex);
290
291 } else {
292 g_hash_table_iter_init(&aIter, attributes);
293 while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
294 int status = regexec(r_patt, attr, 0, NULL, 0);
295
296 if (status == 0) {
297 crm_trace("Matched %s with %s", attr, regex);
298 crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
299 send_attrd_message(NULL, xml);
300 }
301 }
302 }
303
304 free(host);
305 regfree(r_patt);
306 free(r_patt);
307 return;
308
309 } else if (attr == NULL) {
310 crm_err("Update request did not specify attribute or regular expression");
311 free(host);
312 return;
313 }
314
315 if (host == NULL) {
316 crm_trace("Inferring host");
317 host = strdup(attrd_cluster->uname);
318 crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, host);
319 crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, attrd_cluster->nodeid);
320 }
321
322 a = g_hash_table_lookup(attributes, attr);
323
324
325 if (value) {
326 if (attrd_value_needs_expansion(value)) {
327 int int_value;
328 attribute_value_t *v = NULL;
329
330 if (a) {
331 v = g_hash_table_lookup(a->values, host);
332 }
333 int_value = attrd_expand_value(value, (v? v->current : NULL));
334
335 crm_info("Expanded %s=%s to %d", attr, value, int_value);
336 crm_xml_add_int(xml, PCMK__XA_ATTR_VALUE, int_value);
337
338
339 value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
340 }
341 }
342
343 crm_debug("Broadcasting %s[%s]=%s%s", attr, host, value,
344 (attrd_election_won()? " (writer)" : ""));
345
346 free(host);
347
348 send_attrd_message(NULL, xml);
349 }
350
351
352
353
354
355
356
357 void
358 attrd_client_clear_failure(xmlNode *xml)
359 {
360 #if 0
361
362
363
364 if (compare_version("2", minimum_protocol_version) <= 0) {
365
366
367
368 send_attrd_message(NULL, xml);
369 return;
370 }
371 #endif
372
373 const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
374 const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
375 const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
376
377
378 crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
379
380
381
382 if (rsc) {
383 char *pattern;
384
385 if (op == NULL) {
386 pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
387
388 } else {
389 guint interval_ms = crm_parse_interval_spec(interval_spec);
390
391 pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
392 rsc, op, interval_ms);
393 }
394
395 crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, pattern);
396 free(pattern);
397
398 } else {
399 crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, ATTRD_RE_CLEAR_ALL);
400 }
401
402
403 if (crm_element_value(xml, PCMK__XA_ATTR_NAME)) {
404 crm_xml_replace(xml, PCMK__XA_ATTR_NAME, NULL);
405 }
406 if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
407 crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
408 }
409
410 attrd_client_update(xml);
411 }
412
413
414
415
416
417
418
419 void
420 attrd_client_refresh(void)
421 {
422 crm_info("Updating all attributes");
423 write_attributes(TRUE, TRUE);
424 }
425
426
427
428
429
430
431
432
433
434
435
436 static xmlNode *build_query_reply(const char *attr, const char *host)
437 {
438 xmlNode *reply = create_xml_node(NULL, __func__);
439 attribute_t *a;
440
441 if (reply == NULL) {
442 return NULL;
443 }
444 crm_xml_add(reply, F_TYPE, T_ATTRD);
445 crm_xml_add(reply, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
446
447
448 a = g_hash_table_lookup(attributes, attr);
449 if (a) {
450 attribute_value_t *v;
451 xmlNode *host_value;
452
453 crm_xml_add(reply, PCMK__XA_ATTR_NAME, attr);
454
455
456 if (pcmk__str_eq(host, "localhost", pcmk__str_casei)) {
457 host = attrd_cluster->uname;
458 crm_trace("Mapped localhost to %s", host);
459 }
460
461
462 if (host) {
463 v = g_hash_table_lookup(a->values, host);
464 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
465 if (host_value == NULL) {
466 free_xml(reply);
467 return NULL;
468 }
469 crm_xml_add(host_value, PCMK__XA_ATTR_NODE_NAME, host);
470 crm_xml_add(host_value, PCMK__XA_ATTR_VALUE,
471 (v? v->current : NULL));
472
473
474 } else {
475 GHashTableIter iter;
476
477 g_hash_table_iter_init(&iter, a->values);
478 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
479 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
480 if (host_value == NULL) {
481 free_xml(reply);
482 return NULL;
483 }
484 crm_xml_add(host_value, PCMK__XA_ATTR_NODE_NAME, v->nodename);
485 crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, v->current);
486 }
487 }
488 }
489 return reply;
490 }
491
492
493
494
495
496
497
498
499
500
501 void
502 attrd_client_query(pcmk__client_t *client, uint32_t id, uint32_t flags,
503 xmlNode *query)
504 {
505 const char *attr;
506 const char *origin = crm_element_value(query, F_ORIG);
507 xmlNode *reply;
508
509 if (origin == NULL) {
510 origin = "unknown client";
511 }
512 crm_debug("Query arrived from %s", origin);
513
514
515 attr = crm_element_value(query, PCMK__XA_ATTR_NAME);
516 if (attr == NULL) {
517 crm_warn("Ignoring malformed query from %s (no attribute name given)",
518 origin);
519 return;
520 }
521
522
523 reply = build_query_reply(attr, crm_element_value(query,
524 PCMK__XA_ATTR_NODE_NAME));
525 if (reply == NULL) {
526 crm_err("Could not respond to query from %s: could not create XML reply",
527 origin);
528 return;
529 }
530 crm_log_xml_trace(reply, "Reply");
531
532
533 client->request_id = 0;
534 {
535 int rc = pcmk__ipc_send_xml(client, id, reply, flags);
536
537 if (rc != pcmk_rc_ok) {
538 crm_err("Could not respond to query from %s: %s " CRM_XS " rc=%d",
539 origin, pcmk_rc_str(rc), rc);
540 }
541 }
542 free_xml(reply);
543 }
544
545
546
547
548
549
550
551
552 static void
553 attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
554 {
555 const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
556 const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
557 const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
558 const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
559 guint interval_ms = crm_parse_interval_spec(interval_spec);
560 char *attr = NULL;
561 GHashTableIter iter;
562 regex_t regex;
563
564 if (attrd_failure_regex(®ex, rsc, op, interval_ms) != pcmk_ok) {
565 crm_info("Ignoring invalid request to clear failures for %s",
566 (rsc? rsc : "all resources"));
567 return;
568 }
569
570 crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
571
572
573 if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
574 crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
575 }
576
577 g_hash_table_iter_init(&iter, attributes);
578 while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
579 if (regexec(®ex, attr, 0, NULL, 0) == 0) {
580 crm_trace("Matched %s when clearing %s",
581 attr, (rsc? rsc : "all resources"));
582 crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
583 attrd_peer_update(peer, xml, host, FALSE);
584 }
585 }
586 regfree(®ex);
587 }
588
589
590
591
592
593
594
595
596
597 static void
598 process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
599 {
600 crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
601 peer->uname);
602
603 if (peer_won) {
604
605
606
607 clear_attribute_value_seen();
608 }
609
610
611 for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
612 child = pcmk__xml_next(child)) {
613 attrd_peer_update(peer, child,
614 crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
615 TRUE);
616 }
617
618 if (peer_won) {
619
620
621
622 broadcast_unseen_local_values(peer, xml);
623 }
624 }
625
626
627
628
629
630 void
631 attrd_broadcast_protocol()
632 {
633 xmlNode *attrd_op = create_xml_node(NULL, __func__);
634
635 crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
636 crm_xml_add(attrd_op, F_ORIG, crm_system_name);
637 crm_xml_add(attrd_op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
638 crm_xml_add(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL);
639 crm_xml_add(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION);
640 crm_xml_add_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1);
641 attrd_client_update(attrd_op);
642 free_xml(attrd_op);
643 }
644
645 void
646 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
647 {
648 const char *op = crm_element_value(xml, PCMK__XA_TASK);
649 const char *election_op = crm_element_value(xml, F_CRM_TASK);
650 const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
651 bool peer_won = false;
652
653 if (election_op) {
654 attrd_handle_election_op(peer, xml);
655 return;
656 }
657
658 if (attrd_shutting_down()) {
659
660
661
662
663 return;
664 }
665
666 peer_won = attrd_check_for_new_writer(peer, xml);
667
668 if (pcmk__strcase_any_of(op, PCMK__ATTRD_CMD_UPDATE, PCMK__ATTRD_CMD_UPDATE_BOTH,
669 PCMK__ATTRD_CMD_UPDATE_DELAY, NULL)) {
670 attrd_peer_update(peer, xml, host, FALSE);
671
672 } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC, pcmk__str_casei)) {
673 attrd_peer_sync(peer, xml);
674
675 } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_PEER_REMOVE, pcmk__str_casei)) {
676 attrd_peer_remove(host, TRUE, peer->uname);
677
678 } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_CLEAR_FAILURE, pcmk__str_casei)) {
679
680
681
682 attrd_peer_clear_failure(peer, xml);
683
684 } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
685 && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
686 process_peer_sync_response(peer, peer_won, xml);
687
688 } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
689
690
691
692 }
693 }
694
695 void
696 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
697 {
698 GHashTableIter aIter;
699 GHashTableIter vIter;
700
701 attribute_t *a = NULL;
702 attribute_value_t *v = NULL;
703 xmlNode *sync = create_xml_node(NULL, __func__);
704
705 crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
706
707 g_hash_table_iter_init(&aIter, attributes);
708 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
709 g_hash_table_iter_init(&vIter, a->values);
710 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
711 crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
712 add_attribute_value_xml(sync, a, v, false);
713 }
714 }
715
716 crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
717 send_attrd_message(peer, sync);
718 free_xml(sync);
719 }
720
721
722
723
724
725
726
727
728
729 void
730 attrd_peer_remove(const char *host, gboolean uncache, const char *source)
731 {
732 attribute_t *a = NULL;
733 GHashTableIter aIter;
734
735 CRM_CHECK(host != NULL, return);
736 crm_notice("Removing all %s attributes for peer %s", host, source);
737
738 g_hash_table_iter_init(&aIter, attributes);
739 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
740 if(g_hash_table_remove(a->values, host)) {
741 crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
742 }
743 }
744
745 if (uncache) {
746 crm_remote_peer_cache_remove(host);
747 reap_crm_member(0, host);
748 }
749 }
750
751
752
753
754
755
756
757
758
759
760
761 static attribute_value_t *
762 attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
763 {
764 attribute_value_t *v = g_hash_table_lookup(values, host);
765 int is_remote = 0;
766
767 crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
768 if (is_remote) {
769 cache_remote_node(host);
770 }
771
772 if (v == NULL) {
773 v = calloc(1, sizeof(attribute_value_t));
774 CRM_ASSERT(v != NULL);
775
776 v->nodename = strdup(host);
777 CRM_ASSERT(v->nodename != NULL);
778
779 v->is_remote = is_remote;
780 g_hash_table_replace(values, v->nodename, v);
781 }
782 return(v);
783 }
784
785 void
786 broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
787 {
788 GHashTableIter aIter;
789 GHashTableIter vIter;
790 attribute_t *a = NULL;
791 attribute_value_t *v = NULL;
792 xmlNode *sync = NULL;
793
794 g_hash_table_iter_init(&aIter, attributes);
795 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
796 g_hash_table_iter_init(&vIter, a->values);
797 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
798 if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
799 pcmk__str_casei)) {
800 if (sync == NULL) {
801 sync = create_xml_node(NULL, __func__);
802 crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
803 }
804 add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
805 }
806 }
807 }
808
809 if (sync != NULL) {
810 crm_debug("Broadcasting local-only values");
811 send_attrd_message(NULL, sync);
812 free_xml(sync);
813 }
814 }
815
816
817
818
819
820
821
822
823
824
825
826
827
828 static attribute_value_t *
829 broadcast_local_value(attribute_t *a)
830 {
831 attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
832 xmlNode *sync = create_xml_node(NULL, __func__);
833
834 crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
835 add_attribute_value_xml(sync, a, v, false);
836 attrd_xml_add_writer(sync);
837 send_attrd_message(NULL, sync);
838 free_xml(sync);
839 return v;
840 }
841
842 void
843 attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
844 {
845 bool update_both = FALSE;
846 attribute_t *a;
847 attribute_value_t *v = NULL;
848 gboolean is_force_write = FALSE;
849
850 const char *op = crm_element_value(xml, PCMK__XA_TASK);
851 const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
852 const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
853 crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
854
855 if (attr == NULL) {
856 crm_warn("Could not update attribute: peer did not specify name");
857 return;
858 }
859
860
861 update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
862 pcmk__str_null_matches | pcmk__str_casei);
863
864
865 a = g_hash_table_lookup(attributes, attr);
866 if (a == NULL) {
867 if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_casei)) {
868 a = create_attribute(xml);
869 } else {
870 crm_warn("Could not update %s: attribute not found", attr);
871 return;
872 }
873 }
874
875
876 if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_casei)) {
877 const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
878 int dampen = 0;
879
880 if (dvalue == NULL) {
881 crm_warn("Could not update %s: peer did not specify value for delay",
882 attr);
883 return;
884 }
885
886 dampen = crm_get_msec(dvalue);
887 if (dampen < 0) {
888 crm_warn("Could not update %s: invalid delay value %dms (%s)",
889 attr, dampen, dvalue);
890 return;
891 }
892
893 if (a->timeout_ms != dampen) {
894 mainloop_timer_del(a->timer);
895 a->timeout_ms = dampen;
896 if (dampen > 0) {
897 a->timer = mainloop_timer_add(attr, a->timeout_ms, FALSE,
898 attribute_timer_cb, a);
899 crm_info("Update attribute %s delay to %dms (%s)",
900 attr, dampen, dvalue);
901 } else {
902 a->timer = NULL;
903 crm_info("Update attribute %s to remove delay", attr);
904 }
905
906
907
908
909 write_or_elect_attribute(a);
910 }
911
912 if (!update_both) {
913 return;
914 }
915 }
916
917
918 if (host == NULL) {
919 GHashTableIter vIter;
920
921 crm_debug("Setting %s for all hosts to %s", attr, value);
922 xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
923 g_hash_table_iter_init(&vIter, a->values);
924 while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
925 attrd_peer_update(peer, xml, host, filter);
926 }
927 return;
928 }
929
930
931
932 v = attrd_lookup_or_create_value(a->values, host, xml);
933
934 if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
935 && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
936
937 crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
938 attr, host, v->current, value, peer->uname);
939 v = broadcast_local_value(a);
940
941 } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
942 crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
943 attr, host, v->current? v->current : "(unset)", value? value : "(unset)", peer->uname);
944 free(v->current);
945 v->current = (value? strdup(value) : NULL);
946 a->changed = TRUE;
947
948 if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
949 && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
950
951 if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
952 attrd_set_requesting_shutdown();
953
954 } else {
955 attrd_clear_requesting_shutdown();
956 }
957 }
958
959
960 if (a->timeout_ms && a->timer) {
961 crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
962 mainloop_timer_start(a->timer);
963 } else {
964 write_or_elect_attribute(a);
965 }
966
967 } else {
968 if (is_force_write && a->timeout_ms && a->timer) {
969
970
971 crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)", attr, host, peer->uname, value);
972 a->force_write = TRUE;
973 } else {
974 crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
975 }
976 }
977
978
979 v->seen = TRUE;
980
981
982 if ((v->nodeid == 0) && (v->is_remote == FALSE)
983 && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
984 (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
985
986 crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
987
988 crm_trace("Learned %s has node id %s",
989 known_peer->uname, known_peer->uuid);
990 if (attrd_election_won()) {
991 write_attributes(FALSE, FALSE);
992 }
993 }
994 }
995
996 void
997 write_or_elect_attribute(attribute_t *a)
998 {
999 if (attrd_election_won()) {
1000 write_attribute(a, FALSE);
1001 } else {
1002 attrd_start_election_if_needed();
1003 }
1004 }
1005
1006 gboolean
1007 attrd_election_cb(gpointer user_data)
1008 {
1009 attrd_declare_winner();
1010
1011
1012 attrd_peer_sync(NULL, NULL);
1013
1014
1015 write_attributes(TRUE, FALSE);
1016 return FALSE;
1017 }
1018
1019 #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
1020
1021 void
1022 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
1023 {
1024 bool gone = false;
1025 bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
1026
1027 switch (kind) {
1028 case crm_status_uname:
1029 crm_debug("%s node %s is now %s",
1030 (is_remote? "Remote" : "Cluster"),
1031 peer->uname, state_text(peer->state));
1032 break;
1033
1034 case crm_status_processes:
1035 if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
1036 gone = true;
1037 }
1038 crm_debug("Node %s is %s a peer",
1039 peer->uname, (gone? "no longer" : "now"));
1040 break;
1041
1042 case crm_status_nstate:
1043 crm_debug("%s node %s is now %s (was %s)",
1044 (is_remote? "Remote" : "Cluster"),
1045 peer->uname, state_text(peer->state), state_text(data));
1046 if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
1047
1048
1049
1050 if (attrd_election_won()
1051 && !pcmk_is_set(peer->flags, crm_remote_node)) {
1052 attrd_peer_sync(peer, NULL);
1053 }
1054 } else {
1055
1056 attrd_peer_remove(peer->uname, FALSE, "loss");
1057 gone = true;
1058 }
1059 break;
1060 }
1061
1062
1063 if (gone && !is_remote) {
1064 attrd_remove_voter(peer);
1065
1066
1067 } else if (!gone && is_remote) {
1068 cache_remote_node(peer->uname);
1069 }
1070 }
1071
1072 static void
1073 attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
1074 {
1075 int level = LOG_ERR;
1076 GHashTableIter iter;
1077 const char *peer = NULL;
1078 attribute_value_t *v = NULL;
1079
1080 char *name = user_data;
1081 attribute_t *a = g_hash_table_lookup(attributes, name);
1082
1083 if(a == NULL) {
1084 crm_info("Attribute %s no longer exists", name);
1085 return;
1086 }
1087
1088 a->update = 0;
1089 if (rc == pcmk_ok && call_id < 0) {
1090 rc = call_id;
1091 }
1092
1093 switch (rc) {
1094 case pcmk_ok:
1095 level = LOG_INFO;
1096 last_cib_op_done = call_id;
1097 if (a->timer && !a->timeout_ms) {
1098
1099 mainloop_timer_del(a->timer);
1100 a->timer = NULL;
1101 }
1102 break;
1103
1104 case -pcmk_err_diff_failed:
1105 case -ETIME:
1106 case -ENXIO:
1107
1108
1109 level = LOG_WARNING;
1110 break;
1111 }
1112
1113 do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
1114 call_id, a->id, pcmk_strerror(rc), rc);
1115
1116 g_hash_table_iter_init(&iter, a->values);
1117 while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
1118 do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
1119 free(v->requested);
1120 v->requested = NULL;
1121 if (rc != pcmk_ok) {
1122 a->changed = TRUE;
1123 }
1124 }
1125
1126 if (a->changed && attrd_election_won()) {
1127 if (rc == pcmk_ok) {
1128
1129
1130
1131 write_attribute(a, FALSE);
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142 } else if (a->timer) {
1143
1144 if (!mainloop_timer_running(a->timer)) {
1145 crm_trace("Delayed re-attempted write (%dms) for %s",
1146 a->timeout_ms, name);
1147 mainloop_timer_start(a->timer);
1148 }
1149 } else {
1150
1151
1152
1153
1154 a->timer = mainloop_timer_add(a->id, 2000, FALSE,
1155 attribute_timer_cb, a);
1156 mainloop_timer_start(a->timer);
1157 }
1158 }
1159 }
1160
1161 void
1162 write_attributes(bool all, bool ignore_delay)
1163 {
1164 GHashTableIter iter;
1165 attribute_t *a = NULL;
1166
1167 crm_debug("Writing out %s attributes", all? "all" : "changed");
1168 g_hash_table_iter_init(&iter, attributes);
1169 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
1170 if (!all && a->unknown_peer_uuids) {
1171
1172 a->changed = TRUE;
1173 } else if (a->force_write) {
1174
1175 a->changed = TRUE;
1176 }
1177
1178 if(all || a->changed) {
1179
1180 write_attribute(a, (a->force_write ? TRUE : ignore_delay));
1181 } else {
1182 crm_trace("Skipping unchanged attribute %s", a->id);
1183 }
1184 }
1185 }
1186
1187 static void
1188 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
1189 {
1190 const char *set = NULL;
1191 xmlNode *xml_obj = NULL;
1192
1193 xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
1194 crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1195
1196 xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
1197 crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1198
1199 xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
1200 if (a->set) {
1201 crm_xml_set_id(xml_obj, "%s", a->set);
1202 } else {
1203 crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
1204 }
1205 set = ID(xml_obj);
1206
1207 xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
1208 if (a->uuid) {
1209 crm_xml_set_id(xml_obj, "%s", a->uuid);
1210 } else {
1211 crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
1212 }
1213 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
1214
1215 if(value) {
1216 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
1217
1218 } else {
1219 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
1220 crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
1221 }
1222 }
1223
1224 static void
1225 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
1226 {
1227 attribute_value_t *a_v = NULL;
1228 a_v = calloc(1, sizeof(attribute_value_t));
1229 CRM_ASSERT(a_v != NULL);
1230
1231 a_v->nodeid = v->nodeid;
1232 a_v->nodename = strdup(v->nodename);
1233
1234 if (v->current != NULL) {
1235 a_v->current = strdup(v->current);
1236 }
1237
1238 g_hash_table_replace(t, a_v->nodename, a_v);
1239 }
1240
1241 static void
1242 send_alert_attributes_value(attribute_t *a, GHashTable *t)
1243 {
1244 int rc = 0;
1245 attribute_value_t *at = NULL;
1246 GHashTableIter vIter;
1247
1248 g_hash_table_iter_init(&vIter, t);
1249
1250 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
1251 rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
1252 a->id, at->current);
1253 crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
1254 a->id, at->nodename, at->current, at->nodeid, rc);
1255 }
1256 }
1257
1258 void
1259 write_attribute(attribute_t *a, bool ignore_delay)
1260 {
1261 int private_updates = 0, cib_updates = 0;
1262 xmlNode *xml_top = NULL;
1263 attribute_value_t *v = NULL;
1264 GHashTableIter iter;
1265 enum cib_call_options flags = cib_quorum_override;
1266 GHashTable *alert_attribute_value = NULL;
1267
1268 if (a == NULL) {
1269 return;
1270 }
1271
1272
1273 if (!a->is_private) {
1274
1275
1276 CRM_CHECK(the_cib != NULL, return);
1277 if (a->update && (a->update < last_cib_op_done)) {
1278 crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
1279 a->update = 0;
1280
1281 } else if (a->update) {
1282 crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
1283 return;
1284
1285 } else if (mainloop_timer_running(a->timer)) {
1286 if (ignore_delay) {
1287
1288
1289
1290 mainloop_timer_stop(a->timer);
1291 crm_debug("Write out of '%s': timer is running but ignore delay", a->id);
1292 } else {
1293 crm_info("Write out of '%s' delayed: timer is running", a->id);
1294 return;
1295 }
1296 }
1297
1298
1299 xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
1300 }
1301
1302
1303 a->changed = FALSE;
1304
1305
1306 a->unknown_peer_uuids = FALSE;
1307
1308
1309 a->force_write = FALSE;
1310
1311
1312 alert_attribute_value = pcmk__strikey_table(NULL, free_attribute_value);
1313
1314
1315 g_hash_table_iter_init(&iter, a->values);
1316 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
1317 crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
1318
1319
1320 if (peer == NULL) {
1321 crm_notice("Cannot update %s[%s]=%s because peer not known",
1322 a->id, v->nodename, v->current);
1323 continue;
1324 }
1325
1326
1327 if (peer->id && (v->nodeid == 0)) {
1328 crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
1329 v->nodeid = peer->id;
1330 }
1331
1332
1333 if (a->is_private) {
1334 private_updates++;
1335 continue;
1336 }
1337
1338
1339 if (peer->uuid == NULL) {
1340 a->unknown_peer_uuids = TRUE;
1341 crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
1342 "(will retry if learned)",
1343 a->id, v->nodename, v->current);
1344 continue;
1345 }
1346
1347
1348 crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
1349 a->id, v->nodename, v->current,
1350 peer->uname, peer->uuid, peer->id, v->nodeid);
1351 build_update_element(xml_top, a, peer->uuid, v->current);
1352 cib_updates++;
1353
1354
1355 set_alert_attribute_value(alert_attribute_value, v);
1356
1357 free(v->requested);
1358 v->requested = NULL;
1359 if (v->current) {
1360 v->requested = strdup(v->current);
1361 } else {
1362
1363
1364
1365 cib__set_call_options(flags, crm_system_name,
1366 cib_mixed_update|cib_scope_local);
1367 }
1368 }
1369
1370 if (private_updates) {
1371 crm_info("Processed %d private change%s for %s, id=%s, set=%s",
1372 private_updates, pcmk__plural_s(private_updates),
1373 a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1374 }
1375 if (cib_updates) {
1376 crm_log_xml_trace(xml_top, __func__);
1377
1378 a->update = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL,
1379 flags, a->user);
1380
1381 crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
1382 a->update, cib_updates, pcmk__plural_s(cib_updates),
1383 a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1384
1385 the_cib->cmds->register_callback_full(the_cib, a->update,
1386 CIB_OP_TIMEOUT_S, FALSE,
1387 strdup(a->id),
1388 "attrd_cib_callback",
1389 attrd_cib_callback, free);
1390
1391 send_alert_attributes_value(a, alert_attribute_value);
1392 }
1393
1394 g_hash_table_destroy(alert_attribute_value);
1395 free_xml(xml_top);
1396 }