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 extern crm_exit_t attrd_exit_status;
  27 
  28 static xmlNode *
  29 attrd_confirmation(int callid)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31     xmlNode *node = create_xml_node(NULL, __func__);
  32 
  33     crm_xml_add(node, F_TYPE, T_ATTRD);
  34     crm_xml_add(node, F_ORIG, get_local_node_name());
  35     crm_xml_add(node, PCMK__XA_TASK, PCMK__ATTRD_CMD_CONFIRM);
  36     crm_xml_add_int(node, XML_LRM_ATTR_CALLID, callid);
  37 
  38     return node;
  39 }
  40 
  41 static void
  42 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  43 {
  44     const char *election_op = crm_element_value(xml, F_CRM_TASK);
  45 
  46     if (election_op) {
  47         attrd_handle_election_op(peer, xml);
  48         return;
  49     }
  50 
  51     if (attrd_shutting_down()) {
  52         /* If we're shutting down, we want to continue responding to election
  53          * ops as long as we're a cluster member (because our vote may be
  54          * needed). Ignore all other messages.
  55          */
  56         return;
  57 
  58     } else {
  59         pcmk__request_t request = {
  60             .ipc_client     = NULL,
  61             .ipc_id         = 0,
  62             .ipc_flags      = 0,
  63             .peer           = peer->uname,
  64             .xml            = xml,
  65             .call_options   = 0,
  66             .result         = PCMK__UNKNOWN_RESULT,
  67         };
  68 
  69         request.op = crm_element_value_copy(request.xml, PCMK__XA_TASK);
  70         CRM_CHECK(request.op != NULL, return);
  71 
  72         attrd_handle_request(&request);
  73 
  74         /* Having finished handling the request, check to see if the originating
  75          * peer requested confirmation.  If so, send that confirmation back now.
  76          */
  77         if (pcmk__xe_attr_is_true(xml, PCMK__XA_CONFIRM) &&
  78             !pcmk__str_eq(request.op, PCMK__ATTRD_CMD_CONFIRM, pcmk__str_none)) {
  79             int callid = 0;
  80             xmlNode *reply = NULL;
  81 
  82             /* Add the confirmation ID for the message we are confirming to the
  83              * response so the originating peer knows what they're a confirmation
  84              * for.
  85              */
  86             crm_element_value_int(xml, XML_LRM_ATTR_CALLID, &callid);
  87             reply = attrd_confirmation(callid);
  88 
  89             /* And then send the confirmation back to the originating peer.  This
  90              * ends up right back in this same function (attrd_peer_message) on the
  91              * peer where it will have to do something with a PCMK__XA_CONFIRM type
  92              * message.
  93              */
  94             crm_debug("Sending %s a confirmation", peer->uname);
  95             attrd_send_message(peer, reply, false);
  96             free_xml(reply);
  97         }
  98 
  99         pcmk__reset_request(&request);
 100     }
 101 }
 102 
 103 static void
 104 attrd_cpg_dispatch(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
 105                  const struct cpg_name *groupName,
 106                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 107 {
 108     uint32_t kind = 0;
 109     xmlNode *xml = NULL;
 110     const char *from = NULL;
 111     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
 112 
 113     if(data == NULL) {
 114         return;
 115     }
 116 
 117     if (kind == crm_class_cluster) {
 118         xml = string2xml(data);
 119     }
 120 
 121     if (xml == NULL) {
 122         crm_err("Bad message of class %d received from %s[%u]: '%.120s'", kind, from, nodeid, data);
 123     } else {
 124         crm_node_t *peer = crm_get_peer(nodeid, from);
 125 
 126         attrd_peer_message(peer, xml);
 127     }
 128 
 129     free_xml(xml);
 130     free(data);
 131 }
 132 
 133 static void
 134 attrd_cpg_destroy(gpointer unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 135 {
 136     if (attrd_shutting_down()) {
 137         crm_info("Corosync disconnection complete");
 138 
 139     } else {
 140         crm_crit("Lost connection to cluster layer, shutting down");
 141         attrd_exit_status = CRM_EX_DISCONNECT;
 142         attrd_shutdown(0);
 143     }
 144 }
 145 
 146 /*!
 147  * \internal
 148  * \brief Override an attribute sync with a local value
 149  *
 150  * Broadcast the local node's value for an attribute that's different from the
 151  * value provided in a peer's attribute synchronization response. This ensures a
 152  * node's values for itself take precedence and all peers are kept in sync.
 153  *
 154  * \param[in] a          Attribute entry to override
 155  *
 156  * \return Local instance of attribute value
 157  */
 158 static attribute_value_t *
 159 broadcast_local_value(const attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161     attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
 162     xmlNode *sync = create_xml_node(NULL, __func__);
 163 
 164     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 165     attrd_add_value_xml(sync, a, v, false);
 166     attrd_send_message(NULL, sync, false);
 167     free_xml(sync);
 168     return v;
 169 }
 170 
 171 /*!
 172  * \internal
 173  * \brief Ensure a Pacemaker Remote node is in the correct peer cache
 174  *
 175  * \param[in] node_name  Name of Pacemaker Remote node to check
 176  */
 177 static void
 178 cache_remote_node(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     /* If we previously assumed this node was an unseen cluster node,
 181      * remove its entry from the cluster peer cache.
 182      */
 183     crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
 184 
 185     if (dup && (dup->uuid == NULL)) {
 186         reap_crm_member(0, node_name);
 187     }
 188 
 189     // Ensure node is in the remote peer cache
 190     CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
 191 }
 192 
 193 #define state_text(state) pcmk__s((state), "in unknown state")
 194 
 195 /*!
 196  * \internal
 197  * \brief Return host's hash table entry (creating one if needed)
 198  *
 199  * \param[in,out] values Hash table of values
 200  * \param[in]     host   Name of peer to look up
 201  * \param[in]     xml    XML describing the attribute
 202  *
 203  * \return Pointer to new or existing hash table entry
 204  */
 205 static attribute_value_t *
 206 attrd_lookup_or_create_value(GHashTable *values, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 207                              const xmlNode *xml)
 208 {
 209     attribute_value_t *v = g_hash_table_lookup(values, host);
 210     int is_remote = 0;
 211 
 212     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 213     if (is_remote) {
 214         cache_remote_node(host);
 215     }
 216 
 217     if (v == NULL) {
 218         v = calloc(1, sizeof(attribute_value_t));
 219         CRM_ASSERT(v != NULL);
 220 
 221         pcmk__str_update(&v->nodename, host);
 222         v->is_remote = is_remote;
 223         g_hash_table_replace(values, v->nodename, v);
 224     }
 225     return(v);
 226 }
 227 
 228 static void
 229 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 230 {
 231     bool gone = false;
 232     bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
 233 
 234     switch (kind) {
 235         case crm_status_uname:
 236             crm_debug("%s node %s is now %s",
 237                       (is_remote? "Remote" : "Cluster"),
 238                       peer->uname, state_text(peer->state));
 239             break;
 240 
 241         case crm_status_processes:
 242             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
 243                 gone = true;
 244             }
 245             crm_debug("Node %s is %s a peer",
 246                       peer->uname, (gone? "no longer" : "now"));
 247             break;
 248 
 249         case crm_status_nstate:
 250             crm_debug("%s node %s is now %s (was %s)",
 251                       (is_remote? "Remote" : "Cluster"),
 252                       peer->uname, state_text(peer->state), state_text(data));
 253             if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
 254                 /* If we're the writer, send new peers a list of all attributes
 255                  * (unless it's a remote node, which doesn't run its own attrd)
 256                  */
 257                 if (attrd_election_won()
 258                     && !pcmk_is_set(peer->flags, crm_remote_node)) {
 259                     attrd_peer_sync(peer, NULL);
 260                 }
 261             } else {
 262                 // Remove all attribute values associated with lost nodes
 263                 attrd_peer_remove(peer->uname, false, "loss");
 264                 gone = true;
 265             }
 266             break;
 267     }
 268 
 269     // Remove votes from cluster nodes that leave, in case election in progress
 270     if (gone && !is_remote) {
 271         attrd_remove_voter(peer);
 272         attrd_remove_peer_protocol_ver(peer->uname);
 273         attrd_do_not_expect_from_peer(peer->uname);
 274 
 275     // Ensure remote nodes that come up are in the remote node cache
 276     } else if (!gone && is_remote) {
 277         cache_remote_node(peer->uname);
 278     }
 279 }
 280 
 281 static void
 282 record_peer_nodeid(attribute_value_t *v, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 283 {
 284     crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 285 
 286     crm_trace("Learned %s has node id %s", known_peer->uname, known_peer->uuid);
 287     if (attrd_election_won()) {
 288         attrd_write_attributes(false, false);
 289     }
 290 }
 291 
 292 static void
 293 update_attr_on_host(attribute_t *a, const crm_node_t *peer, const xmlNode *xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 294                     const char *attr, const char *value, const char *host,
 295                     bool filter, int is_force_write)
 296 {
 297     attribute_value_t *v = NULL;
 298 
 299     v = attrd_lookup_or_create_value(a->values, host, xml);
 300 
 301     if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
 302         && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
 303 
 304         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 305                    attr, host, v->current, value, peer->uname);
 306         v = broadcast_local_value(a);
 307 
 308     } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
 309         crm_notice("Setting %s[%s]%s%s: %s -> %s "
 310                    CRM_XS " from %s with %s write delay",
 311                    attr, host, a->set_type ? " in " : "",
 312                    pcmk__s(a->set_type, ""), pcmk__s(v->current, "(unset)"),
 313                    pcmk__s(value, "(unset)"), peer->uname,
 314                    (a->timeout_ms == 0)? "no" : pcmk__readable_interval(a->timeout_ms));
 315         pcmk__str_update(&v->current, value);
 316         a->changed = true;
 317 
 318         if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
 319             && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
 320 
 321             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 322                 attrd_set_requesting_shutdown();
 323 
 324             } else {
 325                 attrd_clear_requesting_shutdown();
 326             }
 327         }
 328 
 329         // Write out new value or start dampening timer
 330         if (a->timeout_ms && a->timer) {
 331             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 332             mainloop_timer_start(a->timer);
 333         } else {
 334             attrd_write_or_elect_attribute(a);
 335         }
 336 
 337     } else {
 338         if (is_force_write == 1 && a->timeout_ms && a->timer) {
 339             /* Save forced writing and set change flag. */
 340             /* The actual attribute is written by Writer after election. */
 341             crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)",
 342                       attr, host, peer->uname, value);
 343             a->force_write = TRUE;
 344         } else {
 345             crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 346         }
 347     }
 348 
 349     /* Set the seen flag for attribute processing held only in the own node. */
 350     v->seen = TRUE;
 351 
 352     /* If this is a cluster node whose node ID we are learning, remember it */
 353     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 354         && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
 355                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 356         record_peer_nodeid(v, host);
 357     }
 358 }
 359 
 360 static void
 361 attrd_peer_update_one(const crm_node_t *peer, xmlNode *xml, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 362 {
 363     attribute_t *a = NULL;
 364     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 365     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 366     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 367     int is_force_write = 0;
 368 
 369     if (attr == NULL) {
 370         crm_warn("Could not update attribute: peer did not specify name");
 371         return;
 372     }
 373 
 374     crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
 375 
 376     a = attrd_populate_attribute(xml, attr);
 377     if (a == NULL) {
 378         return;
 379     }
 380 
 381     if (host == NULL) {
 382         // If no host was specified, update all hosts
 383         GHashTableIter vIter;
 384 
 385         crm_debug("Setting %s for all hosts to %s", attr, value);
 386         xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
 387         g_hash_table_iter_init(&vIter, a->values);
 388 
 389         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 390             update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 391         }
 392 
 393     } else {
 394         // Update attribute value for the given host
 395         update_attr_on_host(a, peer, xml, attr, value, host, filter, is_force_write);
 396     }
 397 
 398     /* If this is a message from some attrd instance broadcasting its protocol
 399      * version, check to see if it's a new minimum version.
 400      */
 401     if (pcmk__str_eq(attr, CRM_ATTR_PROTOCOL, pcmk__str_none)) {
 402         attrd_update_minimum_protocol_ver(peer->uname, value);
 403     }
 404 }
 405 
 406 static void
 407 broadcast_unseen_local_values(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 408 {
 409     GHashTableIter aIter;
 410     GHashTableIter vIter;
 411     attribute_t *a = NULL;
 412     attribute_value_t *v = NULL;
 413     xmlNode *sync = NULL;
 414 
 415     g_hash_table_iter_init(&aIter, attributes);
 416     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 417         g_hash_table_iter_init(&vIter, a->values);
 418         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 419             if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
 420                                            pcmk__str_casei)) {
 421                 if (sync == NULL) {
 422                     sync = create_xml_node(NULL, __func__);
 423                     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 424                 }
 425                 attrd_add_value_xml(sync, a, v, a->timeout_ms && a->timer);
 426             }
 427         }
 428     }
 429 
 430     if (sync != NULL) {
 431         crm_debug("Broadcasting local-only values");
 432         attrd_send_message(NULL, sync, false);
 433         free_xml(sync);
 434     }
 435 }
 436 
 437 int
 438 attrd_cluster_connect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 440     attrd_cluster = pcmk_cluster_new();
 441 
 442     attrd_cluster->destroy = attrd_cpg_destroy;
 443     attrd_cluster->cpg.cpg_deliver_fn = attrd_cpg_dispatch;
 444     attrd_cluster->cpg.cpg_confchg_fn = pcmk_cpg_membership;
 445 
 446     crm_set_status_callback(&attrd_peer_change_cb);
 447 
 448     if (crm_cluster_connect(attrd_cluster) == FALSE) {
 449         crm_err("Cluster connection failed");
 450         return -ENOTCONN;
 451     }
 452     return pcmk_ok;
 453 }
 454 
 455 void
 456 attrd_peer_clear_failure(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 457 {
 458     xmlNode *xml = request->xml;
 459     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 460     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 461     const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
 462     const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
 463     guint interval_ms = crm_parse_interval_spec(interval_spec);
 464     char *attr = NULL;
 465     GHashTableIter iter;
 466     regex_t regex;
 467 
 468     crm_node_t *peer = crm_get_peer(0, request->peer);
 469 
 470     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 471         crm_info("Ignoring invalid request to clear failures for %s",
 472                  pcmk__s(rsc, "all resources"));
 473         return;
 474     }
 475 
 476     crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 477 
 478     /* Make sure value is not set, so we delete */
 479     if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
 480         crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
 481     }
 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 clear request
 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 crm_node_t *peer, bool peer_won, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 505 {
 506     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 507              peer->uname);
 508 
 509     if (peer_won) {
 510         /* Initialize the "seen" flag for all attributes to cleared, so we can
 511          * detect attributes that local node has but the writer doesn't.
 512          */
 513         attrd_clear_value_seen();
 514     }
 515 
 516     // Process each attribute update in the sync response
 517     for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
 518          child = pcmk__xml_next(child)) {
 519         attrd_peer_update(peer, child,
 520                           crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
 521                           true);
 522     }
 523 
 524     if (peer_won) {
 525         /* If any attributes are still not marked as seen, the writer doesn't
 526          * know about them, so send all peers an update with them.
 527          */
 528         broadcast_unseen_local_values();
 529     }
 530 }
 531 
 532 /*!
 533  * \internal
 534  * \brief Remove all attributes and optionally peer cache entries for a node
 535  *
 536  * \param[in] host     Name of node to purge
 537  * \param[in] uncache  If true, remove node from peer caches
 538  * \param[in] source   Who requested removal (only used for logging)
 539  */
 540 void
 541 attrd_peer_remove(const char *host, bool uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 542 {
 543     attribute_t *a = NULL;
 544     GHashTableIter aIter;
 545 
 546     CRM_CHECK(host != NULL, return);
 547     crm_notice("Removing all %s attributes for peer %s", host, source);
 548 
 549     g_hash_table_iter_init(&aIter, attributes);
 550     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 551         if(g_hash_table_remove(a->values, host)) {
 552             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 553         }
 554     }
 555 
 556     if (uncache) {
 557         crm_remote_peer_cache_remove(host);
 558         reap_crm_member(0, host);
 559     }
 560 }
 561 
 562 void
 563 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 564 {
 565     GHashTableIter aIter;
 566     GHashTableIter vIter;
 567 
 568     attribute_t *a = NULL;
 569     attribute_value_t *v = NULL;
 570     xmlNode *sync = create_xml_node(NULL, __func__);
 571 
 572     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 573 
 574     g_hash_table_iter_init(&aIter, attributes);
 575     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 576         g_hash_table_iter_init(&vIter, a->values);
 577         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 578             crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
 579             attrd_add_value_xml(sync, a, v, false);
 580         }
 581     }
 582 
 583     crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
 584     attrd_send_message(peer, sync, false);
 585     free_xml(sync);
 586 }
 587 
 588 void
 589 attrd_peer_update(const crm_node_t *peer, xmlNode *xml, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
 590                   bool filter)
 591 {
 592     bool handle_sync_point = false;
 593 
 594     if (xml_has_children(xml)) {
 595         for (xmlNode *child = first_named_child(xml, XML_ATTR_OP); child != NULL;
 596              child = crm_next_same_xml(child)) {
 597             attrd_copy_xml_attributes(xml, child);
 598             attrd_peer_update_one(peer, child, filter);
 599 
 600             if (attrd_request_has_sync_point(child)) {
 601                 handle_sync_point = true;
 602             }
 603         }
 604 
 605     } else {
 606         attrd_peer_update_one(peer, xml, filter);
 607 
 608         if (attrd_request_has_sync_point(xml)) {
 609             handle_sync_point = true;
 610         }
 611     }
 612 
 613     /* If the update XML specified that the client wanted to wait for a sync
 614      * point, process that now.
 615      */
 616     if (handle_sync_point) {
 617         crm_trace("Hit local sync point for attribute update");
 618         attrd_ack_waitlist_clients(attrd_sync_point_local, xml);
 619     }
 620 }

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