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

   1 /*
   2  * Copyright 2013-2023 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/msg_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 = create_xml_node(NULL, __func__);
  30 
  31     crm_xml_add(node, F_TYPE, T_ATTRD);
  32     crm_xml_add(node, F_ORIG, get_local_node_name());
  33     crm_xml_add(node, PCMK__XA_TASK, PCMK__ATTRD_CMD_CONFIRM);
  34     crm_xml_add_int(node, XML_LRM_ATTR_CALLID, 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, F_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, XML_LRM_ATTR_CALLID, &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_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 110 
 111     if(data == NULL) {
 112         return;
 113     }
 114 
 115     if (kind == crm_class_cluster) {
 116         xml = string2xml(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         crm_node_t *peer = crm_get_peer(nodeid, from);
 123 
 124         attrd_peer_message(peer, 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 Override an attribute sync with a local value
 147  *
 148  * Broadcast the local node's value for an attribute that's different from the
 149  * value provided in a peer's attribute synchronization response. This ensures a
 150  * node's values for itself take precedence and all peers are kept in sync.
 151  *
 152  * \param[in] a          Attribute entry to override
 153  *
 154  * \return Local instance of attribute value
 155  */
 156 static attribute_value_t *
 157 broadcast_local_value(const attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159     attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
 160     xmlNode *sync = create_xml_node(NULL, __func__);
 161 
 162     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 163     attrd_add_value_xml(sync, a, v, false);
 164     attrd_send_message(NULL, sync, false);
 165     free_xml(sync);
 166     return v;
 167 }
 168 
 169 /*!
 170  * \internal
 171  * \brief Ensure a Pacemaker Remote node is in the correct peer cache
 172  *
 173  * \param[in] node_name  Name of Pacemaker Remote node to check
 174  */
 175 static void
 176 cache_remote_node(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     /* If we previously assumed this node was an unseen cluster node,
 179      * remove its entry from the cluster peer cache.
 180      */
 181     crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name, NULL);
 182 
 183     if (dup && (dup->uuid == NULL)) {
 184         reap_crm_member(0, node_name);
 185     }
 186 
 187     // Ensure node is in the remote peer cache
 188     CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
 189 }
 190 
 191 #define state_text(state) pcmk__s((state), "in unknown state")
 192 
 193 /*!
 194  * \internal
 195  * \brief Return host's hash table entry (creating one if needed)
 196  *
 197  * \param[in,out] values Hash table of values
 198  * \param[in]     host   Name of peer to look up
 199  * \param[in]     xml    XML describing the attribute
 200  *
 201  * \return Pointer to new or existing hash table entry
 202  */
 203 static attribute_value_t *
 204 attrd_lookup_or_create_value(GHashTable *values, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 205                              const xmlNode *xml)
 206 {
 207     attribute_value_t *v = g_hash_table_lookup(values, host);
 208     int is_remote = 0;
 209 
 210     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 211     if (is_remote) {
 212         cache_remote_node(host);
 213     }
 214 
 215     if (v == NULL) {
 216         v = calloc(1, sizeof(attribute_value_t));
 217         CRM_ASSERT(v != NULL);
 218 
 219         pcmk__str_update(&v->nodename, host);
 220         v->is_remote = is_remote;
 221         g_hash_table_replace(values, v->nodename, v);
 222     }
 223     return(v);
 224 }
 225 
 226 static void
 227 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 228 {
 229     bool gone = false;
 230     bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
 231 
 232     switch (kind) {
 233         case crm_status_uname:
 234             crm_debug("%s node %s is now %s",
 235                       (is_remote? "Remote" : "Cluster"),
 236                       peer->uname, state_text(peer->state));
 237             break;
 238 
 239         case crm_status_processes:
 240             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
 241                 gone = true;
 242             }
 243             crm_debug("Node %s is %s a peer",
 244                       peer->uname, (gone? "no longer" : "now"));
 245             break;
 246 
 247         case crm_status_nstate:
 248             crm_debug("%s node %s is now %s (was %s)",
 249                       (is_remote? "Remote" : "Cluster"),
 250                       peer->uname, state_text(peer->state), state_text(data));
 251             if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
 252                 /* If we're the writer, send new peers a list of all attributes
 253                  * (unless it's a remote node, which doesn't run its own attrd)
 254                  */
 255                 if (attrd_election_won()
 256                     && !pcmk_is_set(peer->flags, crm_remote_node)) {
 257                     attrd_peer_sync(peer, NULL);
 258                 }
 259             } else {
 260                 // Remove all attribute values associated with lost nodes
 261                 attrd_peer_remove(peer->uname, false, "loss");
 262                 gone = true;
 263             }
 264             break;
 265     }
 266 
 267     // Remove votes from cluster nodes that leave, in case election in progress
 268     if (gone && !is_remote) {
 269         attrd_remove_voter(peer);
 270         attrd_remove_peer_protocol_ver(peer->uname);
 271         attrd_do_not_expect_from_peer(peer->uname);
 272 
 273     // Ensure remote nodes that come up are in the remote node cache
 274     } else if (!gone && is_remote) {
 275         cache_remote_node(peer->uname);
 276     }
 277 }
 278 
 279 static void
 280 record_peer_nodeid(attribute_value_t *v, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 281 {
 282     crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 283 
 284     crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid);
 285     if (attrd_election_won()) {
 286         attrd_write_attributes(attrd_write_changed);
 287     }
 288 }
 289 
 290 static void
 291 update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 292                     const char *attr, const char *value, const char *host,
 293                     bool filter, int is_force_write)
 294 {
 295     attribute_value_t *v = NULL;
 296 
 297     v = attrd_lookup_or_create_value(a->values, host, xml);
 298 
 299     if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
 300         && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
 301 
 302         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 303                    attr, host, v->current, value, peer->uname);
 304         v = broadcast_local_value(a);
 305 
 306     } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
 307         crm_notice("Setting %s[%s]%s%s: %s -> %s "
 308                    CRM_XS " from %s with %s write delay",
 309                    attr, host, a->set_type ? " in " : "",
 310                    pcmk__s(a->set_type, ""), pcmk__s(v->current, "(unset)"),
 311                    pcmk__s(value, "(unset)"), peer->uname,
 312                    (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
 313         pcmk__str_update(&v->current, value);
 314         a->changed = true;
 315 
 316         if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
 317             && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
 318 
 319             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 320                 attrd_set_requesting_shutdown();
 321 
 322             } else {
 323                 attrd_clear_requesting_shutdown();
 324             }
 325         }
 326 
 327         // Write out new value or start dampening timer
 328         if (a->timeout_ms && a->timer) {
 329             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 330             mainloop_timer_start(a->timer);
 331         } else {
 332             attrd_write_or_elect_attribute(a);
 333         }
 334 
 335     } else {
 336         if (is_force_write == 1 && a->timeout_ms && a->timer) {
 337             /* Save forced writing and set change flag. */
 338             /* The actual attribute is written by Writer after election. */
 339             crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)",
 340                       attr, host, peer->uname, value);
 341             a->force_write = TRUE;
 342         } else {
 343             crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 344         }
 345     }
 346 
 347     /* Set the seen flag for attribute processing held only in the own node. */
 348     v->seen = TRUE;
 349 
 350     /* If this is a cluster node whose node ID we are learning, remember it */
 351     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 352         && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
 353                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 354         record_peer_nodeid(v, host);
 355     }
 356 }
 357 
 358 static void
 359 attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 360 {
 361     attribute_t *a = NULL;
 362     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 363     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 364     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 365     int is_force_write = 0;
 366 
 367     if (attr == NULL) {
 368         crm_warn("Could not update attribute: peer did not specify name");
 369         return;
 370     }
 371 
 372     crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
 373 
 374     a = attrd_populate_attribute(xml, attr);
 375     if (a == NULL) {
 376         return;
 377     }
 378 
 379     if (host == NULL) {
 380         // If no host was specified, update all hosts
 381         GHashTableIter vIter;
 382 
 383         crm_debug("Setting %s for all hosts to %s", attr, value);
 384         xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
 385         g_hash_table_iter_init(&vIter, a->values);
 386 
 387         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 388             update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 389         }
 390 
 391     } else {
 392         // Update attribute value for the given host
 393         update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 394     }
 395 
 396     /* If this is a message from some attrd instance broadcasting its protocol
 397      * version, check to see if it's a new minimum version.
 398      */
 399     if (pcmk__str_eq(attr, CRM_ATTR_PROTOCOL, pcmk__str_none)) {
 400         attrd_update_minimum_protocol_ver(peer->uname, value);
 401     }
 402 }
 403 
 404 static void
 405 broadcast_unseen_local_values(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 406 {
 407     GHashTableIter aIter;
 408     GHashTableIter vIter;
 409     attribute_t *a = NULL;
 410     attribute_value_t *v = NULL;
 411     xmlNode *sync = NULL;
 412 
 413     g_hash_table_iter_init(&aIter, attributes);
 414     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 415         g_hash_table_iter_init(&vIter, a->values);
 416         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 417             if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
 418                                            pcmk__str_casei)) {
 419                 if (sync == NULL) {
 420                     sync = create_xml_node(NULL, __func__);
 421                     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 422                 }
 423                 attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
 424             }
 425         }
 426     }
 427 
 428     if (sync != NULL) {
 429         crm_debug("Broadcasting local-only values");
 430         attrd_send_message(NULL, sync, false);
 431         free_xml(sync);
 432     }
 433 }
 434 
 435 int
 436 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438     attrd_cluster = pcmk_cluster_new();
 439 
 440     attrd_cluster->destroy = attrd_cpg_destroy;
 441     attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
 442     attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
 443 
 444     crm_set_status_callback(&attrd_peer_change_cb);
 445 
 446     if (crm_cluster_connect(attrd_cluster) == FALSE) {
 447         crm_err("Cluster connection failed");
 448         return -ENOTCONN;
 449     }
 450     return pcmk_ok;
 451 }
 452 
 453 void
 454 attrd_peer_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 455 {
 456     xmlNode *xml = request->xml;
 457     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 458     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 459     const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
 460     const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
 461     guint interval_ms = crm_parse_interval_spec(interval_spec);
 462     char *attr = NULL;
 463     GHashTableIter iter;
 464     regex_t regex;
 465 
 466     crm_node_t *peer = crm_get_peer(0, request->peer);
 467 
 468     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 469         crm_info("Ignoring invalid request to clear failures for %s",
 470                  pcmk__s(rsc, "all resources"));
 471         return;
 472     }
 473 
 474     crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 475 
 476     /* Make sure value is not set, so we delete */
 477     xml_remove_prop(xml, PCMK__XA_ATTR_VALUE);
 478 
 479     g_hash_table_iter_init(&iter, attributes);
 480     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 481         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 482             crm_trace("Matched %s when clearing %s",
 483                       attr, pcmk__s(rsc, "all resources"));
 484             crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 485             attrd_peer_update(peer, xml, host, false);
 486         }
 487     }
 488     regfree(&regex);
 489 }
 490 
 491 /*!
 492  * \internal
 493  * \brief Load attributes from a peer sync response
 494  *
 495  * \param[in]     peer      Peer that sent clear request
 496  * \param[in]     peer_won  Whether peer is the attribute writer
 497  * \param[in,out] xml       Request XML
 498  */
 499 void
 500 attrd_peer_sync_response(const crm_node_t *peer, bool peer_won, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 501 {
 502     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 503              peer->uname);
 504 
 505     if (peer_won) {
 506         /* Initialize the "seen" flag for all attributes to cleared, so we can
 507          * detect attributes that local node has but the writer doesn't.
 508          */
 509         attrd_clear_value_seen();
 510     }
 511 
 512     // Process each attribute update in the sync response
 513     for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
 514          child = pcmk__xml_next(child)) {
 515         attrd_peer_update(peer, child,
 516                           crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
 517                           true);
 518     }
 519 
 520     if (peer_won) {
 521         /* If any attributes are still not marked as seen, the writer doesn't
 522          * know about them, so send all peers an update with them.
 523          */
 524         broadcast_unseen_local_values();
 525     }
 526 }
 527 
 528 /*!
 529  * \internal
 530  * \brief Remove all attributes and optionally peer cache entries for a node
 531  *
 532  * \param[in] host     Name of node to purge
 533  * \param[in] uncache  If true, remove node from peer caches
 534  * \param[in] source   Who requested removal (only used for logging)
 535  */
 536 void
 537 attrd_peer_remove(const char *host, bool uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 538 {
 539     attribute_t *a = NULL;
 540     GHashTableIter aIter;
 541 
 542     CRM_CHECK(host != NULL, return);
 543     crm_notice("Removing all %s attributes for peer %s", host, source);
 544 
 545     g_hash_table_iter_init(&aIter, attributes);
 546     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 547         if(g_hash_table_remove(a->values, host)) {
 548             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 549         }
 550     }
 551 
 552     if (uncache) {
 553         crm_remote_peer_cache_remove(host);
 554         reap_crm_member(0, host);
 555     }
 556 }
 557 
 558 void
 559 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 560 {
 561     GHashTableIter aIter;
 562     GHashTableIter vIter;
 563 
 564     attribute_t *a = NULL;
 565     attribute_value_t *v = NULL;
 566     xmlNode *sync = create_xml_node(NULL, __func__);
 567 
 568     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 569 
 570     g_hash_table_iter_init(&aIter, attributes);
 571     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 572         g_hash_table_iter_init(&vIter, a->values);
 573         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 574             crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
 575             attrd_add_value_xml(sync, a, v, false);
 576         }
 577     }
 578 
 579     crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
 580     attrd_send_message(peer, sync, false);
 581     free_xml(sync);
 582 }
 583 
 584 void
 585 attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 586                   bool filter)
 587 {
 588     bool handle_sync_point = false;
 589 
 590     CRM_CHECK((peer != NULL) && (xml != NULL), return);
 591     if (xml->children != NULL) {
 592         for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
 593              child = crm_next_same_xml(child)) {
 594             attrd_copy_xml_attributes(xml, child);
 595             attrd_peer_update_one(peer, child, filter);
 596 
 597             if (attrd_request_has_sync_point(child)) {
 598                 handle_sync_point = true;
 599             }
 600         }
 601 
 602     } else {
 603         attrd_peer_update_one(peer, xml, filter);
 604 
 605         if (attrd_request_has_sync_point(xml)) {
 606             handle_sync_point = true;
 607         }
 608     }
 609 
 610     /* If the update XML specified that the client wanted to wait for a sync
 611      * point, process that now.
 612      */
 613     if (handle_sync_point) {
 614         crm_trace("Hit local sync point for attribute update");
 615         attrd_ack_waitlist_clients(attrd_sync_point_local, xml);
 616     }
 617 }

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