root/daemons/attrd/attrd_commands.c

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

DEFINITIONS

This source file includes following definitions.
  1. send_attrd_message
  2. attribute_timer_cb
  3. free_attribute_value
  4. free_attribute
  5. cache_remote_node
  6. add_attribute_value_xml
  7. clear_attribute_value_seen
  8. create_attribute
  9. attrd_client_peer_remove
  10. attrd_client_update
  11. attrd_client_clear_failure
  12. attrd_client_refresh
  13. build_query_reply
  14. attrd_client_query
  15. attrd_peer_clear_failure
  16. process_peer_sync_response
  17. attrd_broadcast_protocol
  18. attrd_peer_message
  19. attrd_peer_sync
  20. attrd_peer_remove
  21. attrd_lookup_or_create_value
  22. broadcast_unseen_local_values
  23. broadcast_local_value
  24. attrd_peer_update
  25. write_or_elect_attribute
  26. attrd_election_cb
  27. attrd_peer_change_cb
  28. attrd_cib_callback
  29. write_attributes
  30. build_update_element
  31. set_alert_attribute_value
  32. send_alert_attributes_value
  33. write_attribute

   1 /*
   2  * Copyright 2013-2021 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 <sys/types.h>
  13 #include <regex.h>
  14 #include <glib.h>
  15 
  16 #include <crm/msg_xml.h>
  17 #include <crm/cluster.h>
  18 #include <crm/cib.h>
  19 #include <crm/common/xml_internal.h>
  20 #include <crm/cluster/internal.h>
  21 #include <crm/cluster/election_internal.h>
  22 #include <crm/cib/internal.h>
  23 
  24 #include "pacemaker-attrd.h"
  25 
  26 /*
  27  * Legacy attrd (all pre-1.1.11 Pacemaker versions, plus all versions when used
  28  * with the no-longer-supported CMAN or corosync-plugin stacks) is unversioned.
  29  *
  30  * With atomic attrd, each attrd will send ATTRD_PROTOCOL_VERSION with every
  31  * peer request and reply. As of Pacemaker 2.0.0, at start-up each attrd will
  32  * also set a private attribute for itself with its version, so any attrd can
  33  * determine the minimum version supported by all peers.
  34  *
  35  * Protocol  Pacemaker  Significant changes
  36  * --------  ---------  -------------------
  37  *     1       1.1.11   PCMK__ATTRD_CMD_UPDATE (PCMK__XA_ATTR_NAME only),
  38  *                      PCMK__ATTRD_CMD_PEER_REMOVE, PCMK__ATTRD_CMD_REFRESH,
  39  *                      PCMK__ATTRD_CMD_FLUSH, PCMK__ATTRD_CMD_SYNC,
  40  *                      PCMK__ATTRD_CMD_SYNC_RESPONSE
  41  *     1       1.1.13   PCMK__ATTRD_CMD_UPDATE (with PCMK__XA_ATTR_PATTERN),
  42  *                      PCMK__ATTRD_CMD_QUERY
  43  *     1       1.1.15   PCMK__ATTRD_CMD_UPDATE_BOTH,
  44  *                      PCMK__ATTRD_CMD_UPDATE_DELAY
  45  *     2       1.1.17   PCMK__ATTRD_CMD_CLEAR_FAILURE
  46  *     3       2.1.1    PCMK__ATTRD_CMD_SYNC_RESPONSE indicates remote nodes
  47  */
  48 #define ATTRD_PROTOCOL_VERSION "3"
  49 
  50 int last_cib_op_done = 0;
  51 GHashTable *attributes = NULL;
  52 
  53 void write_attribute(attribute_t *a, bool ignore_delay);
  54 void write_or_elect_attribute(attribute_t *a);
  55 void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
  56 void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
  57 void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
  58 
  59 static void broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml);
  60 
  61 static gboolean
  62 send_attrd_message(crm_node_t * node, xmlNode * data)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     crm_xml_add(data, F_TYPE, T_ATTRD);
  65     crm_xml_add(data, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
  66     attrd_xml_add_writer(data);
  67     return send_cluster_message(node, crm_msg_attrd, data, TRUE);
  68 }
  69 
  70 static gboolean
  71 attribute_timer_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     attribute_t *a = data;
  74     crm_trace("Dampen interval expired for %s", a->id);
  75     write_or_elect_attribute(a);
  76     return FALSE;
  77 }
  78 
  79 static void
  80 free_attribute_value(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     attribute_value_t *v = data;
  83 
  84     free(v->nodename);
  85     free(v->current);
  86     free(v->requested);
  87     free(v);
  88 }
  89 
  90 void
  91 free_attribute(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     attribute_t *a = data;
  94     if(a) {
  95         free(a->id);
  96         free(a->set);
  97         free(a->uuid);
  98         free(a->user);
  99 
 100         mainloop_timer_del(a->timer);
 101         g_hash_table_destroy(a->values);
 102 
 103         free(a);
 104     }
 105 }
 106 
 107 /*!
 108  * \internal
 109  * \brief Ensure a Pacemaker Remote node is in the correct peer cache
 110  *
 111  * \param[in]
 112  */
 113 static void
 114 cache_remote_node(const char *node_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 115 {
 116     /* If we previously assumed this node was an unseen cluster node,
 117      * remove its entry from the cluster peer cache.
 118      */
 119     crm_node_t *dup = pcmk__search_cluster_node_cache(0, node_name);
 120 
 121     if (dup && (dup->uuid == NULL)) {
 122         reap_crm_member(0, node_name);
 123     }
 124 
 125     // Ensure node is in the remote peer cache
 126     CRM_ASSERT(crm_remote_peer_get(node_name) != NULL);
 127 }
 128 
 129 /*!
 130  * \internal
 131  * \brief Create an XML representation of an attribute for use in peer messages
 132  *
 133  * \param[in] parent       Create attribute XML as child element of this element
 134  * \param[in] a            Attribute to represent
 135  * \param[in] v            Attribute value to represent
 136  * \param[in] force_write  If true, value should be written even if unchanged
 137  *
 138  * \return XML representation of attribute
 139  */
 140 static xmlNode *
 141 add_attribute_value_xml(xmlNode *parent, attribute_t *a, attribute_value_t *v,
     /* [previous][next][first][last][top][bottom][index][help] */
 142                         bool force_write)
 143 {
 144     xmlNode *xml = create_xml_node(parent, __func__);
 145 
 146     crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
 147     crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set);
 148     crm_xml_add(xml, PCMK__XA_ATTR_UUID, a->uuid);
 149     crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
 150     crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, v->nodename);
 151     if (v->nodeid > 0) {
 152         crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, v->nodeid);
 153     }
 154     if (v->is_remote != 0) {
 155         crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
 156     }
 157     crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
 158     crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING, a->timeout_ms / 1000);
 159     crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE, a->is_private);
 160     crm_xml_add_int(xml, PCMK__XA_ATTR_FORCE, force_write);
 161 
 162     return xml;
 163 }
 164 
 165 static void
 166 clear_attribute_value_seen(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168     GHashTableIter aIter;
 169     GHashTableIter vIter;
 170     attribute_t *a;
 171     attribute_value_t *v = NULL;
 172 
 173     g_hash_table_iter_init(&aIter, attributes);
 174     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 175         g_hash_table_iter_init(&vIter, a->values);
 176         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 177             v->seen = FALSE;
 178             crm_trace("Clear seen flag %s[%s] = %s.", a->id, v->nodename, v->current);
 179         }
 180     }
 181 }
 182 
 183 static attribute_t *
 184 create_attribute(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     int dampen = 0;
 187     const char *value = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
 188     attribute_t *a = calloc(1, sizeof(attribute_t));
 189 
 190     a->id      = crm_element_value_copy(xml, PCMK__XA_ATTR_NAME);
 191     a->set     = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
 192     a->uuid    = crm_element_value_copy(xml, PCMK__XA_ATTR_UUID);
 193     a->values = pcmk__strikey_table(NULL, free_attribute_value);
 194 
 195     crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &a->is_private);
 196 
 197     a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
 198     crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
 199 
 200     if(value) {
 201         dampen = crm_get_msec(value);
 202         crm_trace("Created attribute %s with delay %dms (%s)", a->id, dampen, value);
 203     } else {
 204         crm_trace("Created attribute %s with no delay", a->id);
 205     }
 206 
 207     if(dampen > 0) {
 208         a->timeout_ms = dampen;
 209         a->timer = mainloop_timer_add(a->id, a->timeout_ms, FALSE, attribute_timer_cb, a);
 210     } else if (dampen < 0) {
 211         crm_warn("Ignoring invalid delay %s for attribute %s", value, a->id);
 212     }
 213 
 214     g_hash_table_replace(attributes, a->id, a);
 215     return a;
 216 }
 217 
 218 /*!
 219  * \internal
 220  * \brief Respond to a client peer-remove request (i.e. propagate to all peers)
 221  *
 222  * \param[in] client_name Name of client that made request (for log messages)
 223  * \param[in] xml         Root of request XML
 224  *
 225  * \return void
 226  */
 227 void
 228 attrd_client_peer_remove(pcmk__client_t *client, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230     // Host and ID are not used in combination, rather host has precedence
 231     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 232     char *host_alloc = NULL;
 233 
 234     if (host == NULL) {
 235         int nodeid = 0;
 236 
 237         crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID, &nodeid);
 238         if (nodeid > 0) {
 239             crm_node_t *node = pcmk__search_cluster_node_cache(nodeid, NULL);
 240             char *host_alloc = NULL;
 241 
 242             if (node && node->uname) {
 243                 // Use cached name if available
 244                 host = node->uname;
 245             } else {
 246                 // Otherwise ask cluster layer
 247                 host_alloc = get_node_name(nodeid);
 248                 host = host_alloc;
 249             }
 250             crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, host);
 251         }
 252     }
 253 
 254     if (host) {
 255         crm_info("Client %s is requesting all values for %s be removed",
 256                  pcmk__client_name(client), host);
 257         send_attrd_message(NULL, xml); /* ends up at attrd_peer_message() */
 258         free(host_alloc);
 259     } else {
 260         crm_info("Ignoring request by client %s to remove all peer values without specifying peer",
 261                  pcmk__client_name(client));
 262     }
 263 }
 264 
 265 /*!
 266  * \internal
 267  * \brief Respond to a client update request
 268  *
 269  * \param[in] xml         Root of request XML
 270  *
 271  * \return void
 272  */
 273 void
 274 attrd_client_update(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 275 {
 276     attribute_t *a = NULL;
 277     char *host = crm_element_value_copy(xml, PCMK__XA_ATTR_NODE_NAME);
 278     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 279     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 280     const char *regex = crm_element_value(xml, PCMK__XA_ATTR_PATTERN);
 281 
 282     /* If a regex was specified, broadcast a message for each match */
 283     if ((attr == NULL) && regex) {
 284         GHashTableIter aIter;
 285         regex_t *r_patt = calloc(1, sizeof(regex_t));
 286 
 287         crm_debug("Setting %s to %s", regex, value);
 288         if (regcomp(r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
 289             crm_err("Bad regex '%s' for update", regex);
 290 
 291         } else {
 292             g_hash_table_iter_init(&aIter, attributes);
 293             while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
 294                 int status = regexec(r_patt, attr, 0, NULL, 0);
 295 
 296                 if (status == 0) {
 297                     crm_trace("Matched %s with %s", attr, regex);
 298                     crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 299                     send_attrd_message(NULL, xml);
 300                 }
 301             }
 302         }
 303 
 304         free(host);
 305         regfree(r_patt);
 306         free(r_patt);
 307         return;
 308 
 309     } else if (attr == NULL) {
 310         crm_err("Update request did not specify attribute or regular expression");
 311         free(host);
 312         return;
 313     }
 314 
 315     if (host == NULL) {
 316         crm_trace("Inferring host");
 317         host = strdup(attrd_cluster->uname);
 318         crm_xml_add(xml, PCMK__XA_ATTR_NODE_NAME, host);
 319         crm_xml_add_int(xml, PCMK__XA_ATTR_NODE_ID, attrd_cluster->nodeid);
 320     }
 321 
 322     a = g_hash_table_lookup(attributes, attr);
 323 
 324     /* If value was specified using ++ or += notation, expand to real value */
 325     if (value) {
 326         if (attrd_value_needs_expansion(value)) {
 327             int int_value;
 328             attribute_value_t *v = NULL;
 329 
 330             if (a) {
 331                 v = g_hash_table_lookup(a->values, host);
 332             }
 333             int_value = attrd_expand_value(value, (v? v->current : NULL));
 334 
 335             crm_info("Expanded %s=%s to %d", attr, value, int_value);
 336             crm_xml_add_int(xml, PCMK__XA_ATTR_VALUE, int_value);
 337 
 338             /* Replacing the value frees the previous memory, so re-query it */
 339             value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 340         }
 341     }
 342 
 343     crm_debug("Broadcasting %s[%s]=%s%s", attr, host, value,
 344               (attrd_election_won()? " (writer)" : ""));
 345 
 346     free(host);
 347 
 348     send_attrd_message(NULL, xml); /* ends up at attrd_peer_message() */
 349 }
 350 
 351 /*!
 352  * \internal
 353  * \brief Respond to client clear-failure request
 354  *
 355  * \param[in] xml         Request XML
 356  */
 357 void
 358 attrd_client_clear_failure(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 359 {
 360 #if 0
 361     /* @TODO Track the minimum supported protocol version across all nodes,
 362      * then enable this more-efficient code.
 363      */
 364     if (compare_version("2", minimum_protocol_version) <= 0) {
 365         /* Propagate to all peers (including ourselves).
 366          * This ends up at attrd_peer_message().
 367          */
 368         send_attrd_message(NULL, xml);
 369         return;
 370     }
 371 #endif
 372 
 373     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 374     const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
 375     const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
 376 
 377     /* Map this to an update */
 378     crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 379 
 380     /* Add regular expression matching desired attributes */
 381 
 382     if (rsc) {
 383         char *pattern;
 384 
 385         if (op == NULL) {
 386             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
 387 
 388         } else {
 389             guint interval_ms = crm_parse_interval_spec(interval_spec);
 390 
 391             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
 392                                         rsc, op, interval_ms);
 393         }
 394 
 395         crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, pattern);
 396         free(pattern);
 397 
 398     } else {
 399         crm_xml_add(xml, PCMK__XA_ATTR_PATTERN, ATTRD_RE_CLEAR_ALL);
 400     }
 401 
 402     /* Make sure attribute and value are not set, so we delete via regex */
 403     if (crm_element_value(xml, PCMK__XA_ATTR_NAME)) {
 404         crm_xml_replace(xml, PCMK__XA_ATTR_NAME, NULL);
 405     }
 406     if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
 407         crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
 408     }
 409 
 410     attrd_client_update(xml);
 411 }
 412 
 413 /*!
 414  * \internal
 415  * \brief Respond to a client refresh request (i.e. write out all attributes)
 416  *
 417  * \return void
 418  */
 419 void
 420 attrd_client_refresh(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     crm_info("Updating all attributes");
 423     write_attributes(TRUE, TRUE);
 424 }
 425 
 426 /*!
 427  * \internal
 428  * \brief Build the XML reply to a client query
 429  *
 430  * param[in] attr Name of requested attribute
 431  * param[in] host Name of requested host (or NULL for all hosts)
 432  *
 433  * \return New XML reply
 434  * \note Caller is responsible for freeing the resulting XML
 435  */
 436 static xmlNode *build_query_reply(const char *attr, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 437 {
 438     xmlNode *reply = create_xml_node(NULL, __func__);
 439     attribute_t *a;
 440 
 441     if (reply == NULL) {
 442         return NULL;
 443     }
 444     crm_xml_add(reply, F_TYPE, T_ATTRD);
 445     crm_xml_add(reply, PCMK__XA_ATTR_VERSION, ATTRD_PROTOCOL_VERSION);
 446 
 447     /* If desired attribute exists, add its value(s) to the reply */
 448     a = g_hash_table_lookup(attributes, attr);
 449     if (a) {
 450         attribute_value_t *v;
 451         xmlNode *host_value;
 452 
 453         crm_xml_add(reply, PCMK__XA_ATTR_NAME, attr);
 454 
 455         /* Allow caller to use "localhost" to refer to local node */
 456         if (pcmk__str_eq(host, "localhost", pcmk__str_casei)) {
 457             host = attrd_cluster->uname;
 458             crm_trace("Mapped localhost to %s", host);
 459         }
 460 
 461         /* If a specific node was requested, add its value */
 462         if (host) {
 463             v = g_hash_table_lookup(a->values, host);
 464             host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
 465             if (host_value == NULL) {
 466                 free_xml(reply);
 467                 return NULL;
 468             }
 469             crm_xml_add(host_value, PCMK__XA_ATTR_NODE_NAME, host);
 470             crm_xml_add(host_value, PCMK__XA_ATTR_VALUE,
 471                         (v? v->current : NULL));
 472 
 473         /* Otherwise, add all nodes' values */
 474         } else {
 475             GHashTableIter iter;
 476 
 477             g_hash_table_iter_init(&iter, a->values);
 478             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
 479                 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
 480                 if (host_value == NULL) {
 481                     free_xml(reply);
 482                     return NULL;
 483                 }
 484                 crm_xml_add(host_value, PCMK__XA_ATTR_NODE_NAME, v->nodename);
 485                 crm_xml_add(host_value, PCMK__XA_ATTR_VALUE, v->current);
 486             }
 487         }
 488     }
 489     return reply;
 490 }
 491 
 492 /*!
 493  * \internal
 494  * \brief Respond to a client query
 495  *
 496  * \param[in] client Who queried us
 497  * \param[in] query  Root of query XML
 498  *
 499  * \return void
 500  */
 501 void
 502 attrd_client_query(pcmk__client_t *client, uint32_t id, uint32_t flags,
     /* [previous][next][first][last][top][bottom][index][help] */
 503                    xmlNode *query)
 504 {
 505     const char *attr;
 506     const char *origin = crm_element_value(query, F_ORIG);
 507     xmlNode *reply;
 508 
 509     if (origin == NULL) {
 510         origin = "unknown client";
 511     }
 512     crm_debug("Query arrived from %s", origin);
 513 
 514     /* Request must specify attribute name to query */
 515     attr = crm_element_value(query, PCMK__XA_ATTR_NAME);
 516     if (attr == NULL) {
 517         crm_warn("Ignoring malformed query from %s (no attribute name given)",
 518                  origin);
 519         return;
 520     }
 521 
 522     /* Build the XML reply */
 523     reply = build_query_reply(attr, crm_element_value(query,
 524                                                       PCMK__XA_ATTR_NODE_NAME));
 525     if (reply == NULL) {
 526         crm_err("Could not respond to query from %s: could not create XML reply",
 527                  origin);
 528         return;
 529     }
 530     crm_log_xml_trace(reply, "Reply");
 531 
 532     /* Send the reply to the client */
 533     client->request_id = 0;
 534     {
 535         int rc = pcmk__ipc_send_xml(client, id, reply, flags);
 536 
 537         if (rc != pcmk_rc_ok) {
 538             crm_err("Could not respond to query from %s: %s " CRM_XS " rc=%d",
 539                     origin, pcmk_rc_str(rc), rc);
 540         }
 541     }
 542     free_xml(reply);
 543 }
 544 
 545 /*!
 546  * \internal
 547  * \brief Clear failure-related attributes
 548  *
 549  * \param[in] peer  Peer that sent clear request
 550  * \param[in] xml   Request XML
 551  */
 552 static void
 553 attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 554 {
 555     const char *rsc = crm_element_value(xml, PCMK__XA_ATTR_RESOURCE);
 556     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 557     const char *op = crm_element_value(xml, PCMK__XA_ATTR_OPERATION);
 558     const char *interval_spec = crm_element_value(xml, PCMK__XA_ATTR_INTERVAL);
 559     guint interval_ms = crm_parse_interval_spec(interval_spec);
 560     char *attr = NULL;
 561     GHashTableIter iter;
 562     regex_t regex;
 563 
 564     if (attrd_failure_regex(&regex, rsc, op, interval_ms) != pcmk_ok) {
 565         crm_info("Ignoring invalid request to clear failures for %s",
 566                  (rsc? rsc : "all resources"));
 567         return;
 568     }
 569 
 570     crm_xml_add(xml, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 571 
 572     /* Make sure value is not set, so we delete */
 573     if (crm_element_value(xml, PCMK__XA_ATTR_VALUE)) {
 574         crm_xml_replace(xml, PCMK__XA_ATTR_VALUE, NULL);
 575     }
 576 
 577     g_hash_table_iter_init(&iter, attributes);
 578     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 579         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 580             crm_trace("Matched %s when clearing %s",
 581                       attr, (rsc? rsc : "all resources"));
 582             crm_xml_add(xml, PCMK__XA_ATTR_NAME, attr);
 583             attrd_peer_update(peer, xml, host, FALSE);
 584         }
 585     }
 586     regfree(&regex);
 587 }
 588 
 589 /*!
 590  * \internal
 591  * \brief Load attributes from a peer sync response
 592  *
 593  * \param[in] peer      Peer that sent clear request
 594  * \param[in] peer_won  Whether peer is the attribute writer
 595  * \param[in] xml       Request XML
 596  */
 597 static void
 598 process_peer_sync_response(crm_node_t *peer, bool peer_won, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 599 {
 600     crm_info("Processing " PCMK__ATTRD_CMD_SYNC_RESPONSE " from %s",
 601              peer->uname);
 602 
 603     if (peer_won) {
 604         /* Initialize the "seen" flag for all attributes to cleared, so we can
 605          * detect attributes that local node has but the writer doesn't.
 606          */
 607         clear_attribute_value_seen();
 608     }
 609 
 610     // Process each attribute update in the sync response
 611     for (xmlNode *child = pcmk__xml_first_child(xml); child != NULL;
 612          child = pcmk__xml_next(child)) {
 613         attrd_peer_update(peer, child,
 614                           crm_element_value(child, PCMK__XA_ATTR_NODE_NAME),
 615                           TRUE);
 616     }
 617 
 618     if (peer_won) {
 619         /* If any attributes are still not marked as seen, the writer doesn't
 620          * know about them, so send all peers an update with them.
 621          */
 622         broadcast_unseen_local_values(peer, xml);
 623     }
 624 }
 625 
 626 /*!
 627     \internal
 628     \brief Broadcast private attribute for local node with protocol version
 629 */
 630 void
 631 attrd_broadcast_protocol()
     /* [previous][next][first][last][top][bottom][index][help] */
 632 {
 633     xmlNode *attrd_op = create_xml_node(NULL, __func__);
 634 
 635     crm_xml_add(attrd_op, F_TYPE, T_ATTRD);
 636     crm_xml_add(attrd_op, F_ORIG, crm_system_name);
 637     crm_xml_add(attrd_op, PCMK__XA_TASK, PCMK__ATTRD_CMD_UPDATE);
 638     crm_xml_add(attrd_op, PCMK__XA_ATTR_NAME, CRM_ATTR_PROTOCOL);
 639     crm_xml_add(attrd_op, PCMK__XA_ATTR_VALUE, ATTRD_PROTOCOL_VERSION);
 640     crm_xml_add_int(attrd_op, PCMK__XA_ATTR_IS_PRIVATE, 1);
 641     attrd_client_update(attrd_op);
 642     free_xml(attrd_op);
 643 }
 644 
 645 void
 646 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 647 {
 648     const char *op = crm_element_value(xml, PCMK__XA_TASK);
 649     const char *election_op = crm_element_value(xml, F_CRM_TASK);
 650     const char *host = crm_element_value(xml, PCMK__XA_ATTR_NODE_NAME);
 651     bool peer_won = false;
 652 
 653     if (election_op) {
 654         attrd_handle_election_op(peer, xml);
 655         return;
 656     }
 657 
 658     if (attrd_shutting_down()) {
 659         /* If we're shutting down, we want to continue responding to election
 660          * ops as long as we're a cluster member (because our vote may be
 661          * needed). Ignore all other messages.
 662          */
 663         return;
 664     }
 665 
 666     peer_won = attrd_check_for_new_writer(peer, xml);
 667 
 668     if (pcmk__strcase_any_of(op, PCMK__ATTRD_CMD_UPDATE, PCMK__ATTRD_CMD_UPDATE_BOTH,
 669                              PCMK__ATTRD_CMD_UPDATE_DELAY, NULL)) {
 670         attrd_peer_update(peer, xml, host, FALSE);
 671 
 672     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC, pcmk__str_casei)) {
 673         attrd_peer_sync(peer, xml);
 674 
 675     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_PEER_REMOVE, pcmk__str_casei)) {
 676         attrd_peer_remove(host, TRUE, peer->uname);
 677 
 678     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_CLEAR_FAILURE, pcmk__str_casei)) {
 679         /* It is not currently possible to receive this as a peer command,
 680          * but will be, if we one day enable propagating this operation.
 681          */
 682         attrd_peer_clear_failure(peer, xml);
 683 
 684     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_SYNC_RESPONSE, pcmk__str_casei)
 685                && !pcmk__str_eq(peer->uname, attrd_cluster->uname, pcmk__str_casei)) {
 686         process_peer_sync_response(peer, peer_won, xml);
 687 
 688     } else if (pcmk__str_eq(op, PCMK__ATTRD_CMD_FLUSH, pcmk__str_casei)) {
 689         /* Ignore. The flush command was removed in 2.0.0 but may be
 690          * received from peers running older versions.
 691          */
 692     }
 693 }
 694 
 695 void
 696 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 697 {
 698     GHashTableIter aIter;
 699     GHashTableIter vIter;
 700 
 701     attribute_t *a = NULL;
 702     attribute_value_t *v = NULL;
 703     xmlNode *sync = create_xml_node(NULL, __func__);
 704 
 705     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 706 
 707     g_hash_table_iter_init(&aIter, attributes);
 708     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 709         g_hash_table_iter_init(&vIter, a->values);
 710         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 711             crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
 712             add_attribute_value_xml(sync, a, v, false);
 713         }
 714     }
 715 
 716     crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
 717     send_attrd_message(peer, sync);
 718     free_xml(sync);
 719 }
 720 
 721 /*!
 722  * \internal
 723  * \brief Remove all attributes and optionally peer cache entries for a node
 724  *
 725  * \param[in] host     Name of node to purge
 726  * \param[in] uncache  If TRUE, remove node from peer caches
 727  * \param[in] source   Who requested removal (only used for logging)
 728  */
 729 void
 730 attrd_peer_remove(const char *host, gboolean uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 731 {
 732     attribute_t *a = NULL;
 733     GHashTableIter aIter;
 734 
 735     CRM_CHECK(host != NULL, return);
 736     crm_notice("Removing all %s attributes for peer %s", host, source);
 737 
 738     g_hash_table_iter_init(&aIter, attributes);
 739     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 740         if(g_hash_table_remove(a->values, host)) {
 741             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 742         }
 743     }
 744 
 745     if (uncache) {
 746         crm_remote_peer_cache_remove(host);
 747         reap_crm_member(0, host);
 748     }
 749 }
 750 
 751 /*!
 752  * \internal
 753  * \brief Return host's hash table entry (creating one if needed)
 754  *
 755  * \param[in] values Hash table of values
 756  * \param[in] host Name of peer to look up
 757  * \param[in] xml XML describing the attribute
 758  *
 759  * \return Pointer to new or existing hash table entry
 760  */
 761 static attribute_value_t *
 762 attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 763 {
 764     attribute_value_t *v = g_hash_table_lookup(values, host);
 765     int is_remote = 0;
 766 
 767     crm_element_value_int(xml, PCMK__XA_ATTR_IS_REMOTE, &is_remote);
 768     if (is_remote) {
 769         cache_remote_node(host);
 770     }
 771 
 772     if (v == NULL) {
 773         v = calloc(1, sizeof(attribute_value_t));
 774         CRM_ASSERT(v != NULL);
 775 
 776         v->nodename = strdup(host);
 777         CRM_ASSERT(v->nodename != NULL);
 778 
 779         v->is_remote = is_remote;
 780         g_hash_table_replace(values, v->nodename, v);
 781     }
 782     return(v);
 783 }
 784 
 785 void
 786 broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 787 {
 788     GHashTableIter aIter;
 789     GHashTableIter vIter;
 790     attribute_t *a = NULL;
 791     attribute_value_t *v = NULL;
 792     xmlNode *sync = NULL;
 793 
 794     g_hash_table_iter_init(&aIter, attributes);
 795     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 796         g_hash_table_iter_init(&vIter, a->values);
 797         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 798             if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
 799                                            pcmk__str_casei)) {
 800                 if (sync == NULL) {
 801                     sync = create_xml_node(NULL, __func__);
 802                     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 803                 }
 804                 add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
 805             }
 806         }
 807     }
 808 
 809     if (sync != NULL) {
 810         crm_debug("Broadcasting local-only values");
 811         send_attrd_message(NULL, sync);
 812         free_xml(sync);
 813     }
 814 }
 815 
 816 /*!
 817  * \internal
 818  * \brief Override an attribute sync with a local value
 819  *
 820  * Broadcast the local node's value for an attribute that's different from the
 821  * value provided in a peer's attribute synchronization response. This ensures a
 822  * node's values for itself take precedence and all peers are kept in sync.
 823  *
 824  * \param[in] a          Attribute entry to override
 825  *
 826  * \return Local instance of attribute value
 827  */
 828 static attribute_value_t *
 829 broadcast_local_value(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 830 {
 831     attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
 832     xmlNode *sync = create_xml_node(NULL, __func__);
 833 
 834     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 835     add_attribute_value_xml(sync, a, v, false);
 836     attrd_xml_add_writer(sync);
 837     send_attrd_message(NULL, sync);
 838     free_xml(sync);
 839     return v;
 840 }
 841 
 842 void
 843 attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 844 {
 845     bool update_both = FALSE;
 846     attribute_t *a;
 847     attribute_value_t *v = NULL;
 848     gboolean is_force_write = FALSE;
 849 
 850     const char *op = crm_element_value(xml, PCMK__XA_TASK);
 851     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 852     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 853     crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
 854 
 855     if (attr == NULL) {
 856         crm_warn("Could not update attribute: peer did not specify name");
 857         return;
 858     }
 859 
 860     // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK__XA_TASK
 861     update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
 862                                pcmk__str_null_matches | pcmk__str_casei);
 863 
 864     // Look up or create attribute entry
 865     a = g_hash_table_lookup(attributes, attr);
 866     if (a == NULL) {
 867         if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_casei)) {
 868             a = create_attribute(xml);
 869         } else {
 870             crm_warn("Could not update %s: attribute not found", attr);
 871             return;
 872         }
 873     }
 874 
 875     // Update attribute dampening
 876     if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_casei)) {
 877         const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
 878         int dampen = 0;
 879 
 880         if (dvalue == NULL) {
 881             crm_warn("Could not update %s: peer did not specify value for delay",
 882                      attr);
 883             return;
 884         }
 885 
 886         dampen = crm_get_msec(dvalue);
 887         if (dampen < 0) {
 888             crm_warn("Could not update %s: invalid delay value %dms (%s)",
 889                      attr, dampen, dvalue);
 890             return;
 891         }
 892 
 893         if (a->timeout_ms != dampen) {
 894             mainloop_timer_del(a->timer);
 895             a->timeout_ms = dampen;
 896             if (dampen > 0) {
 897                 a->timer = mainloop_timer_add(attr, a->timeout_ms, FALSE,
 898                                               attribute_timer_cb, a);
 899                 crm_info("Update attribute %s delay to %dms (%s)",
 900                          attr, dampen, dvalue);
 901             } else {
 902                 a->timer = NULL;
 903                 crm_info("Update attribute %s to remove delay", attr);
 904             }
 905 
 906             /* If dampening changed, do an immediate write-out,
 907              * otherwise repeated dampening changes would prevent write-outs
 908              */
 909             write_or_elect_attribute(a);
 910         }
 911 
 912         if (!update_both) {
 913             return;
 914         }
 915     }
 916 
 917     // If no host was specified, update all hosts recursively
 918     if (host == NULL) {
 919         GHashTableIter vIter;
 920 
 921         crm_debug("Setting %s for all hosts to %s", attr, value);
 922         xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
 923         g_hash_table_iter_init(&vIter, a->values);
 924         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 925             attrd_peer_update(peer, xml, host, filter);
 926         }
 927         return;
 928     }
 929 
 930     // Update attribute value for one host
 931 
 932     v = attrd_lookup_or_create_value(a->values, host, xml);
 933 
 934     if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
 935         && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
 936 
 937         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 938                    attr, host, v->current, value, peer->uname);
 939         v = broadcast_local_value(a);
 940 
 941     } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
 942         crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
 943                    attr, host, v->current? v->current : "(unset)", value? value : "(unset)", peer->uname);
 944         free(v->current);
 945         v->current = (value? strdup(value) : NULL);
 946         a->changed = TRUE;
 947 
 948         if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
 949             && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
 950 
 951             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 952                 attrd_set_requesting_shutdown();
 953 
 954             } else {
 955                 attrd_clear_requesting_shutdown();
 956             }
 957         }
 958 
 959         // Write out new value or start dampening timer
 960         if (a->timeout_ms && a->timer) {
 961             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 962             mainloop_timer_start(a->timer);
 963         } else {
 964             write_or_elect_attribute(a);
 965         }
 966 
 967     } else {
 968         if (is_force_write && a->timeout_ms && a->timer) {
 969             /* Save forced writing and set change flag. */
 970             /* The actual attribute is written by Writer after election. */
 971             crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)", attr, host, peer->uname, value);
 972             a->force_write = TRUE;
 973         } else {
 974             crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 975         }
 976     }
 977 
 978     /* Set the seen flag for attribute processing held only in the own node. */
 979     v->seen = TRUE;
 980 
 981     /* If this is a cluster node whose node ID we are learning, remember it */
 982     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 983         && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
 984                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 985 
 986         crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 987 
 988         crm_trace("Learned %s has node id %s",
 989                   known_peer->uname, known_peer->uuid);
 990         if (attrd_election_won()) {
 991             write_attributes(FALSE, FALSE);
 992         }
 993     }
 994 }
 995 
 996 void
 997 write_or_elect_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 998 {
 999     if (attrd_election_won()) {
1000         write_attribute(a, FALSE);
1001     } else {
1002         attrd_start_election_if_needed();
1003     }
1004 }
1005 
1006 gboolean
1007 attrd_election_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1008 {
1009     attrd_declare_winner();
1010 
1011     /* Update the peers after an election */
1012     attrd_peer_sync(NULL, NULL);
1013 
1014     /* Update the CIB after an election */
1015     write_attributes(TRUE, FALSE);
1016     return FALSE;
1017 }
1018 
1019 #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
1020 
1021 void
1022 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1023 {
1024     bool gone = false;
1025     bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
1026 
1027     switch (kind) {
1028         case crm_status_uname:
1029             crm_debug("%s node %s is now %s",
1030                       (is_remote? "Remote" : "Cluster"),
1031                       peer->uname, state_text(peer->state));
1032             break;
1033 
1034         case crm_status_processes:
1035             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
1036                 gone = true;
1037             }
1038             crm_debug("Node %s is %s a peer",
1039                       peer->uname, (gone? "no longer" : "now"));
1040             break;
1041 
1042         case crm_status_nstate:
1043             crm_debug("%s node %s is now %s (was %s)",
1044                       (is_remote? "Remote" : "Cluster"),
1045                       peer->uname, state_text(peer->state), state_text(data));
1046             if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
1047                 /* If we're the writer, send new peers a list of all attributes
1048                  * (unless it's a remote node, which doesn't run its own attrd)
1049                  */
1050                 if (attrd_election_won()
1051                     && !pcmk_is_set(peer->flags, crm_remote_node)) {
1052                     attrd_peer_sync(peer, NULL);
1053                 }
1054             } else {
1055                 // Remove all attribute values associated with lost nodes
1056                 attrd_peer_remove(peer->uname, FALSE, "loss");
1057                 gone = true;
1058             }
1059             break;
1060     }
1061 
1062     // Remove votes from cluster nodes that leave, in case election in progress
1063     if (gone && !is_remote) {
1064         attrd_remove_voter(peer);
1065 
1066     // Ensure remote nodes that come up are in the remote node cache
1067     } else if (!gone && is_remote) {
1068         cache_remote_node(peer->uname);
1069     }
1070 }
1071 
1072 static void
1073 attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1074 {
1075     int level = LOG_ERR;
1076     GHashTableIter iter;
1077     const char *peer = NULL;
1078     attribute_value_t *v = NULL;
1079 
1080     char *name = user_data;
1081     attribute_t *a = g_hash_table_lookup(attributes, name);
1082 
1083     if(a == NULL) {
1084         crm_info("Attribute %s no longer exists", name);
1085         return;
1086     }
1087 
1088     a->update = 0;
1089     if (rc == pcmk_ok && call_id < 0) {
1090         rc = call_id;
1091     }
1092 
1093     switch (rc) {
1094         case pcmk_ok:
1095             level = LOG_INFO;
1096             last_cib_op_done = call_id;
1097             if (a->timer && !a->timeout_ms) {
1098                 // Remove temporary dampening for failed writes
1099                 mainloop_timer_del(a->timer);
1100                 a->timer = NULL;
1101             }
1102             break;
1103 
1104         case -pcmk_err_diff_failed:    /* When an attr changes while the CIB is syncing */
1105         case -ETIME:           /* When an attr changes while there is a DC election */
1106         case -ENXIO:           /* When an attr changes while the CIB is syncing a
1107                                 *   newer config from a node that just came up
1108                                 */
1109             level = LOG_WARNING;
1110             break;
1111     }
1112 
1113     do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
1114                call_id, a->id, pcmk_strerror(rc), rc);
1115 
1116     g_hash_table_iter_init(&iter, a->values);
1117     while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
1118         do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
1119         free(v->requested);
1120         v->requested = NULL;
1121         if (rc != pcmk_ok) {
1122             a->changed = TRUE; /* Attempt write out again */
1123         }
1124     }
1125 
1126     if (a->changed && attrd_election_won()) {
1127         if (rc == pcmk_ok) {
1128             /* We deferred a write of a new update because this update was in
1129              * progress. Write out the new value without additional delay.
1130              */
1131             write_attribute(a, FALSE);
1132 
1133         /* We're re-attempting a write because the original failed; delay
1134          * the next attempt so we don't potentially flood the CIB manager
1135          * and logs with a zillion attempts per second.
1136          *
1137          * @TODO We could elect a new writer instead. However, we'd have to
1138          * somehow downgrade our vote, and we'd still need something like this
1139          * if all peers similarly fail to write this attribute (which may
1140          * indicate a corrupted attribute entry rather than a CIB issue).
1141          */
1142         } else if (a->timer) {
1143             // Attribute has a dampening value, so use that as delay
1144             if (!mainloop_timer_running(a->timer)) {
1145                 crm_trace("Delayed re-attempted write (%dms) for %s",
1146                           a->timeout_ms, name);
1147                 mainloop_timer_start(a->timer);
1148             }
1149         } else {
1150             /* Set a temporary dampening of 2 seconds (timer will continue
1151              * to exist until the attribute's dampening gets set or the
1152              * write succeeds).
1153              */
1154             a->timer = mainloop_timer_add(a->id, 2000, FALSE,
1155                                           attribute_timer_cb, a);
1156             mainloop_timer_start(a->timer);
1157         }
1158     }
1159 }
1160 
1161 void
1162 write_attributes(bool all, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
1163 {
1164     GHashTableIter iter;
1165     attribute_t *a = NULL;
1166 
1167     crm_debug("Writing out %s attributes", all? "all" : "changed");
1168     g_hash_table_iter_init(&iter, attributes);
1169     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
1170         if (!all && a->unknown_peer_uuids) {
1171             // Try writing this attribute again, in case peer ID was learned
1172             a->changed = TRUE;
1173         } else if (a->force_write) {
1174             /* If the force_write flag is set, write the attribute. */
1175             a->changed = TRUE;
1176         }
1177 
1178         if(all || a->changed) {
1179             /* When forced write flag is set, ignore delay. */
1180             write_attribute(a, (a->force_write ? TRUE : ignore_delay));
1181         } else {
1182             crm_trace("Skipping unchanged attribute %s", a->id);
1183         }
1184     }
1185 }
1186 
1187 static void
1188 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1189 {
1190     const char *set = NULL;
1191     xmlNode *xml_obj = NULL;
1192 
1193     xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
1194     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1195 
1196     xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
1197     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1198 
1199     xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
1200     if (a->set) {
1201         crm_xml_set_id(xml_obj, "%s", a->set);
1202     } else {
1203         crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
1204     }
1205     set = ID(xml_obj);
1206 
1207     xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
1208     if (a->uuid) {
1209         crm_xml_set_id(xml_obj, "%s", a->uuid);
1210     } else {
1211         crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
1212     }
1213     crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
1214 
1215     if(value) {
1216         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
1217 
1218     } else {
1219         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
1220         crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
1221     }
1222 }
1223 
1224 static void
1225 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
1226 {
1227     attribute_value_t *a_v = NULL;
1228     a_v = calloc(1, sizeof(attribute_value_t));
1229     CRM_ASSERT(a_v != NULL);
1230 
1231     a_v->nodeid = v->nodeid;
1232     a_v->nodename = strdup(v->nodename);
1233 
1234     if (v->current != NULL) {
1235         a_v->current = strdup(v->current);
1236     }
1237 
1238     g_hash_table_replace(t, a_v->nodename, a_v);
1239 }
1240 
1241 static void
1242 send_alert_attributes_value(attribute_t *a, GHashTable *t)
     /* [previous][next][first][last][top][bottom][index][help] */
