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

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