This source file includes following definitions.
- send_attrd_message
- attribute_timer_cb
- free_attribute_value
- free_attribute
- build_attribute_xml
- 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
- attrd_peer_message
- attrd_peer_sync
- attrd_peer_remove
- attrd_lookup_or_create_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
11
12
13
14
15
16
17
18 #include <crm_internal.h>
19
20 #include <sys/types.h>
21 #include <regex.h>
22 #include <glib.h>
23
24 #include <crm/msg_xml.h>
25 #include <crm/cluster.h>
26 #include <crm/cib.h>
27 #include <crm/cluster/internal.h>
28 #include <crm/cluster/election.h>
29 #include <crm/cib/internal.h>
30
31 #include <internal.h>
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 #define ATTRD_PROTOCOL_VERSION "2"
51
52 int last_cib_op_done = 0;
53 char *peer_writer = NULL;
54 GHashTable *attributes = NULL;
55
56 void write_attribute(attribute_t *a);
57 void write_or_elect_attribute(attribute_t *a);
58 void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
59 void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
60 void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
61
62 static gboolean
63 send_attrd_message(crm_node_t * node, xmlNode * data)
64 {
65 crm_xml_add(data, F_TYPE, T_ATTRD);
66 crm_xml_add(data, F_ATTRD_IGNORE_LOCALLY, "atomic-version");
67 crm_xml_add(data, F_ATTRD_VERSION, ATTRD_PROTOCOL_VERSION);
68 crm_xml_add_int(data, F_ATTRD_WRITER, election_state(writer));
69
70 return send_cluster_message(node, crm_msg_attrd, data, TRUE);
71 }
72
73 static gboolean
74 attribute_timer_cb(gpointer data)
75 {
76 attribute_t *a = data;
77 crm_trace("Dampen interval expired for %s in state %d", a->id, election_state(writer));
78 write_or_elect_attribute(a);
79 return FALSE;
80 }
81
82 static void
83 free_attribute_value(gpointer data)
84 {
85 attribute_value_t *v = data;
86
87 free(v->nodename);
88 free(v->current);
89 free(v->requested);
90 free(v);
91 }
92
93 void
94 free_attribute(gpointer data)
95 {
96 attribute_t *a = data;
97 if(a) {
98 free(a->id);
99 free(a->set);
100 free(a->uuid);
101 free(a->user);
102
103 mainloop_timer_del(a->timer);
104 g_hash_table_destroy(a->values);
105
106 free(a);
107 }
108 }
109
110 static xmlNode *
111 build_attribute_xml(
112 xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
113 gboolean is_private, const char *peer, uint32_t peerid, const char *value)
114 {
115 xmlNode *xml = create_xml_node(parent, __FUNCTION__);
116
117 crm_xml_add(xml, F_ATTRD_ATTRIBUTE, name);
118 crm_xml_add(xml, F_ATTRD_SET, set);
119 crm_xml_add(xml, F_ATTRD_KEY, uuid);
120 crm_xml_add(xml, F_ATTRD_USER, user);
121 crm_xml_add(xml, F_ATTRD_HOST, peer);
122 crm_xml_add_int(xml, F_ATTRD_HOST_ID, peerid);
123 crm_xml_add(xml, F_ATTRD_VALUE, value);
124 crm_xml_add_int(xml, F_ATTRD_DAMPEN, timeout_ms/1000);
125 crm_xml_add_int(xml, F_ATTRD_IS_PRIVATE, is_private);
126
127 return xml;
128 }
129
130 static attribute_t *
131 create_attribute(xmlNode *xml)
132 {
133 int dampen = 0;
134 const char *value = crm_element_value(xml, F_ATTRD_DAMPEN);
135 attribute_t *a = calloc(1, sizeof(attribute_t));
136
137 a->id = crm_element_value_copy(xml, F_ATTRD_ATTRIBUTE);
138 a->set = crm_element_value_copy(xml, F_ATTRD_SET);
139 a->uuid = crm_element_value_copy(xml, F_ATTRD_KEY);
140 a->values = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, free_attribute_value);
141
142 crm_element_value_int(xml, F_ATTRD_IS_PRIVATE, &a->is_private);
143
144 #if ENABLE_ACL
145 crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
146 a->user = crm_element_value_copy(xml, F_ATTRD_USER);
147 #endif
148
149 if(value) {
150 dampen = crm_get_msec(value);
151 crm_trace("Created attribute %s with delay %dms (%s)", a->id, dampen, value);
152 } else {
153 crm_trace("Created attribute %s with no delay", a->id);
154 }
155
156 if(dampen > 0) {
157 a->timeout_ms = dampen;
158 a->timer = mainloop_timer_add(a->id, a->timeout_ms, FALSE, attribute_timer_cb, a);
159 } else if (dampen < 0) {
160 crm_warn("Ignoring invalid delay %s for attribute %s", value, a->id);
161 }
162
163 g_hash_table_replace(attributes, a->id, a);
164 return a;
165 }
166
167
168
169
170
171
172
173
174
175
176 void
177 attrd_client_peer_remove(const char *client_name, xmlNode *xml)
178 {
179 const char *host = crm_element_value(xml, F_ATTRD_HOST);
180
181 if (host) {
182 crm_info("Client %s is requesting all values for %s be removed",
183 client_name, host);
184 send_attrd_message(NULL, xml);
185 } else {
186 crm_info("Ignoring request by client %s to remove all peer values without specifying peer",
187 client_name);
188 }
189 }
190
191
192
193
194
195
196
197
198
199 void
200 attrd_client_update(xmlNode *xml)
201 {
202 attribute_t *a = NULL;
203 char *host = crm_element_value_copy(xml, F_ATTRD_HOST);
204 const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
205 const char *value = crm_element_value(xml, F_ATTRD_VALUE);
206 const char *regex = crm_element_value(xml, F_ATTRD_REGEX);
207
208
209 if ((attr == NULL) && regex) {
210 GHashTableIter aIter;
211 regex_t *r_patt = calloc(1, sizeof(regex_t));
212
213 crm_debug("Setting %s to %s", regex, value);
214 if (regcomp(r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
215 crm_err("Bad regex '%s' for update", regex);
216
217 } else {
218 g_hash_table_iter_init(&aIter, attributes);
219 while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
220 int status = regexec(r_patt, attr, 0, NULL, 0);
221
222 if (status == 0) {
223 crm_trace("Matched %s with %s", attr, regex);
224 crm_xml_add(xml, F_ATTRD_ATTRIBUTE, attr);
225 send_attrd_message(NULL, xml);
226 }
227 }
228 }
229
230 free(host);
231 regfree(r_patt);
232 free(r_patt);
233 return;
234
235 } else if (attr == NULL) {
236 crm_err("Update request did not specify attribute or regular expression");
237 free(host);
238 return;
239 }
240
241 if (host == NULL) {
242 crm_trace("Inferring host");
243 host = strdup(attrd_cluster->uname);
244 crm_xml_add(xml, F_ATTRD_HOST, host);
245 crm_xml_add_int(xml, F_ATTRD_HOST_ID, attrd_cluster->nodeid);
246 }
247
248 a = g_hash_table_lookup(attributes, attr);
249
250
251 if (value) {
252 if (attrd_value_needs_expansion(value)) {
253 int int_value;
254 attribute_value_t *v = NULL;
255
256 if (a) {
257 v = g_hash_table_lookup(a->values, host);
258 }
259 int_value = attrd_expand_value(value, (v? v->current : NULL));
260
261 crm_info("Expanded %s=%s to %d", attr, value, int_value);
262 crm_xml_add_int(xml, F_ATTRD_VALUE, int_value);
263
264
265 value = crm_element_value(xml, F_ATTRD_VALUE);
266 }
267 }
268
269 if ((peer_writer == NULL) && (election_state(writer) != election_in_progress)) {
270 crm_info("Starting an election to determine the writer");
271 election_vote(writer);
272 }
273
274 crm_debug("Broadcasting %s[%s] = %s%s", attr, host, value,
275 ((election_state(writer) == election_won)? " (writer)" : ""));
276
277 free(host);
278
279 send_attrd_message(NULL, xml);
280 }
281
282
283
284
285
286
287
288 void
289 attrd_client_clear_failure(xmlNode *xml)
290 {
291 #if 0
292
293
294
295
296 if (all_peers_support_clear_failure) {
297
298
299
300 send_attrd_message(NULL, xml);
301 return;
302 }
303 #endif
304
305 const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE);
306 const char *op = crm_element_value(xml, F_ATTRD_OPERATION);
307 const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL);
308
309
310 crm_xml_add(xml, F_ATTRD_TASK, ATTRD_OP_UPDATE);
311
312
313
314 if (rsc) {
315 char *pattern;
316
317 if (op == NULL) {
318 pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
319
320 } else {
321 int interval = crm_get_interval(interval_s);
322
323 pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
324 rsc, op, interval);
325 }
326
327 crm_xml_add(xml, F_ATTRD_REGEX, pattern);
328 free(pattern);
329
330 } else {
331 crm_xml_add(xml, F_ATTRD_REGEX, ATTRD_RE_CLEAR_ALL);
332 }
333
334
335 if (crm_element_value(xml, F_ATTRD_ATTRIBUTE)) {
336 crm_xml_replace(xml, F_ATTRD_ATTRIBUTE, NULL);
337 }
338 if (crm_element_value(xml, F_ATTRD_VALUE)) {
339 crm_xml_replace(xml, F_ATTRD_VALUE, NULL);
340 }
341
342 attrd_client_update(xml);
343 }
344
345
346
347
348
349
350
351 void
352 attrd_client_refresh(void)
353 {
354 GHashTableIter iter;
355 attribute_t *a = NULL;
356
357
358
359
360 g_hash_table_iter_init(&iter, attributes);
361 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
362 mainloop_timer_stop(a->timer);
363 }
364
365 crm_info("Updating all attributes");
366 write_attributes(TRUE);
367 }
368
369
370
371
372
373
374
375
376
377
378
379 static xmlNode *build_query_reply(const char *attr, const char *host)
380 {
381 xmlNode *reply = create_xml_node(NULL, __FUNCTION__);
382 attribute_t *a;
383
384 if (reply == NULL) {
385 return NULL;
386 }
387 crm_xml_add(reply, F_TYPE, T_ATTRD);
388 crm_xml_add(reply, F_ATTRD_VERSION, ATTRD_PROTOCOL_VERSION);
389
390
391 a = g_hash_table_lookup(attributes, attr);
392 if (a) {
393 attribute_value_t *v;
394 xmlNode *host_value;
395
396 crm_xml_add(reply, F_ATTRD_ATTRIBUTE, attr);
397
398
399 if (safe_str_eq(host, "localhost")) {
400 host = attrd_cluster->uname;
401 crm_trace("Mapped localhost to %s", host);
402 }
403
404
405 if (host) {
406 v = g_hash_table_lookup(a->values, host);
407 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
408 if (host_value == NULL) {
409 free_xml(reply);
410 return NULL;
411 }
412 crm_xml_add(host_value, F_ATTRD_HOST, host);
413 crm_xml_add(host_value, F_ATTRD_VALUE, (v? v->current : NULL));
414
415
416 } else {
417 GHashTableIter iter;
418
419 g_hash_table_iter_init(&iter, a->values);
420 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
421 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
422 if (host_value == NULL) {
423 free_xml(reply);
424 return NULL;
425 }
426 crm_xml_add(host_value, F_ATTRD_HOST, v->nodename);
427 crm_xml_add(host_value, F_ATTRD_VALUE, v->current);
428 }
429 }
430 }
431 return reply;
432 }
433
434
435
436
437
438
439
440
441
442
443 void
444 attrd_client_query(crm_client_t *client, uint32_t id, uint32_t flags, xmlNode *query)
445 {
446 const char *attr;
447 const char *origin = crm_element_value(query, F_ORIG);
448 ssize_t rc;
449 xmlNode *reply;
450
451 if (origin == NULL) {
452 origin = "unknown client";
453 }
454 crm_debug("Query arrived from %s", origin);
455
456
457 attr = crm_element_value(query, F_ATTRD_ATTRIBUTE);
458 if (attr == NULL) {
459 crm_warn("Ignoring malformed query from %s (no attribute name given)",
460 origin);
461 return;
462 }
463
464
465 reply = build_query_reply(attr, crm_element_value(query, F_ATTRD_HOST));
466 if (reply == NULL) {
467 crm_err("Could not respond to query from %s: could not create XML reply",
468 origin);
469 return;
470 }
471 crm_log_xml_trace(reply, "Reply");
472
473
474 client->request_id = 0;
475 if ((rc = crm_ipcs_send(client, id, reply, flags)) < 0) {
476 crm_err("Could not respond to query from %s: %s (%d)",
477 origin, pcmk_strerror(-rc), -rc);
478 }
479 free_xml(reply);
480 }
481
482
483
484
485
486
487
488
489 static void
490 attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
491 {
492 const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE);
493 const char *host = crm_element_value(xml, F_ATTRD_HOST);
494 const char *op = crm_element_value(xml, F_ATTRD_OPERATION);
495 const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL);
496 int interval = crm_get_interval(interval_s);
497 char *attr = NULL;
498 GHashTableIter iter;
499 regex_t regex;
500
501 if (attrd_failure_regex(®ex, rsc, op, interval) != pcmk_ok) {
502 crm_info("Ignoring invalid request to clear failures for %s",
503 (rsc? rsc : "all resources"));
504 return;
505 }
506
507 crm_xml_add(xml, F_ATTRD_TASK, ATTRD_OP_UPDATE);
508
509
510 if (crm_element_value(xml, F_ATTRD_VALUE)) {
511 crm_xml_replace(xml, F_ATTRD_VALUE, NULL);
512 }
513
514 g_hash_table_iter_init(&iter, attributes);
515 while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
516 if (regexec(®ex, attr, 0, NULL, 0) == 0) {
517 crm_trace("Matched %s when clearing %s",
518 attr, (rsc? rsc : "all resources"));
519 crm_xml_add(xml, F_ATTRD_ATTRIBUTE, attr);
520 attrd_peer_update(peer, xml, host, FALSE);
521 }
522 }
523 regfree(®ex);
524 }
525
526 void
527 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
528 {
529 int peer_state = 0;
530 const char *v = crm_element_value(xml, F_ATTRD_VERSION);
531 const char *op = crm_element_value(xml, F_ATTRD_TASK);
532 const char *election_op = crm_element_value(xml, F_CRM_TASK);
533 const char *host = crm_element_value(xml, F_ATTRD_HOST);
534
535 if(election_op) {
536 enum election_result rc = 0;
537
538 crm_xml_add(xml, F_CRM_HOST_FROM, peer->uname);
539 rc = election_count_vote(writer, xml, TRUE);
540 switch(rc) {
541 case election_start:
542 free(peer_writer);
543 peer_writer = NULL;
544 election_vote(writer);
545 break;
546 case election_lost:
547 free(peer_writer);
548 peer_writer = strdup(peer->uname);
549 break;
550 default:
551 election_check(writer);
552 break;
553 }
554 return;
555
556 } else if(v == NULL) {
557
558 if (safe_str_eq(op, ATTRD_OP_UPDATE)) {
559 const char *name = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
560
561 crm_trace("Compatibility update of %s from %s", name, peer->uname);
562 attrd_peer_update(peer, xml, host, FALSE);
563
564 } else if (safe_str_eq(op, ATTRD_OP_FLUSH)) {
565 const char *name = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
566 attribute_t *a = g_hash_table_lookup(attributes, name);
567
568 if(a) {
569 crm_trace("Compatibility write-out of %s for %s from %s", a->id, op, peer->uname);
570 write_or_elect_attribute(a);
571 }
572
573 } else if (safe_str_eq(op, ATTRD_OP_REFRESH)) {
574 GHashTableIter aIter;
575 attribute_t *a = NULL;
576
577 g_hash_table_iter_init(&aIter, attributes);
578 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
579 crm_trace("Compatibility write-out of %s for %s from %s", a->id, op, peer->uname);
580 write_or_elect_attribute(a);
581 }
582 }
583 }
584
585 crm_element_value_int(xml, F_ATTRD_WRITER, &peer_state);
586 if(election_state(writer) == election_won
587 && peer_state == election_won
588 && safe_str_neq(peer->uname, attrd_cluster->uname)) {
589 crm_notice("Detected another attribute writer: %s", peer->uname);
590 election_vote(writer);
591
592 } else if(peer_state == election_won) {
593 if(peer_writer == NULL) {
594 peer_writer = strdup(peer->uname);
595 crm_notice("Recorded attribute writer: %s", peer->uname);
596
597 } else if(safe_str_neq(peer->uname, peer_writer)) {
598 crm_notice("Recorded new attribute writer: %s (was %s)", peer->uname, peer_writer);
599 free(peer_writer);
600 peer_writer = strdup(peer->uname);
601 }
602 }
603
604 if (safe_str_eq(op, ATTRD_OP_UPDATE) || safe_str_eq(op, ATTRD_OP_UPDATE_BOTH) || safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) {
605 attrd_peer_update(peer, xml, host, FALSE);
606
607 } else if (safe_str_eq(op, ATTRD_OP_SYNC)) {
608 attrd_peer_sync(peer, xml);
609
610 } else if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) {
611 attrd_peer_remove(host, TRUE, peer->uname);
612
613 } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) {
614
615
616
617 attrd_peer_clear_failure(peer, xml);
618
619 } else if (safe_str_eq(op, ATTRD_OP_SYNC_RESPONSE)
620 && safe_str_neq(peer->uname, attrd_cluster->uname)) {
621 xmlNode *child = NULL;
622
623 crm_info("Processing %s from %s", op, peer->uname);
624 for (child = __xml_first_child(xml); child != NULL; child = __xml_next(child)) {
625 host = crm_element_value(child, F_ATTRD_HOST);
626 attrd_peer_update(peer, child, host, TRUE);
627 }
628 }
629 }
630
631 void
632 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
633 {
634 GHashTableIter aIter;
635 GHashTableIter vIter;
636
637 attribute_t *a = NULL;
638 attribute_value_t *v = NULL;
639 xmlNode *sync = create_xml_node(NULL, __FUNCTION__);
640
641 crm_xml_add(sync, F_ATTRD_TASK, ATTRD_OP_SYNC_RESPONSE);
642
643 g_hash_table_iter_init(&aIter, attributes);
644 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
645 g_hash_table_iter_init(&vIter, a->values);
646 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
647 crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
648 build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
649 v->nodename, v->nodeid, v->current);
650 }
651 }
652
653 crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
654 send_attrd_message(peer, sync);
655 free_xml(sync);
656 }
657
658
659
660
661
662
663
664
665
666 void
667 attrd_peer_remove(const char *host, gboolean uncache, const char *source)
668 {
669 attribute_t *a = NULL;
670 GHashTableIter aIter;
671
672 CRM_CHECK(host != NULL, return);
673 crm_notice("Removing all %s attributes for peer %s", host, source);
674
675 g_hash_table_iter_init(&aIter, attributes);
676 while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
677 if(g_hash_table_remove(a->values, host)) {
678 crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
679 }
680 }
681
682 if (uncache) {
683 crm_remote_peer_cache_remove(host);
684 reap_crm_member(0, host);
685 }
686 }
687
688
689
690
691
692
693
694
695
696
697
698 static attribute_value_t *
699 attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
700 {
701 attribute_value_t *v = g_hash_table_lookup(values, host);
702 int is_remote = 0;
703
704 crm_element_value_int(xml, F_ATTRD_IS_REMOTE, &is_remote);
705 if (is_remote) {
706
707
708
709 crm_node_t *dup = crm_find_peer(0, host);
710
711 if (dup && (dup->uuid == NULL)) {
712 reap_crm_member(0, host);
713 }
714
715
716 crm_remote_peer_cache_add(host);
717 }
718
719 if (v == NULL) {
720 v = calloc(1, sizeof(attribute_value_t));
721 CRM_ASSERT(v != NULL);
722
723 v->nodename = strdup(host);
724 CRM_ASSERT(v->nodename != NULL);
725
726 v->is_remote = is_remote;
727 g_hash_table_replace(values, v->nodename, v);
728 }
729 return(v);
730 }
731
732 void
733 attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
734 {
735 bool update_both = FALSE;
736 attribute_t *a;
737 attribute_value_t *v = NULL;
738
739 const char *op = crm_element_value(xml, F_ATTRD_TASK);
740 const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
741 const char *value = crm_element_value(xml, F_ATTRD_VALUE);
742
743 if (attr == NULL) {
744 crm_warn("Could not update attribute: peer did not specify name");
745 return;
746 }
747
748 update_both = ((op == NULL)
749 || safe_str_eq(op, ATTRD_OP_UPDATE_BOTH));
750
751
752 a = g_hash_table_lookup(attributes, attr);
753 if (a == NULL) {
754 if (update_both || safe_str_eq(op, ATTRD_OP_UPDATE)) {
755 a = create_attribute(xml);
756 } else {
757 crm_warn("Could not update %s: attribute not found", attr);
758 return;
759 }
760 }
761
762
763 if (update_both || safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) {
764 const char *dvalue = crm_element_value(xml, F_ATTRD_DAMPEN);
765 int dampen = 0;
766
767 if (dvalue == NULL) {
768 crm_warn("Could not update %s: peer did not specify value for delay",
769 attr);
770 return;
771 }
772
773 dampen = crm_get_msec(dvalue);
774 if (dampen < 0) {
775 crm_warn("Could not update %s: invalid delay value %dms (%s)",
776 attr, dampen, dvalue);
777 return;
778 }
779
780 if (a->timeout_ms != dampen) {
781 mainloop_timer_stop(a->timer);
782 mainloop_timer_del(a->timer);
783 a->timeout_ms = dampen;
784 if (dampen > 0) {
785 a->timer = mainloop_timer_add(attr, a->timeout_ms, FALSE,
786 attribute_timer_cb, a);
787 crm_info("Update attribute %s delay to %dms (%s)",
788 attr, dampen, dvalue);
789 } else {
790 a->timer = NULL;
791 crm_info("Update attribute %s to remove delay", attr);
792 }
793
794
795
796
797 write_or_elect_attribute(a);
798 }
799
800 if (!update_both) {
801 return;
802 }
803 }
804
805
806 if (host == NULL) {
807 GHashTableIter vIter;
808
809 crm_debug("Setting %s for all hosts to %s", attr, value);
810 xml_remove_prop(xml, F_ATTRD_HOST_ID);
811 g_hash_table_iter_init(&vIter, a->values);
812 while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
813 attrd_peer_update(peer, xml, host, filter);
814 }
815 return;
816 }
817
818
819
820 v = attrd_lookup_or_create_value(a->values, host, xml);
821
822 if (filter && safe_str_neq(v->current, value)
823 && safe_str_eq(host, attrd_cluster->uname)) {
824
825 xmlNode *sync = create_xml_node(NULL, __FUNCTION__);
826
827 crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
828 attr, host, v->current, value, peer->uname);
829
830 crm_xml_add(sync, F_ATTRD_TASK, ATTRD_OP_SYNC_RESPONSE);
831 v = g_hash_table_lookup(a->values, host);
832 build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
833 a->is_private, v->nodename, v->nodeid, v->current);
834
835 crm_xml_add_int(sync, F_ATTRD_WRITER, election_state(writer));
836
837
838 send_attrd_message(NULL, sync);
839 free_xml(sync);
840
841 } else if (safe_str_neq(v->current, value)) {
842 crm_info("Setting %s[%s]: %s -> %s from %s",
843 attr, host, v->current, value, peer->uname);
844 free(v->current);
845 v->current = (value? strdup(value) : NULL);
846 a->changed = TRUE;
847
848
849 if (a->timer) {
850 crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
851 mainloop_timer_start(a->timer);
852 } else {
853 write_or_elect_attribute(a);
854 }
855
856 } else {
857 crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
858 }
859
860
861 if ((v->nodeid == 0) && (v->is_remote == FALSE)
862 && (crm_element_value_int(xml, F_ATTRD_HOST_ID, (int*)&v->nodeid) == 0)) {
863
864 crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
865
866 crm_trace("Learned %s has node id %s",
867 known_peer->uname, known_peer->uuid);
868 if (election_state(writer) == election_won) {
869 write_attributes(FALSE);
870 }
871 }
872 }
873
874 void
875 write_or_elect_attribute(attribute_t *a)
876 {
877 enum election_result rc = election_state(writer);
878 if(rc == election_won) {
879 write_attribute(a);
880
881 } else if(rc == election_in_progress) {
882 crm_trace("Election in progress to determine who will write out %s", a->id);
883
884 } else if(peer_writer == NULL) {
885 crm_info("Starting an election to determine who will write out %s", a->id);
886 election_vote(writer);
887
888 } else {
889 crm_trace("%s will write out %s, we are in state %d", peer_writer, a->id, rc);
890 }
891 }
892
893 gboolean
894 attrd_election_cb(gpointer user_data)
895 {
896 free(peer_writer);
897 peer_writer = strdup(attrd_cluster->uname);
898
899
900 attrd_peer_sync(NULL, NULL);
901
902
903 write_attributes(TRUE);
904 return FALSE;
905 }
906
907
908 void
909 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
910 {
911 if ((kind == crm_status_nstate) || (kind == crm_status_rstate)) {
912 if (safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
913
914
915
916 if ((election_state(writer) == election_won)
917 && !is_set(peer->flags, crm_remote_node)) {
918 attrd_peer_sync(peer, NULL);
919 }
920 } else {
921
922 attrd_peer_remove(peer->uname, FALSE, "loss");
923 if (peer_writer && safe_str_eq(peer->uname, peer_writer)) {
924 free(peer_writer);
925 peer_writer = NULL;
926 crm_notice("Lost attribute writer %s", peer->uname);
927 }
928 }
929 }
930 }
931
932 static void
933 attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
934 {
935 int level = LOG_ERR;
936 GHashTableIter iter;
937 const char *peer = NULL;
938 attribute_value_t *v = NULL;
939
940 char *name = user_data;
941 attribute_t *a = g_hash_table_lookup(attributes, name);
942
943 if(a == NULL) {
944 crm_info("Attribute %s no longer exists", name);
945 goto done;
946 }
947
948 a->update = 0;
949 if (rc == pcmk_ok && call_id < 0) {
950 rc = call_id;
951 }
952
953 switch (rc) {
954 case pcmk_ok:
955 level = LOG_INFO;
956 last_cib_op_done = call_id;
957 break;
958 case -pcmk_err_diff_failed:
959 case -ETIME:
960 case -ENXIO:
961
962
963 level = LOG_WARNING;
964 break;
965 }
966
967 do_crm_log(level, "Update %d for %s: %s (%d)", call_id, name, pcmk_strerror(rc), rc);
968
969 g_hash_table_iter_init(&iter, a->values);
970 while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
971 do_crm_log(level, "Update %d for %s[%s]=%s: %s (%d)", call_id, a->id, peer, v->requested, pcmk_strerror(rc), rc);
972 free(v->requested);
973 v->requested = NULL;
974 if (rc != pcmk_ok) {
975 a->changed = TRUE;
976 }
977 }
978 done:
979 if(a && a->changed && election_state(writer) == election_won) {
980 write_attribute(a);
981 }
982 }
983
984 void
985 write_attributes(bool all)
986 {
987 GHashTableIter iter;
988 attribute_t *a = NULL;
989
990 crm_debug("Writing out %s attributes", all? "all" : "changed");
991 g_hash_table_iter_init(&iter, attributes);
992 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
993 if (!all && a->unknown_peer_uuids) {
994
995 a->changed = TRUE;
996 }
997
998 if(all || a->changed) {
999 write_attribute(a);
1000 } else {
1001 crm_debug("Skipping unchanged attribute %s", a->id);
1002 }
1003 }
1004 }
1005
1006 static void
1007 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
1008 {
1009 const char *set = NULL;
1010 xmlNode *xml_obj = NULL;
1011
1012 xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
1013 crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1014
1015 xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
1016 crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1017
1018 xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
1019 if (a->set) {
1020 crm_xml_set_id(xml_obj, "%s", a->set);
1021 } else {
1022 crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
1023 }
1024 set = ID(xml_obj);
1025
1026 xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
1027 if (a->uuid) {
1028 crm_xml_set_id(xml_obj, "%s", a->uuid);
1029 } else {
1030 crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
1031 }
1032 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
1033
1034 if(value) {
1035 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
1036
1037 } else {
1038 crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
1039 crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
1040 }
1041 }
1042
1043 static void
1044 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
1045 {
1046 attribute_value_t *a_v = NULL;
1047 a_v = calloc(1, sizeof(attribute_value_t));
1048 CRM_ASSERT(a_v != NULL);
1049
1050 a_v->nodeid = v->nodeid;
1051 a_v->nodename = strdup(v->nodename);
1052
1053 if (v->current != NULL) {
1054 a_v->current = strdup(v->current);
1055 }
1056
1057 g_hash_table_replace(t, a_v->nodename, a_v);
1058 }
1059
1060 static void
1061 send_alert_attributes_value(attribute_t *a, GHashTable *t)
1062 {
1063 int rc = 0;
1064 attribute_value_t *at = NULL;
1065 GHashTableIter vIter;
1066
1067 g_hash_table_iter_init(&vIter, t);
1068
1069 while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
1070 rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
1071 a->id, at->current);
1072 crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
1073 a->id, at->nodename, at->current, at->nodeid, rc);
1074 }
1075 }
1076
1077 void
1078 write_attribute(attribute_t *a)
1079 {
1080 int private_updates = 0, cib_updates = 0;
1081 xmlNode *xml_top = NULL;
1082 attribute_value_t *v = NULL;
1083 GHashTableIter iter;
1084 enum cib_call_options flags = cib_quorum_override;
1085 GHashTable *alert_attribute_value = NULL;
1086
1087 if (a == NULL) {
1088 return;
1089 }
1090
1091
1092 if (!a->is_private) {
1093
1094
1095 if (the_cib == NULL) {
1096 crm_info("Write out of '%s' delayed: cib not connected", a->id);
1097 return;
1098
1099 } else if (a->update && (a->update < last_cib_op_done)) {
1100 crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
1101
1102 } else if (a->update) {
1103 crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
1104 return;
1105
1106 } else if (mainloop_timer_running(a->timer)) {
1107 crm_info("Write out of '%s' delayed: timer is running", a->id);
1108 return;
1109 }
1110
1111
1112 xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
1113 }
1114
1115
1116 a->changed = FALSE;
1117
1118
1119 a->unknown_peer_uuids = FALSE;
1120
1121
1122 alert_attribute_value = g_hash_table_new_full(crm_strcase_hash,
1123 crm_strcase_equal, NULL,
1124 free_attribute_value);
1125
1126
1127 g_hash_table_iter_init(&iter, a->values);
1128 while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
1129 crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
1130
1131
1132 if (peer == NULL) {
1133 crm_notice("Update error (peer not found): %s[%s]=%s failed (host=%p)",
1134 a->id, v->nodename, v->current, peer);
1135 continue;
1136 }
1137
1138
1139 if (peer->id && (v->nodeid == 0)) {
1140 crm_trace("Updating value's nodeid");
1141 v->nodeid = peer->id;
1142 }
1143
1144
1145 if (a->is_private) {
1146 private_updates++;
1147 continue;
1148 }
1149
1150
1151 if (peer->uuid == NULL) {
1152 a->unknown_peer_uuids = TRUE;
1153 crm_notice("Update %s[%s]=%s postponed: unknown peer UUID, will retry if UUID is learned",
1154 a->id, v->nodename, v->current, peer);
1155 continue;
1156 }
1157
1158
1159 crm_debug("Update: %s[%s]=%s (%s %u %u %s)", a->id, v->nodename,
1160 v->current, peer->uuid, peer->id, v->nodeid, peer->uname);
1161 build_update_element(xml_top, a, peer->uuid, v->current);
1162 cib_updates++;
1163
1164
1165 set_alert_attribute_value(alert_attribute_value, v);
1166
1167 free(v->requested);
1168 v->requested = NULL;
1169 if (v->current) {
1170 v->requested = strdup(v->current);
1171 } else {
1172
1173
1174
1175 flags |= cib_mixed_update|cib_scope_local;
1176 }
1177 }
1178
1179 if (private_updates) {
1180 crm_info("Processed %d private change%s for %s, id=%s, set=%s",
1181 private_updates, ((private_updates == 1)? "" : "s"),
1182 a->id, (a->uuid? a->uuid : "<n/a>"), a->set);
1183 }
1184 if (cib_updates) {
1185 crm_log_xml_trace(xml_top, __FUNCTION__);
1186
1187 a->update = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL,
1188 flags, a->user);
1189
1190 crm_info("Sent update %d with %d changes for %s, id=%s, set=%s",
1191 a->update, cib_updates, a->id, (a->uuid? a->uuid : "<n/a>"), a->set);
1192
1193 the_cib->cmds->register_callback_full(the_cib, a->update, 120, FALSE,
1194 strdup(a->id),
1195 "attrd_cib_callback",
1196 attrd_cib_callback, free);
1197
1198 send_alert_attributes_value(a, alert_attribute_value);
1199
1200 }
1201
1202 g_hash_table_destroy(alert_attribute_value);
1203 free_xml(xml_top);
1204 }