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-2022 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         pcmk__str_update(&v->nodename, host);
 777         v->is_remote = is_remote;
 778         g_hash_table_replace(values, v->nodename, v);
 779     }
 780     return(v);
 781 }
 782 
 783 void
 784 broadcast_unseen_local_values(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 785 {
 786     GHashTableIter aIter;
 787     GHashTableIter vIter;
 788     attribute_t *a = NULL;
 789     attribute_value_t *v = NULL;
 790     xmlNode *sync = NULL;
 791 
 792     g_hash_table_iter_init(&aIter, attributes);
 793     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 794         g_hash_table_iter_init(&vIter, a->values);
 795         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 796             if (!(v->seen) && pcmk__str_eq(v->nodename, attrd_cluster->uname,
 797                                            pcmk__str_casei)) {
 798                 if (sync == NULL) {
 799                     sync = create_xml_node(NULL, __func__);
 800                     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 801                 }
 802                 add_attribute_value_xml(sync, a, v, a->timeout_ms && a->timer);
 803             }
 804         }
 805     }
 806 
 807     if (sync != NULL) {
 808         crm_debug("Broadcasting local-only values");
 809         send_attrd_message(NULL, sync);
 810         free_xml(sync);
 811     }
 812 }
 813 
 814 /*!
 815  * \internal
 816  * \brief Override an attribute sync with a local value
 817  *
 818  * Broadcast the local node's value for an attribute that's different from the
 819  * value provided in a peer's attribute synchronization response. This ensures a
 820  * node's values for itself take precedence and all peers are kept in sync.
 821  *
 822  * \param[in] a          Attribute entry to override
 823  *
 824  * \return Local instance of attribute value
 825  */
 826 static attribute_value_t *
 827 broadcast_local_value(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 828 {
 829     attribute_value_t *v = g_hash_table_lookup(a->values, attrd_cluster->uname);
 830     xmlNode *sync = create_xml_node(NULL, __func__);
 831 
 832     crm_xml_add(sync, PCMK__XA_TASK, PCMK__ATTRD_CMD_SYNC_RESPONSE);
 833     add_attribute_value_xml(sync, a, v, false);
 834     attrd_xml_add_writer(sync);
 835     send_attrd_message(NULL, sync);
 836     free_xml(sync);
 837     return v;
 838 }
 839 
 840 void
 841 attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 842 {
 843     bool update_both = FALSE;
 844     attribute_t *a;
 845     attribute_value_t *v = NULL;
 846     gboolean is_force_write = FALSE;
 847 
 848     const char *op = crm_element_value(xml, PCMK__XA_TASK);
 849     const char *attr = crm_element_value(xml, PCMK__XA_ATTR_NAME);
 850     const char *value = crm_element_value(xml, PCMK__XA_ATTR_VALUE);
 851     crm_element_value_int(xml, PCMK__XA_ATTR_FORCE, &is_force_write);
 852 
 853     if (attr == NULL) {
 854         crm_warn("Could not update attribute: peer did not specify name");
 855         return;
 856     }
 857 
 858     // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK__XA_TASK
 859     update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
 860                                pcmk__str_null_matches | pcmk__str_casei);
 861 
 862     // Look up or create attribute entry
 863     a = g_hash_table_lookup(attributes, attr);
 864     if (a == NULL) {
 865         if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_casei)) {
 866             a = create_attribute(xml);
 867         } else {
 868             crm_warn("Could not update %s: attribute not found", attr);
 869             return;
 870         }
 871     }
 872 
 873     // Update attribute dampening
 874     if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_casei)) {
 875         const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
 876         int dampen = 0;
 877 
 878         if (dvalue == NULL) {
 879             crm_warn("Could not update %s: peer did not specify value for delay",
 880                      attr);
 881             return;
 882         }
 883 
 884         dampen = crm_get_msec(dvalue);
 885         if (dampen < 0) {
 886             crm_warn("Could not update %s: invalid delay value %dms (%s)",
 887                      attr, dampen, dvalue);
 888             return;
 889         }
 890 
 891         if (a->timeout_ms != dampen) {
 892             mainloop_timer_del(a->timer);
 893             a->timeout_ms = dampen;
 894             if (dampen > 0) {
 895                 a->timer = mainloop_timer_add(attr, a->timeout_ms, FALSE,
 896                                               attribute_timer_cb, a);
 897                 crm_info("Update attribute %s delay to %dms (%s)",
 898                          attr, dampen, dvalue);
 899             } else {
 900                 a->timer = NULL;
 901                 crm_info("Update attribute %s to remove delay", attr);
 902             }
 903 
 904             /* If dampening changed, do an immediate write-out,
 905              * otherwise repeated dampening changes would prevent write-outs
 906              */
 907             write_or_elect_attribute(a);
 908         }
 909 
 910         if (!update_both) {
 911             return;
 912         }
 913     }
 914 
 915     // If no host was specified, update all hosts recursively
 916     if (host == NULL) {
 917         GHashTableIter vIter;
 918 
 919         crm_debug("Setting %s for all hosts to %s", attr, value);
 920         xml_remove_prop(xml, PCMK__XA_ATTR_NODE_ID);
 921         g_hash_table_iter_init(&vIter, a->values);
 922         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 923             attrd_peer_update(peer, xml, host, filter);
 924         }
 925         return;
 926     }
 927 
 928     // Update attribute value for one host
 929 
 930     v = attrd_lookup_or_create_value(a->values, host, xml);
 931 
 932     if (filter && !pcmk__str_eq(v->current, value, pcmk__str_casei)
 933         && pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)) {
 934 
 935         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 936                    attr, host, v->current, value, peer->uname);
 937         v = broadcast_local_value(a);
 938 
 939     } else if (!pcmk__str_eq(v->current, value, pcmk__str_casei)) {
 940         crm_notice("Setting %s[%s]: %s -> %s " CRM_XS " from %s",
 941                    attr, host, v->current? v->current : "(unset)", value? value : "(unset)", peer->uname);
 942         pcmk__str_update(&v->current, value);
 943         a->changed = TRUE;
 944 
 945         if (pcmk__str_eq(host, attrd_cluster->uname, pcmk__str_casei)
 946             && pcmk__str_eq(attr, XML_CIB_ATTR_SHUTDOWN, pcmk__str_none)) {
 947 
 948             if (!pcmk__str_eq(value, "0", pcmk__str_null_matches)) {
 949                 attrd_set_requesting_shutdown();
 950 
 951             } else {
 952                 attrd_clear_requesting_shutdown();
 953             }
 954         }
 955 
 956         // Write out new value or start dampening timer
 957         if (a->timeout_ms && a->timer) {
 958             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 959             mainloop_timer_start(a->timer);
 960         } else {
 961             write_or_elect_attribute(a);
 962         }
 963 
 964     } else {
 965         if (is_force_write && a->timeout_ms && a->timer) {
 966             /* Save forced writing and set change flag. */
 967             /* The actual attribute is written by Writer after election. */
 968             crm_trace("Unchanged %s[%s] from %s is %s(Set the forced write flag)", attr, host, peer->uname, value);
 969             a->force_write = TRUE;
 970         } else {
 971             crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 972         }
 973     }
 974 
 975     /* Set the seen flag for attribute processing held only in the own node. */
 976     v->seen = TRUE;
 977 
 978     /* If this is a cluster node whose node ID we are learning, remember it */
 979     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 980         && (crm_element_value_int(xml, PCMK__XA_ATTR_NODE_ID,
 981                                   (int*)&v->nodeid) == 0) && (v->nodeid > 0)) {
 982 
 983         crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 984 
 985         crm_trace("Learned %s has node id %s",
 986                   known_peer->uname, known_peer->uuid);
 987         if (attrd_election_won()) {
 988             write_attributes(FALSE, FALSE);
 989         }
 990     }
 991 }
 992 
 993 void
 994 write_or_elect_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 995 {
 996     if (attrd_election_won()) {
 997         write_attribute(a, FALSE);
 998     } else {
 999         attrd_start_election_if_needed();
1000     }
1001 }
1002 
1003 gboolean
1004 attrd_election_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1005 {
1006     attrd_declare_winner();
1007 
1008     /* Update the peers after an election */
1009     attrd_peer_sync(NULL, NULL);
1010 
1011     /* Update the CIB after an election */
1012     write_attributes(TRUE, FALSE);
1013     return FALSE;
1014 }
1015 
1016 #define state_text(state) ((state)? (const char *)(state) : "in unknown state")
1017 
1018 void
1019 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
1020 {
1021     bool gone = false;
1022     bool is_remote = pcmk_is_set(peer->flags, crm_remote_node);
1023 
1024     switch (kind) {
1025         case crm_status_uname:
1026             crm_debug("%s node %s is now %s",
1027                       (is_remote? "Remote" : "Cluster"),
1028                       peer->uname, state_text(peer->state));
1029             break;
1030 
1031         case crm_status_processes:
1032             if (!pcmk_is_set(peer->processes, crm_get_cluster_proc())) {
1033                 gone = true;
1034             }
1035             crm_debug("Node %s is %s a peer",
1036                       peer->uname, (gone? "no longer" : "now"));
1037             break;
1038 
1039         case crm_status_nstate:
1040             crm_debug("%s node %s is now %s (was %s)",
1041                       (is_remote? "Remote" : "Cluster"),
1042                       peer->uname, state_text(peer->state), state_text(data));
1043             if (pcmk__str_eq(peer->state, CRM_NODE_MEMBER, pcmk__str_casei)) {
1044                 /* If we're the writer, send new peers a list of all attributes
1045                  * (unless it's a remote node, which doesn't run its own attrd)
1046                  */
1047                 if (attrd_election_won()
1048                     && !pcmk_is_set(peer->flags, crm_remote_node)) {
1049                     attrd_peer_sync(peer, NULL);
1050                 }
1051             } else {
1052                 // Remove all attribute values associated with lost nodes
1053                 attrd_peer_remove(peer->uname, FALSE, "loss");
1054                 gone = true;
1055             }
1056             break;
1057     }
1058 
1059     // Remove votes from cluster nodes that leave, in case election in progress
1060     if (gone && !is_remote) {
1061         attrd_remove_voter(peer);
1062 
1063     // Ensure remote nodes that come up are in the remote node cache
1064     } else if (!gone && is_remote) {
1065         cache_remote_node(peer->uname);
1066     }
1067 }
1068 
1069 static void
1070 attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1071 {
1072     int level = LOG_ERR;
1073     GHashTableIter iter;
1074     const char *peer = NULL;
1075     attribute_value_t *v = NULL;
1076 
1077     char *name = user_data;
1078     attribute_t *a = g_hash_table_lookup(attributes, name);
1079 
1080     if(a == NULL) {
1081         crm_info("Attribute %s no longer exists", name);
1082         return;
1083     }
1084 
1085     a->update = 0;
1086     if (rc == pcmk_ok && call_id < 0) {
1087         rc = call_id;
1088     }
1089 
1090     switch (rc) {
1091         case pcmk_ok:
1092             level = LOG_INFO;
1093             last_cib_op_done = call_id;
1094             if (a->timer && !a->timeout_ms) {
1095                 // Remove temporary dampening for failed writes
1096                 mainloop_timer_del(a->timer);
1097                 a->timer = NULL;
1098             }
1099             break;
1100 
1101         case -pcmk_err_diff_failed:    /* When an attr changes while the CIB is syncing */
1102         case -ETIME:           /* When an attr changes while there is a DC election */
1103         case -ENXIO:           /* When an attr changes while the CIB is syncing a
1104                                 *   newer config from a node that just came up
1105                                 */
1106             level = LOG_WARNING;
1107             break;
1108     }
1109 
1110     do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
1111                call_id, a->id, pcmk_strerror(rc), rc);
1112 
1113     g_hash_table_iter_init(&iter, a->values);
1114     while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
1115         do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
1116         free(v->requested);
1117         v->requested = NULL;
1118         if (rc != pcmk_ok) {
1119             a->changed = TRUE; /* Attempt write out again */
1120         }
1121     }
1122 
1123     if (a->changed && attrd_election_won()) {
1124         if (rc == pcmk_ok) {
1125             /* We deferred a write of a new update because this update was in
1126              * progress. Write out the new value without additional delay.
1127              */
1128             write_attribute(a, FALSE);
1129 
1130         /* We're re-attempting a write because the original failed; delay
1131          * the next attempt so we don't potentially flood the CIB manager
1132          * and logs with a zillion attempts per second.
1133          *
1134          * @TODO We could elect a new writer instead. However, we'd have to
1135          * somehow downgrade our vote, and we'd still need something like this
1136          * if all peers similarly fail to write this attribute (which may
1137          * indicate a corrupted attribute entry rather than a CIB issue).
1138          */
1139         } else if (a->timer) {
1140             // Attribute has a dampening value, so use that as delay
1141             if (!mainloop_timer_running(a->timer)) {
1142                 crm_trace("Delayed re-attempted write (%dms) for %s",
1143                           a->timeout_ms, name);
1144                 mainloop_timer_start(a->timer);
1145             }
1146         } else {
1147             /* Set a temporary dampening of 2 seconds (timer will continue
1148              * to exist until the attribute's dampening gets set or the
1149              * write succeeds).
1150              */
1151             a->timer = mainloop_timer_add(a->id, 2000, FALSE,
1152                                           attribute_timer_cb, a);
1153             mainloop_timer_start(a->timer);
1154         }
1155     }
1156 }
1157 
1158 void
1159 write_attributes(bool all, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
1160 {
1161     GHashTableIter iter;
1162     attribute_t *a = NULL;
1163 
1164     crm_debug("Writing out %s attributes", all? "all" : "changed");
1165     g_hash_table_iter_init(&iter, attributes);
1166     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
1167         if (!all && a->unknown_peer_uuids) {
1168             // Try writing this attribute again, in case peer ID was learned
1169             a->changed = TRUE;
1170         } else if (a->force_write) {
1171             /* If the force_write flag is set, write the attribute. */
1172             a->changed = TRUE;
1173         }
1174 
1175         if(all || a->changed) {
1176             /* When forced write flag is set, ignore delay. */
1177             write_attribute(a, (a->force_write ? TRUE : ignore_delay));
1178         } else {
1179             crm_trace("Skipping unchanged attribute %s", a->id);
1180         }
1181     }
1182 }
1183 
1184 static void
1185 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1186 {
1187     const char *set = NULL;
1188     xmlNode *xml_obj = NULL;
1189 
1190     xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
1191     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1192 
1193     xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
1194     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1195 
1196     xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
1197     if (a->set) {
1198         crm_xml_set_id(xml_obj, "%s", a->set);
1199     } else {
1200         crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
1201     }
1202     set = ID(xml_obj);
1203 
1204     xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
1205     if (a->uuid) {
1206         crm_xml_set_id(xml_obj, "%s", a->uuid);
1207     } else {
1208         crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
1209     }
1210     crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
1211 
1212     if(value) {
1213         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
1214 
1215     } else {
1216         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
1217         crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
1218     }
1219 }
1220 
1221 static void
1222 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
1223 {
1224     attribute_value_t *a_v = NULL;
1225     a_v = calloc(1, sizeof(attribute_value_t));
1226     CRM_ASSERT(a_v != NULL);
1227 
1228     a_v->nodeid = v->nodeid;
1229     a_v->nodename = strdup(v->nodename);
1230     pcmk__str_update(&a_v->current, v->current);
1231 
1232     g_hash_table_replace(t, a_v->nodename, a_v);
1233 }
1234 
1235 static void
1236 send_alert_attributes_value(attribute_t *a, GHashTable *t)
     /* [previous][next][first][last][top][bottom][index][help] */
