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. record_peer_nodeid
  8. update_attr_on_host
  9. attrd_peer_update_one
  10. broadcast_unseen_local_values
  11. attrd_cluster_connect
  12. attrd_peer_clear_failure
  13. attrd_peer_sync_response
  14. attrd_peer_remove
  15. attrd_peer_sync
  16. attrd_peer_update

   1 /*
   2  * Copyright 2013-2024 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[%u]: '%.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 is now %s",
 172                       (is_remote? "Remote" : "Cluster"),
 173                       peer->name, state_text(peer->state));
 174             break;
 175 
 176         case pcmk__node_update_processes:
 177             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
 178                 gone = true;
 179             }
 180             crm_debug("Node %s is %s a peer",
 181                       peer->name, (gone? "no longer" : "now"));
 182             break;
 183 
 184         case pcmk__node_update_state:
 185             crm_debug("%s node %s is now %s (was %s)",
 186                       (is_remote? "Remote" : "Cluster"),
 187                       peer->name, state_text(peer->state), state_text(data));
 188             if (pcmk__str_eq(peer->state, PCMK_VALUE_MEMBER, pcmk__str_none)) {
 189                 /* If we're the writer, send new peers a list of all attributes
 190                  * (unless it's a remote node, which doesn't run its own attrd)
 191                  */
 192                 if (attrd_election_won()
 193                     && !pcmk_is_set(peer->flags, pcmk__node_status_remote)) {
 194                     attrd_peer_sync(peer);
 195                 }
 196             } else {
 197                 // Remove all attribute values associated with lost nodes
 198                 attrd_peer_remove(peer->name, false, "loss");
 199                 gone = true;
 200             }
 201             break;
 202     }
 203 
 204     // Remove votes from cluster nodes that leave, in case election in progress
 205     if (gone && !is_remote) {
 206         attrd_remove_voter(peer);
 207         attrd_remove_peer_protocol_ver(peer->name);
 208         attrd_do_not_expect_from_peer(peer->name);
 209     }
 210 }
 211 
 212 static void
 213 record_peer_nodeid(attribute_value_t *v, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 214 {
 215     pcmk__node_status_t *known_peer =
 216         pcmk__get_node(v->nodeid, host, NULL, pcmk__node_search_cluster_member);
 217 
 218     crm_trace("Learned %s has node id %s",
 219               known_peer->name, known_peer->xml_id);
 220     if (attrd_election_won()) {
 221         attrd_write_attributes(attrd_write_changed);
 222     }
 223 }
 224 
 225 #define readable_value(rv_v) pcmk__s((rv_v)->current, "(unset)")
 226 
 227 #define readable_peer(p)    \
 228     (((p) == NULL)? "all peers" : pcmk__s((p)->name, "unknown peer"))
 229 
 230 static void
 231 update_attr_on_host(attribute_t *a, const pcmk__node_status_t *peer,
     /* [previous][next][first][last][top][bottom][index][help] */
 232                     const xmlNode *xml, const char *attr, const char *value,
 233                     const char *host, bool filter)
 234 {
 235     int is_remote = 0;
 236     bool changed = false;
 237     attribute_value_t *v = NULL;
 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 value is for a Pacemaker Remote node, remember that
 249     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 250     if (is_remote) {
 251         attrd_set_value_flags(v, attrd_value_remote);
 252         pcmk__assert(pcmk__cluster_lookup_remote_node(host) != NULL);
 253     }
 254 
 255     // Check whether the value changed
 256     changed = !pcmk__str_eq(v->current, value, pcmk__str_casei);
 257 
 258     if (changed && filter
 259         && pcmk__str_eq(host, attrd_cluster->priv->node_name,
 260                         pcmk__str_casei)) {
 261         /* Broadcast the local value for an attribute that differs from the
 262          * value provided in a peer's attribute synchronization response. This
 263          * ensures a node's values for itself take precedence and all peers are
 264          * kept in sync.
 265          */
 266         v = g_hash_table_lookup(a->values, attrd_cluster->priv->node_name);
 267         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 268                    attr, host, readable_value(v), value, peer->name);
 269         attrd_broadcast_value(a, v);
 270 
 271     } else if (changed) {
 272         crm_notice("Setting %s[%s]%s%s: %s -> %s "
 273                    QB_XS " from %s with %s write delay",
 274                    attr, host, a->set_type ? " in " : "",
 275                    pcmk__s(a->set_type, ""), readable_value(v),
 276                    pcmk__s(value, "(unset)"), peer->name,
 277                    (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
 278         pcmk__str_update(&v->current, value);
 279         attrd_set_attr_flags(a, attrd_attr_changed);
 280 
 281         if (pcmk__str_eq(host, attrd_cluster->priv->node_name, pcmk__str_casei)
 282             && pcmk__str_eq(attr, PCMK__NODE_ATTR_SHUTDOWN, pcmk__str_none)) {
 283 
 284             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 285                 attrd_set_requesting_shutdown();
 286 
 287             } else {
 288                 attrd_clear_requesting_shutdown();
 289             }
 290         }
 291 
 292         // Write out new value or start dampening timer
 293         if (a->timeout_ms && a->timer) {
 294             crm_trace("Delaying write of %s %s for dampening",
 295                       attr, pcmk__readable_interval(a->timeout_ms));
 296             mainloop_timer_start(a->timer);
 297         } else {
 298             attrd_write_or_elect_attribute(a);
 299         }
 300 
 301     } else {
 302         int is_force_write = 0;
 303 
 304         crm_element_value_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE,
 305                               &is_force_write);
 306 
 307         if (is_force_write == 1 && a->timeout_ms && a->timer) {
 308             /* Save forced writing and set change flag. */
 309             /* The actual attribute is written by Writer after election. */
 310             crm_trace("%s[%s] from %s is unchanged (%s), forcing write",
 311                       attr, host, peer->name, pcmk__s(value, "unset"));
 312             attrd_set_attr_flags(a, attrd_attr_force_write);
 313         } else {
 314             crm_trace("%s[%s] from %s is unchanged (%s)",
 315                       attr, host, peer->name, pcmk__s(value, "unset"));
 316         }
 317     }
 318 
 319     // This allows us to later detect local values that peer doesn't know about
 320     attrd_set_value_flags(v, attrd_value_from_peer);
 321 
 322     /* If this is a cluster node whose node ID we are learning, remember it */
 323     if ((v->nodeid == 0) && !pcmk_is_set(v->flags, attrd_value_remote)
 324         && (crm_element_value_int(xml, PCMK__XA_ATTR_HOST_ID,
 325                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 326         record_peer_nodeid(v, host);
 327     }
 328 }
 329 
 330 static void
 331 attrd_peer_update_one(const pcmk__node_status_t *peer, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 332                       bool filter)
 333 {
 334     attribute_t *a = NULL;
 335     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 336     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 337     const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 338 
 339     if (attr == NULL) {
 340         crm_warn("Could not update attribute: peer did not specify name");
 341         return;
 342     }
 343 
 344     a = attrd_populate_attribute(xml, attr);
 345     if (a == NULL) {
 346         return;
 347     }
 348 
 349     if (host == NULL) {
 350         // If no host was specified, update all hosts
 351         GHashTableIter vIter;
 352 
 353         crm_debug("Setting %s for all hosts to %s", attr, value);
 354         pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_HOST_ID);
 355         g_hash_table_iter_init(&vIter, a->values);
 356 
 357         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 358             update_attr_on_host(a, peer, xml, attr, value, host, filter);
 359         }
 360 
 361     } else {
 362         // Update attribute value for the given host
 363         update_attr_on_host(a, peer, xml, attr, value, host, filter);
 364     }
 365 
 366     /* If this is a message from some attrd instance broadcasting its protocol
 367      * version, check to see if it's a new minimum version.
 368      */
 369     if (pcmk__str_eq(attr, CRM_ATTR_PROTOCOL, pcmk__str_none)) {
 370         attrd_update_minimum_protocol_ver(peer->name, value);
 371     }
 372 }
 373 
 374 static void
 375 broadcast_unseen_local_values(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 376 {
 377     GHashTableIter aIter;
 378     GHashTableIter vIter;
 379     attribute_t *a = NULL;
 380     attribute_value_t *v = NULL;
 381     xmlNode *sync = NULL;
 382 
 383     g_hash_table_iter_init(&aIter, attributes);
 384     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 385 
 386         g_hash_table_iter_init(&vIter, a->values);
 387         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 388 
 389             if (!pcmk_is_set(v->flags, attrd_value_from_peer)
 390                 && pcmk__str_eq(v->nodename, attrd_cluster->priv->node_name,
 391                                 pcmk__str_casei)) {
 392                 crm_trace("* %s[%s]='%s' is local-only",
 393                           a->id, v->nodename, readable_value(v));
 394                 if (sync == NULL) {
 395                     sync = pcmk__xe_create(NULL, __func__);
 396                     crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 397                 }
 398                 attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
 399             }
 400         }
 401     }
 402 
 403     if (sync != NULL) {
 404         crm_debug("Broadcasting local-only values");
 405         attrd_send_message(NULL, sync, false);
 406         pcmk__xml_free(sync);
 407     }
 408 }
 409 
 410 int
 411 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 412 {
 413     int rc = pcmk_rc_ok;
 414 
 415     attrd_cluster = pcmk_cluster_new();
 416 
 417     pcmk_cluster_set_destroy_fn(attrd_cluster, attrd_cpg_destroy);
 418     pcmk_cpg_set_deliver_fn(attrd_cluster, attrd_cpg_dispatch);
 419     pcmk_cpg_set_confchg_fn(attrd_cluster, pcmk__cpg_confchg_cb);
 420 
 421     pcmk__cluster_set_status_callback(&attrd_peer_change_cb);
 422 
 423     rc = pcmk_cluster_connect(attrd_cluster);
 424     rc = pcmk_rc2legacy(rc);
 425     if (rc != pcmk_ok) {
 426         crm_err("Cluster connection failed");
 427         return rc;
 428     }
 429     return pcmk_ok;
 430 }
 431 
 432 void
 433 attrd_peer_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 434 {
 435     xmlNode *xml = request->xml;
 436     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 437     const char *host = crm_element_value(xml, PCMK__XA_ATTR_HOST);
 438     const char *op = crm_element_value(xml, PCMK__XA_ATTR_CLEAR_OPERATION);
 439     const char *interval_spec = crm_element_value(xml,
 440                                                   PCMK__XA_ATTR_CLEAR_INTERVAL);
 441     guint interval_ms = 0U;
 442     char *attr = NULL;
 443     GHashTableIter iter;
 444     regex_t regex;
 445 
 446     pcmk__node_status_t *peer =
 447         pcmk__get_node(0, request->peer, NULL,
 448                        pcmk__node_search_cluster_member);
 449 
 450     pcmk_parse_interval_spec(interval_spec, &interval_ms);
 451 
 452     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 453         crm_info("Ignoring invalid request to clear failures for %s",
 454                  pcmk__s(rsc, "all resources"));
 455         return;
 456     }
 457 
 458     crm_xml_add(xml, PCMK_XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 459 
 460     /* Make sure value is not set, so we delete */
 461     pcmk__xe_remove_attr(xml, PCMK__XA_ATTR_VALUE);
 462 
 463     g_hash_table_iter_init(&iter, attributes);
 464     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 465         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 466             crm_trace("Matched %s when clearing %s",
 467                       attr, pcmk__s(rsc, "all resources"));
 468             crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 469             attrd_peer_update(peer, xml, host, false);
 470         }
 471     }
 472     regfree(&regex);
 473 }
 474 
 475 /*!
 476  * \internal
 477  * \brief Load attributes from a peer sync response
 478  *
 479  * \param[in]     peer      Peer that sent sync response
 480  * \param[in]     peer_won  Whether peer is the attribute writer
 481  * \param[in,out] xml       Request XML
 482  */
 483 void
 484 attrd_peer_sync_response(const pcmk__node_status_t *peer, bool peer_won,
     /* [previous][next][first][last][top][bottom][index][help] */
 485                          xmlNode *xml)
 486 {
 487     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 488              peer->name);
 489 
 490     if (peer_won) {
 491         /* Initialize the "seen" flag for all attributes to cleared, so we can
 492          * detect attributes that local node has but the writer doesn't.
 493          */
 494         attrd_clear_value_seen();
 495     }
 496 
 497     // Process each attribute update in the sync response
 498     for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL);
 499          child != NULL; child = pcmk__xe_next(child, NULL)) {
 500 
 501         attrd_peer_update(peer, child,
 502                           crm_element_value(child, PCMK__XA_ATTR_HOST), true);
 503     }
 504 
 505     if (peer_won) {
 506         /* If any attributes are still not marked as seen, the writer doesn't
 507          * know about them, so send all peers an update with them.
 508          */
 509         broadcast_unseen_local_values();
 510     }
 511 }
 512 
 513 /*!
 514  * \internal
 515  * \brief Remove all attributes and optionally peer cache entries for a node
 516  *
 517  * \param[in] host     Name of node to purge
 518  * \param[in] uncache  If true, remove node from peer caches
 519  * \param[in] source   Who requested removal (only used for logging)
 520  */
 521 void
 522 attrd_peer_remove(const char *host, bool uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 523 {
 524     attribute_t *a = NULL;
 525     GHashTableIter aIter;
 526 
 527     CRM_CHECK(host != NULL, return);
 528     crm_notice("Removing all %s attributes for node %s "
 529                QB_XS " %s reaping node from cache",
 530                host, source, (uncache? "and" : "without"));
 531 
 532     g_hash_table_iter_init(&aIter, attributes);
 533     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 534         if(g_hash_table_remove(a->values, host)) {
 535             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 536         }
 537     }
 538 
 539     if (uncache) {
 540         pcmk__purge_node_from_cache(host, 0);
 541     }
 542 }
 543 
 544 /*!
 545  * \internal
 546  * \brief Send all known attributes and values to a peer
 547  *
 548  * \param[in] peer  Peer to send sync to (if NULL, broadcast to all peers)
 549  */
 550 void
 551 attrd_peer_sync(pcmk__node_status_t *peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 552 {
 553     GHashTableIter aIter;
 554     GHashTableIter vIter;
 555 
 556     attribute_t *a = NULL;
 557     attribute_value_t *v = NULL;
 558     xmlNode *sync = pcmk__xe_create(NULL, __func__);
 559 
 560     crm_xml_add(sync, PCMK_XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 561 
 562     g_hash_table_iter_init(&aIter, attributes);
 563     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 564         g_hash_table_iter_init(&vIter, a->values);
 565         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 566             crm_debug("Syncing %s[%s]='%s' to %s",
 567                       a->id, v->nodename, readable_value(v),
 568                       readable_peer(peer));
 569             attrd_add_value_xml(sync, a, v, false);
 570         }
 571     }
 572 
 573     crm_debug("Syncing values to %s", readable_peer(peer));
 574     attrd_send_message(peer, sync, false);
 575     pcmk__xml_free(sync);
 576 }
 577 
 578 void
 579 attrd_peer_update(const pcmk__node_status_t *peer, xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 580                   const char *host, bool filter)
 581 {
 582     bool handle_sync_point = false;
 583 
 584     CRM_CHECK((peer != NULL) && (xml != NULL), return);
 585     if (xml->children != NULL) {
 586         for (xmlNode *child = pcmk__xe_first_child(xml, PCMK_XE_OP, NULL, NULL);
 587              child != NULL; child = pcmk__xe_next(child, PCMK_XE_OP)) {
 588 
 589             pcmk__xe_copy_attrs(child, xml, pcmk__xaf_no_overwrite);
 590             attrd_peer_update_one(peer, child, filter);
 591 
 592             if (attrd_request_has_sync_point(child)) {
 593                 handle_sync_point = true;
 594             }
 595         }
 596 
 597     } else {
 598         attrd_peer_update_one(peer, xml, filter);
 599 
 600         if (attrd_request_has_sync_point(xml)) {
 601             handle_sync_point = true;
 602         }
 603     }
 604 
 605     /* If the update XML specified that the client wanted to wait for a sync
 606      * point, process that now.
 607      */
 608     if (handle_sync_point) {
 609         crm_trace("Hit local sync point for attribute update");
 610         attrd_ack_waitlist_clients(attrd_sync_point_local, xml);
 611     }
 612 }

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