root/daemons/attrd/attrd_corosync.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. attrd_confirmation
  2. attrd_peer_message
  3. attrd_cpg_dispatch
  4. attrd_cpg_destroy
  5. attrd_broadcast_value
  6. attrd_peer_change_cb
  7. update_attr_on_host
  8. attrd_peer_update_one
  9. broadcast_unseen_local_values
  10. attrd_cluster_connect
  11. attrd_peer_clear_failure
  12. attrd_peer_sync_response
  13. attrd_peer_remove
  14. attrd_peer_sync
  15. attrd_peer_update

   1 /*
   2  * Copyright 2013-2025 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <errno.h>
  13 #include <stdbool.h>
  14 #include <stdint.h>
  15 #include <stdlib.h>
  16 
  17 #include <crm/cluster.h>
  18 #include <crm/cluster/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 
  24 #include "pacemaker-attrd.h"
  25 
  26 static xmlNode *
  27 attrd_confirmation(int callid)
     /* [previous][next][first][last][top][bottom][index][help] */
  28 {
  29     xmlNode *node = pcmk__xe_create(NULL, __func__);
  30 
  31     crm_xml_add(node, PCMK__XA_T, PCMK__VALUE_ATTRD);
  32     crm_xml_add(node, PCMK__XA_SRC, pcmk__cluster_local_node_name());
  33     crm_xml_add(node, PCMK_XA_TASK, PCMK__ATTRD_CMD_CONFIRM);
  34     crm_xml_add_int(node, PCMK__XA_CALL_ID, callid);
  35 
  36     return node;
  37 }
  38 
  39 static void
  40 attrd_peer_message(pcmk__node_status_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42     const char *election_op = crm_element_value(xml, PCMK__XA_CRM_TASK);
  43 
  44     if (election_op) {
  45         attrd_handle_election_op(peer, xml);
  46         return;
  47     }
  48 
  49     if (attrd_shutting_down(false)) {
  50         /* If we're shutting down, we want to continue responding to election
  51          * ops as long as we're a cluster member (because our vote may be
  52          * needed). Ignore all other messages.
  53          */
  54         return;
  55 
  56     } else {
  57         pcmk__request_t request = {
  58             .ipc_client     = NULL,
  59             .ipc_id         = 0,
  60             .ipc_flags      = 0,
  61             .peer           = peer->name,
  62             .xml            = xml,
  63             .call_options   = 0,
  64             .result         = PCMK__UNKNOWN_RESULT,
  65         };
  66 
  67         request.op = crm_element_value_copy(request.xml, PCMK_XA_TASK);
  68         CRM_CHECK(request.op != NULL, return);
  69 
  70         attrd_handle_request(&request);
  71 
  72         /* Having finished handling the request, check to see if the originating
  73          * peer requested confirmation.  If so, send that confirmation back now.
  74          */
  75         if (pcmk__xe_attr_is_true(xml, PCMK__XA_CONFIRM) &&
  76             !pcmk__str_eq(request.op, PCMK__ATTRD_CMD_CONFIRM, pcmk__str_none)) {
  77             int callid = 0;
  78             xmlNode *reply = NULL;
  79 
  80             /* Add the confirmation ID for the message we are confirming to the
  81              * response so the originating peer knows what they're a confirmation
  82              * for.
  83              */
  84             crm_element_value_int(xml, PCMK__XA_CALL_ID, &callid);
  85             reply = attrd_confirmation(callid);
  86 
  87             /* And then send the confirmation back to the originating peer.  This
  88              * ends up right back in this same function (attrd_peer_message) on the
  89              * peer where it will have to do something with a PCMK__XA_CONFIRM type
  90              * message.
  91              */
  92             crm_debug("Sending %s a confirmation", peer->name);
  93             attrd_send_message(peer, reply, false);
  94             pcmk__xml_free(reply);
  95         }
  96 
  97         pcmk__reset_request(&request);
  98     }
  99 }
 100 
 101 static void
 102 attrd_cpg_dispatch(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
 103                  const struct cpg_name *groupName,
 104                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 105 {
 106     xmlNode *xml = NULL;
 107     const char *from = NULL;
 108     char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from);
 109 
 110     if(data == NULL) {
 111         return;
 112     }
 113 
 114     xml = pcmk__xml_parse(data);
 115 
 116     if (xml == NULL) {
 117         crm_err("Bad message received from %s[%" PRIu32 "]: '%.120s'",
 118                 from, nodeid, data);
 119     } else {
 120         attrd_peer_message(pcmk__get_node(nodeid, from, NULL,
 121                                           pcmk__node_search_cluster_member),
 122                            xml);
 123     }
 124 
 125     pcmk__xml_free(xml);
 126     free(data);
 127 }
 128 
 129 static void
 130 attrd_cpg_destroy(gpointer unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     if (attrd_shutting_down(false)) {
 133         crm_info("Disconnected from Corosync process group");
 134 
 135     } else {
 136         crm_crit("Lost connection to Corosync process group, shutting down");
 137         attrd_exit_status = CRM_EX_DISCONNECT;
 138         attrd_shutdown(0);
 139     }
 140 }
 141 
 142 /*!
 143  * \internal
 144  * \brief Broadcast an update for a single attribute value
 145  *
 146  * \param[in] a  Attribute to broadcast
 147  * \param[in] v  Attribute value to broadcast
 148  */
 149 void
 150 attrd_broadcast_value(const attribute_t *a, const attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     xmlNode *op = pcmk__xe_create(NULL, PCMK_XE_OP);
 153 
 154     crm_xml_add(op, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 155     attrd_add_value_xml(op, a, v, false);
 156     attrd_send_message(NULL, op, false);
 157     pcmk__xml_free(op);
 158 }
 159 
 160 #define state_text(state) pcmk__s((state), "in unknown state")
 161 
 162 static void
 163 attrd_peer_change_cb(enum pcmk__node_update kind, pcmk__node_status_t *peer,
     /* [previous][next][first][last][top][bottom][index][help] */
 164                      const void *data)
 165 {
 166     bool gone = false;
 167     bool is_remote = pcmk_is_set(peer->flags, pcmk__node_status_remote);
 168 
 169     switch (kind) {
 170         case pcmk__node_update_name:
 171             crm_debug("%s node %s[%" PRIu32 "] is now %s",
 172                       (is_remote? "Remote" : "Cluster"),
 173                       pcmk__s(peer->name, "unknown"), peer->cluster_layer_id,
 174                       state_text(peer->state));
 175             break;
 176 
 177         case pcmk__node_update_processes:
 178             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
 179                 gone = true;
 180             }
 181             crm_debug("Node %s[%" PRIu32 "] is %s a peer",
 182                       pcmk__s(peer->name, "unknown"), peer->cluster_layer_id,
 183                       (gone? "no longer" : "now"));
 184             break;
 185 
 186         case pcmk__node_update_state:
 187             crm_debug("%s node %s[%" PRIu32 "] is now %s (was %s)",
 188                       (is_remote? "Remote" : "Cluster"),
 189                       pcmk__s(peer->name, "unknown"), peer->cluster_layer_id,
 190                       state_text(peer->state), state_text(data));
 191             if (pcmk__str_eq(peer->state, PCMK_VALUE_MEMBER, pcmk__str_none)) {
 192                 /* If we're the writer, send new peers a list of all attributes
 193                  * (unless it's a remote node, which doesn't run its own attrd)
 194                  */
 195                 if (!is_remote) {
 196                    if (attrd_election_won()) {
 197                        attrd_peer_sync(peer);
 198 
 199                    } else {
 200                        // Anyway send a message so that the peer learns our name
 201                        attrd_send_protocol(peer);
 202                    }
 203                 }
 204 
 205             } else {
 206                 // Remove all attribute values associated with lost nodes
 207                 if (peer->name != NULL) {
 208                     attrd_peer_remove(peer->name, false, "loss");
 209                 }
 210                 gone = true;
 211             }
 212             break;
 213     }
 214 
 215     // Remove votes from cluster nodes that leave, in case election in progress
 216     if (gone && !is_remote && peer->name != NULL) {
 217         attrd_remove_voter(peer);
 218         attrd_remove_peer_protocol_ver(peer->name);
 219         attrd_do_not_expect_from_peer(peer->name);
 220     }
 221 }
 222 
 223 #define readable_value(rv_v) pcmk__s((rv_v)->current, "(unset)")
 224 
 225 #define readable_peer(p)    \
 226     (((p) == NULL)? "all peers" : pcmk__s((p)->name, "unknown peer"))
 227 
 228 static void
 229 update_attr_on_host(attribute_t *a, const pcmk__node_status_t *peer,
     /* [previous][next][first][last][top][bottom][index][help] */
 230                     const xmlNode *xml, const char *attr, const char *value,
 231                     const char *host, bool filter)
 232 {
 233     int is_remote = 0;
 234     bool changed = false;
 235     attribute_value_t *v = NULL;
 236     const char *prev_xml_id = NULL;
 237     const char *node_xml_id = crm_element_value(xml, PCMK__XA_ATTR_HOST_ID);
 238 
 239     // Create entry for value if not already existing
 240     v = g_hash_table_lookup(a->values, host);
 241     if (v == NULL) {
 242         v = pcmk__assert_alloc(1, sizeof(attribute_value_t));
 243 
 244         v->nodename = pcmk__str_copy(host);
 245         g_hash_table_replace(a->values, v->nodename, v);
 246     }
 247 
 248     /* If update doesn't contain the node XML ID, fall back to any previously
 249      * known value (for logging)
 250      */
 251     prev_xml_id = attrd_get_node_xml_id(v->nodename);
 252     if (node_xml_id == NULL) {
 253         node_xml_id = prev_xml_id;
 254     }
 255 
 256     // If value is for a Pacemaker Remote node, remember that
 257     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 258     if (is_remote) {
 259         attrd_set_value_flags(v, attrd_value_remote);
 260         pcmk__assert(pcmk__cluster_lookup_remote_node(host) != NULL);
 261     }
 262 
 263     // Check whether the value changed
 264     changed = !pcmk__str_eq(v->current, value, pcmk__str_casei);
 265 
 266     if (changed && filter
 267         && pcmk__str_eq(host, attrd_cluster->priv->node_name,
 268                         pcmk__str_casei)) {
 269         /* Broadcast the local value for an attribute that differs from the
 270          * value provided in a peer's attribute synchronization response. This
 271          * ensures a node's values for itself take precedence and all peers are
 272          * kept in sync.
 273          */
 274         v = g_hash_table_lookup(a->values, attrd_cluster->priv->node_name);
 275         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 276                    attr, host, readable_value(v), value, peer->name);
 277         attrd_broadcast_value(a, v);
 278 
 279     } else if (changed) {
 280         crm_notice("Setting %s[%s]%s%s: %s -> %s "
 281                    QB_XS " from %s with %s write delay and node XML ID %s",
 282                    attr, host, a->set_type ? " in " : "",
 283                    pcmk__s(a->set_type, ""), readable_value(v),
 284                    pcmk__s(value, "(unset)"), peer->name,
 285                    (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms),
 286                    pcmk__s(node_xml_id, "unknown"));
 287         pcmk__str_update(&v->current, value);
 288         attrd_set_attr_flags(a, attrd_attr_changed);
 289 
 290         if (pcmk__str_eq(host, attrd_cluster->priv->node_name, pcmk__str_casei)
 291             && pcmk__str_eq(attr, PCMK__NODE_ATTR_SHUTDOWN, pcmk__str_none)) {
 292 
 293             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 294                 attrd_set_requesting_shutdown();
 295 
 296             } else {
 297                 attrd_clear_requesting_shutdown();
 298             }
 299         }
 300 
 301         // Write out new value or start dampening timer
 302         if (a->timeout_ms && a->timer) {
 303             crm_trace("Delaying write of %s %s for dampening",
 304                       attr, pcmk__readable_interval(a->timeout_ms));
 305             mainloop_timer_start(a->timer);
 306         } else {
 307             attrd_write_or_elect_attribute(a);
 308         }
 309 
 310     } else {
 311         int is_force_write = 0;
 312 
 313         crm_element_value_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE,
 314                               &is_force_write);
 315 
 316         if (is_force_write == 1 && a->timeout_ms && a->timer) {
 317             /* Save forced writing and set change flag. */
 318             /* The actual attribute is written by Writer after election. */
 319             crm_trace("%s[%s] from %s is unchanged (%s), forcing write",
 320                       attr, host, peer->name, pcmk__s(value, "unset"));
 321             attrd_set_attr_flags(a, attrd_attr_force_write);
 322         } else {
 323             crm_trace("%s[%s] from %s is unchanged (%s)",
 324                       attr, host, peer->name, pcmk__s(value, "unset"));
 325         }
 326     }
 327 
 328     // This allows us to later detect local values that peer doesn't know about
 329     attrd_set_value_flags(v, attrd_value_from_peer);
 330 
 331     // Remember node's XML ID if we're just learning it
 332     if ((node_xml_id != NULL)
 333         && !pcmk__str_eq(node_xml_id, prev_xml_id, pcmk__str_none)) {
 334         // Remember node's name in case unknown in the membership cache
 335         pcmk__node_status_t *known_peer =
 336             pcmk__get_node(0, host, node_xml_id,
 337                            pcmk__node_search_cluster_member);
 338 
 339         crm_trace("Learned %s[%s] node XML ID is %s (was %s)",
 340                   a->id, known_peer->name, node_xml_id,
 341                   pcmk__s(prev_xml_id, "unknown"));
 342         attrd_set_node_xml_id(v->nodename, node_xml_id);
 343         if (attrd_election_won()) {
 344             // In case we couldn't write a value missing the XML ID before
 345             attrd_write_attributes(attrd_write_changed);
 346         }
 347     }
 348 }
 349 
 350 static void
 351 attrd_peer_update_one(const pcmk__node_status_t *peer, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 352                       bool filter)
 353 {
 354     attribute_t *a = NULL;
 355     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 356     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 357     const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 358 
 359     if (attr == NULL) {
 360         crm_warn("Could not update attribute: peer did not specify name");
 361         return;
 362     }
 363 
 364     a = attrd_populate_attribute(xml, attr);
 365     if (a == NULL) {
 366         return;
 367     }
 368 
 369     if (host == NULL) {
 370         // If no host was specified, update all hosts
 371         GHashTableIter vIter;
 372 
 373         crm_debug("Setting %s for all hosts to %s", attr, value);
 374         pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_HOST_ID);
 375         g_hash_table_iter_init(&vIter, a->values);
 376 
 377         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 378             update_attr_on_host(a, peer, xml, attr, value, host, filter);
 379         }
 380 
 381     } else {
 382         // Update attribute value for the given host
 383         update_attr_on_host(a, peer, xml, attr, value, host, filter);
 384     }
 385 
 386     /* If this is a message from some attrd instance broadcasting its protocol
 387      * version, check to see if it's a new minimum version.
 388      */
 389     if (pcmk__str_eq(attr, CRM_ATTR_PROTOCOL, pcmk__str_none)) {
 390         attrd_update_minimum_protocol_ver(peer->name, value);
 391     }
 392 }
 393 
 394 static void
 395 broadcast_unseen_local_values(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     GHashTableIter aIter;
 398     GHashTableIter vIter;
 399     attribute_t *a = NULL;
 400     attribute_value_t *v = NULL;
 401     xmlNode *sync = NULL;
 402 
 403     g_hash_table_iter_init(&aIter, attributes);
 404     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 405 
 406         g_hash_table_iter_init(&vIter, a->values);
 407         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 408 
 409             if (!pcmk_is_set(v->flags, attrd_value_from_peer)
 410                 && pcmk__str_eq(v->nodename, attrd_cluster->priv->node_name,
 411                                 pcmk__str_casei)) {
 412                 crm_trace("* %s[%s]='%s' is local-only",
 413                           a->id, v->nodename, readable_value(v));
 414                 if (sync == NULL) {
 415                     sync = pcmk__xe_create(NULL, __func__);
 416                     crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 417                 }
 418                 attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
 419             }
 420         }
 421     }
 422 
 423     if (sync != NULL) {
 424         crm_debug("Broadcasting local-only values");
 425         attrd_send_message(NULL, sync, false);
 426         pcmk__xml_free(sync);
 427     }
 428 }
 429 
 430 int
 431 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 432 {
 433     int rc = pcmk_rc_ok;
 434 
 435     attrd_cluster = pcmk_cluster_new();
 436 
 437     pcmk_cluster_set_destroy_fn(attrd_cluster, attrd_cpg_destroy);
 438     pcmk_cpg_set_deliver_fn(attrd_cluster, attrd_cpg_dispatch);
 439     pcmk_cpg_set_confchg_fn(attrd_cluster, pcmk__cpg_confchg_cb);
 440 
 441     pcmk__cluster_set_status_callback(&attrd_peer_change_cb);
 442 
 443     rc = pcmk_cluster_connect(attrd_cluster);
 444     rc = pcmk_rc2legacy(rc);
 445     if (rc != pcmk_ok) {
 446         crm_err("Cluster connection failed");
 447         return rc;
 448     }
 449     return pcmk_ok;
 450 }
 451 
 452 void
 453 attrd_peer_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455     xmlNode *xml = request->xml;
 456     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 457     const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 458     const char *op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION);
 459     const char *interval_spec = crm_element_value(xml,
 460                                                   PCMK__XA_ATTR_CLEAR_INTERVAL);
 461     guint interval_ms = 0U;
 462     char *attr = NULL;
 463     GHashTableIter iter;
 464     regex_t regex;
 465 
 466     pcmk__node_status_t *peer =
 467         pcmk__get_node(0, request->peer, NULL,
 468                        pcmk__node_search_cluster_member);
 469 
 470     pcmk_parse_interval_spec(interval_spec, &interval_ms);
 471 
 472     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 473         crm_info("Ignoring invalid request to clear failures for %s",
 474                  pcmk__s(rsc, "all resources"));
 475         return;
 476     }
 477 
 478     crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 479 
 480     /* Make sure value is not set, so we delete */
 481     pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE);
 482 
 483     g_hash_table_iter_init(&iter, attributes);
 484     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 485         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 486             crm_trace("Matched %s when clearing %s",
 487                       attr, pcmk__s(rsc, "all resources"));
 488             crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 489             attrd_peer_update(peer, xml, host, false);
 490         }
 491     }
 492     regfree(&regex);
 493 }
 494 
 495 /*!
 496  * \internal
 497  * \brief Load attributes from a peer sync response
 498  *
 499  * \param[in]     peer      Peer that sent sync response
 500  * \param[in]     peer_won  Whether peer is the attribute writer
 501  * \param[in,out] xml       Request XML
 502  */
 503 void
 504 attrd_peer_sync_response(const pcmk__node_status_t *peer, bool peer_won,
     /* [previous][next][first][last][top][bottom][index][help] */
 505                          xmlNode *xml)
 506 {
 507     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 508              peer->name);
 509 
 510     if (peer_won) {
 511         /* Initialize the "seen" flag for all attributes to cleared, so we can
 512          * detect attributes that local node has but the writer doesn't.
 513          */
 514         attrd_clear_value_seen();
 515     }
 516 
 517     // Process each attribute update in the sync response
 518     for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL);
 519          child != NULL; child = pcmk__xe_next(child, NULL)) {
 520 
 521         attrd_peer_update(peer, child,
 522                           crm_element_value(child, PCMK__XA_ATTR_HOST), true);
 523     }
 524 
 525     if (peer_won) {
 526         /* If any attributes are still not marked as seen, the writer doesn't
 527          * know about them, so send all peers an update with them.
 528          */
 529         broadcast_unseen_local_values();
 530     }
 531 }
 532 
 533 /*!
 534  * \internal
 535  * \brief Remove all attributes and optionally peer cache entries for a node
 536  *
 537  * \param[in] host     Name of node to purge
 538  * \param[in] uncache  If true, remove node from peer caches
 539  * \param[in] source   Who requested removal (only used for logging)
 540  */
 541 void
 542 attrd_peer_remove(const char *host, bool uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 543 {
 544     attribute_t *a = NULL;
 545     GHashTableIter aIter;
 546 
 547     CRM_CHECK(host != NULL, return);
 548     crm_notice("Removing all %s attributes for node %s "
 549                QB_XS " %s reaping node from cache",
 550                host, source, (uncache? "and" : "without"));
 551 
 552     g_hash_table_iter_init(&aIter, attributes);
 553     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 554         if(g_hash_table_remove(a->values, host)) {
 555             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 556         }
 557     }
 558 
 559     if (uncache) {
 560         pcmk__purge_node_from_cache(host, 0);
 561         attrd_forget_node_xml_id(host);
 562     }
 563 }
 564 
 565 /*!
 566  * \internal
 567  * \brief Send all known attributes and values to a peer
 568  *
 569  * \param[in] peer  Peer to send sync to (if NULL, broadcast to all peers)
 570  */
 571 void
 572 attrd_peer_sync(pcmk__node_status_t *peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 573 {
 574     GHashTableIter aIter;
 575     GHashTableIter vIter;
 576 
 577     attribute_t *a = NULL;
 578     attribute_value_t *v = NULL;
 579     xmlNode *sync = pcmk__xe_create(NULL, __func__);
 580 
 581     crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 582 
 583     g_hash_table_iter_init(&aIter, attributes);
 584     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 585         g_hash_table_iter_init(&vIter, a->values);
 586         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 587             crm_debug("Syncing %s[%s]='%s' to %s",
 588                       a->id, v->nodename, readable_value(v),
 589                       readable_peer(peer));
 590             attrd_add_value_xml(sync, a, v, false);
 591         }
 592     }
 593 
 594     crm_debug("Syncing values to %s", readable_peer(peer));
 595     attrd_send_message(peer, sync, false);
 596     pcmk__xml_free(sync);
 597 }
 598 
 599 void
 600 attrd_peer_update(const pcmk__node_status_t *peer, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 601                   const char *host, bool filter)
 602 {
 603     bool handle_sync_point = false;
 604 
 605     CRM_CHECK((peer != NULL) && (xml != NULL), return);
 606     if (xml->children != NULL) {
 607         for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL, NULL);
 608              child != NULL; child = pcmk__xe_next(child, PCMK_XE_OP)) {
 609 
 610             pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
 611             attrd_peer_update_one(peer, child, filter);
 612 
 613             if (attrd_request_has_sync_point(child)) {
 614                 handle_sync_point = true;
 615             }
 616         }
 617 
 618     } else {
 619         attrd_peer_update_one(peer, xml, filter);
 620 
 621         if (attrd_request_has_sync_point(xml)) {
 622             handle_sync_point = true;
 623         }
 624     }
 625 
 626     /* If the update XML specified that the client wanted to wait for a sync
 627      * point, process that now.
 628      */
 629     if (handle_sync_point) {
 630         crm_trace("Hit local sync point for attribute update");
 631         attrd_ack_waitlist_clients(attrd_sync_point_local, xml);
 632     }
 633 }

/* [previous][next][first][last][top][bottom][index][help] */