1237 {
1238     int rc = 0;
1239     attribute_value_t *at = NULL;
1240     GHashTableIter vIter;
1241 
1242     g_hash_table_iter_init(&vIter, t);
1243 
1244     while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
1245         rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
1246                                         a->id, at->current);
1247         crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
1248                   a->id, at->nodename, at->current, at->nodeid, rc);
1249     }
1250 }
1251 
1252 void
1253 write_attribute(attribute_t *a, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
1254 {
1255     int private_updates = 0, cib_updates = 0;
1256     xmlNode *xml_top = NULL;
1257     attribute_value_t *v = NULL;
1258     GHashTableIter iter;
1259     enum cib_call_options flags = cib_quorum_override;
1260     GHashTable *alert_attribute_value = NULL;
1261 
1262     if (a == NULL) {
1263         return;
1264     }
1265 
1266     /* If this attribute will be written to the CIB ... */
1267     if (!a->is_private) {
1268 
1269         /* Defer the write if now's not a good time */
1270         CRM_CHECK(the_cib != NULL, return);
1271         if (a->update && (a->update < last_cib_op_done)) {
1272             crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
1273             a->update = 0; // Don't log this message again
1274 
1275         } else if (a->update) {
1276             crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
1277             return;
1278 
1279         } else if (mainloop_timer_running(a->timer)) {
1280             if (ignore_delay) {
1281                 /* 'refresh' forces a write of the current value of all attributes
1282                  * Cancel any existing timers, we're writing it NOW
1283                  */
1284                 mainloop_timer_stop(a->timer);
1285                 crm_debug("Write out of '%s': timer is running but ignore delay", a->id);
1286             } else {
1287                 crm_info("Write out of '%s' delayed: timer is running", a->id);
1288                 return;
1289             }
1290         }
1291 
1292         /* Initialize the status update XML */
1293         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
1294     }
1295 
1296     /* Attribute will be written shortly, so clear changed flag */
1297     a->changed = FALSE;
1298 
1299     /* We will check all peers' uuids shortly, so initialize this to false */
1300     a->unknown_peer_uuids = FALSE;
1301 
1302     /* Attribute will be written shortly, so clear forced write flag */
1303     a->force_write = FALSE;    
1304 
1305     /* Make the table for the attribute trap */
1306     alert_attribute_value = pcmk__strikey_table(NULL, free_attribute_value);
1307 
1308     /* Iterate over each peer value of this attribute */
1309     g_hash_table_iter_init(&iter, a->values);
1310     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
1311         crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
1312 
1313         /* If the value's peer info does not correspond to a peer, ignore it */
1314         if (peer == NULL) {
1315             crm_notice("Cannot update %s[%s]=%s because peer not known",
1316                        a->id, v->nodename, v->current);
1317             continue;
1318         }
1319 
1320         /* If we're just learning the peer's node id, remember it */
1321         if (peer->id && (v->nodeid == 0)) {
1322             crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
1323             v->nodeid = peer->id;
1324         }
1325 
1326         /* If this is a private attribute, no update needs to be sent */
1327         if (a->is_private) {
1328             private_updates++;
1329             continue;
1330         }
1331 
1332         /* If the peer is found, but its uuid is unknown, defer write */
1333         if (peer->uuid == NULL) {
1334             a->unknown_peer_uuids = TRUE;
1335             crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
1336                        "(will retry if learned)",
1337                        a->id, v->nodename, v->current);
1338             continue;
1339         }
1340 
1341         /* Add this value to status update XML */
1342         crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
1343                   a->id, v->nodename, v->current,
1344                   peer->uname, peer->uuid, peer->id, v->nodeid);
1345         build_update_element(xml_top, a, peer->uuid, v->current);
1346         cib_updates++;
1347 
1348         /* Preservation of the attribute to transmit alert */
1349         set_alert_attribute_value(alert_attribute_value, v);
1350 
1351         free(v->requested);
1352         v->requested = NULL;
1353         if (v->current) {
1354             v->requested = strdup(v->current);
1355         } else {
1356             /* Older attrd versions don't know about the cib_mixed_update
1357              * flag so make sure it goes to the local cib which does
1358              */
1359             cib__set_call_options(flags, crm_system_name,
1360                                   cib_mixed_update|cib_scope_local);
1361         }
1362     }
1363 
1364     if (private_updates) {
1365         crm_info("Processed %d private change%s for %s, id=%s, set=%s",
1366                  private_updates, pcmk__plural_s(private_updates),
1367                  a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1368     }
1369     if (cib_updates) {
1370         crm_log_xml_trace(xml_top, __func__);
1371 
1372         a->update = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL,
1373                                     flags, a->user);
1374 
1375         crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
1376                  a->update, cib_updates, pcmk__plural_s(cib_updates),
1377                  a->id, (a->uuid? a->uuid : "n/a"), (a->set? a->set : "n/a"));
1378 
1379         the_cib->cmds->register_callback_full(the_cib, a->update,
1380                                               CIB_OP_TIMEOUT_S, FALSE,
1381                                               strdup(a->id),
1382                                               "attrd_cib_callback",
1383                                               attrd_cib_callback, free);
1384         /* Transmit alert of the attribute */
1385         send_alert_attributes_value(a, alert_attribute_value);
1386     }
1387 
1388     g_hash_table_destroy(alert_attribute_value);
1389     free_xml(xml_top);
1390 }

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