1243 {
1244     int rc = 0;
1245     attribute_value_t *at = NULL;
1246     GHashTableIter vIter;
1247 
1248     g_hash_table_iter_init(&vIter, t);
1249 
1250     while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
1251         rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
1252                                         a->id, at->current);
1253         crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
1254                   a->id, at->nodename, at->current, at->nodeid, rc);
1255     }
1256 }
1257 
1258 void
1259 write_attribute(attribute_t *a, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
1260 {
1261     int private_updates = 0, cib_updates = 0;
1262     xmlNode *xml_top = NULL;
1263     attribute_value_t *v = NULL;
1264     GHashTableIter iter;
1265     enum cib_call_options flags = cib_quorum_override;
1266     GHashTable *alert_attribute_value = NULL;
1267 
1268     if (a == NULL) {
1269         return;
1270     }
1271 
1272     /* If this attribute will be written to the CIB ... */
1273     if (!a->is_private) {
1274 
1275         /* Defer the write if now's not a good time */
1276         CRM_CHECK(the_cib != NULL, return);
1277         if (a->update && (a->update < last_cib_op_done)) {
1278             crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
1279             a->update = 0; // Don't log this message again
1280 
1281         } else if (a->update) {
1282             crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
1283             return;
1284 
1285         } else if (mainloop_timer_running(a->timer)) {
1286             if (ignore_delay) {
1287                 /* 'refresh' forces a write of the current value of all attributes
1288                  * Cancel any existing timers, we're writing it NOW
1289                  */
1290                 mainloop_timer_stop(a->timer);
1291                 crm_debug("Write out of '%s': timer is running but ignore delay", a->id);
1292             } else {
1293                 crm_info("Write out of '%s' delayed: timer is running", a->id);
1294                 return;
1295             }
1296         }
1297 
1298         /* Initialize the status update XML */
1299         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
1300     }
1301 
1302     /* Attribute will be written shortly, so clear changed flag */
1303     a->changed = FALSE;
1304 
1305     /* We will check all peers' uuids shortly, so initialize this to false */
1306     a->unknown_peer_uuids = FALSE;
1307 
1308     /* Attribute will be written shortly, so clear forced write flag */
1309     a->force_write = FALSE;    
1310 
1311     /* Make the table for the attribute trap */
1312     alert_attribute_value = pcmk__strikey_table(NULL, free_attribute_value);
1313 
1314     /* Iterate over each peer value of this attribute */
1315     g_hash_table_iter_init(&iter, a->values);
1316     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
1317         crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
1318 
1319         /* If the value's peer info does not correspond to a peer, ignore it */
1320         if (peer == NULL) {
1321             crm_notice("Cannot update %s[%s]=%s because peer not known",
1322                        a->id, v->nodename, v->current);
1323             continue;
1324         }
1325 
1326         /* If we're just learning the peer's node id, remember it */
1327         if (peer->id && (v->nodeid == 0)) {
1328             crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
1329             v->nodeid = peer->id;
1330         }
1331 
1332         /* If this is a private attribute, no update needs to be sent */
1333         if (a->is_private) {
1334             private_updates++;
1335             continue;
1336         }
1337 
1338         /* If the peer is found, but its uuid is unknown, defer write */
1339         if (peer->uuid == NULL) {
1340             a->unknown_peer_uuids = TRUE;
1341             crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
1342                        "(will retry if learned)",
1343                        a->id, v->nodename, v->current);
1344             continue;
1345         }
1346 
1347         /* Add this value to status update XML */
1348         crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
1349                   a->id, v->nodename, v->current,
1350                   peer->uname, peer->uuid, peer->id, v->nodeid);
1351         build_update_element(xml_top, a, peer->uuid, v->current);
1352         cib_updates++;
1353 
1354         /* Preservation of the attribute to transmit alert */
1355         set_alert_attribute_value(alert_attribute_value, v);
1356 
1357         free(v->requested);
1358         v->requested = NULL;
1359         if (v->current) {
1360             v->requested = strdup(v->current);
1361         } else {
1362             /* Older attrd versions don't know about the cib_mixed_update
1363              * flag so make sure it goes to the local cib which does
1364              */
1365             cib__set_call_options(flags, crm_system_name,
1366                                   cib_mixed_update|cib_scope_local);
1367         }
1368     }
1369 
1370     if (private_updates) {
1371         crm_info("Processed %d private change%s for %s, id=%s, set=%s",
1372                  private_updates, pcmk__plural_s(private_updates),
1373                  a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1374     }
1375     if (cib_updates) {
1376         crm_log_xml_trace(xml_top, __func__);
1377 
1378         a->update = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL,
1379                                     flags, a->user);
1380 
1381         crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
1382                  a->update, cib_updates, pcmk__plural_s(cib_updates),
1383                  a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1384 
1385         the_cib->cmds->register_callback_full(the_cib, a->update,
1386                                               CIB_OP_TIMEOUT_S, FALSE,
1387                                               strdup(a->id),
1388                                               "attrd_cib_callback",
1389                                               attrd_cib_callback, free);
1390         /* Transmit alert of the attribute */
1391         send_alert_attributes_value(a, alert_attribute_value);
1392     }
1393 
1394     g_hash_table_destroy(alert_attribute_value);
1395     free_xml(xml_top);
1396 }

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