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

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