root/daemons/attrd/attrd_cib.c

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

DEFINITIONS

This source file includes following definitions.
  1. attrd_cib_destroy_cb
  2. attrd_cib_updated_cb
  3. attrd_cib_connect
  4. attrd_cib_disconnect
  5. attrd_erase_cb
  6. attrd_cib_erase_transient_attrs
  7. attrd_cib_init
  8. attribute_timer_cb
  9. attrd_cib_callback
  10. add_set_attr_update
  11. add_unset_attr_update
  12. add_attr_update
  13. send_alert_attributes_value
  14. set_alert_attribute_value
  15. attrd_add_timer
  16. write_attribute
  17. attrd_write_attributes
  18. attrd_write_or_elect_attribute

   1 /*
   2  * Copyright 2013-2024 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <errno.h>
  13 #include <inttypes.h>   // PRIu32
  14 #include <stdbool.h>
  15 #include <stdlib.h>
  16 #include <glib.h>
  17 
  18 #include <crm/cib/internal.h>       // cib__*
  19 #include <crm/common/logging.h>
  20 #include <crm/common/results.h>
  21 #include <crm/common/strings_internal.h>
  22 #include <crm/common/xml.h>
  23 #include <crm/cluster/internal.h>   // pcmk__get_node()
  24 
  25 #include "pacemaker-attrd.h"
  26 
  27 static int last_cib_op_done = 0;
  28 
  29 static void write_attribute(attribute_t *a, bool ignore_delay);
  30 
  31 static void
  32 attrd_cib_destroy_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     cib_t *cib = user_data;
  35 
  36     cib->cmds->signoff(cib);
  37 
  38     if (attrd_shutting_down(false)) {
  39         crm_info("Disconnected from the CIB manager");
  40 
  41     } else {
  42         // @TODO This should trigger a reconnect, not a shutdown
  43         crm_crit("Lost connection to the CIB manager, shutting down");
  44         attrd_exit_status = CRM_EX_DISCONNECT;
  45         attrd_shutdown(0);
  46     }
  47 }
  48 
  49 static void
  50 attrd_cib_updated_cb(const char *event, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     const xmlNode *patchset = NULL;
  53     const char *client_name = NULL;
  54     bool status_changed = false;
  55 
  56     if (attrd_shutting_down(true)) {
  57         crm_debug("Ignoring CIB change during shutdown");
  58         return;
  59     }
  60 
  61     if (cib__get_notify_patchset(msg, &patchset) != pcmk_rc_ok) {
  62         return;
  63     }
  64 
  65     if (cib__element_in_patchset(patchset, PCMK_XE_ALERTS)) {
  66         mainloop_set_trigger(attrd_config_read);
  67     }
  68 
  69     status_changed = cib__element_in_patchset(patchset, PCMK_XE_STATUS);
  70 
  71     client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTNAME);
  72     if (!cib__client_triggers_refresh(client_name)) {
  73         /* This change came from a source that ensured the CIB is consistent
  74          * with our attributes table, so we don't need to write anything out.
  75          */
  76         return;
  77     }
  78 
  79     if (!attrd_election_won()) {
  80         // Don't write attributes if we're not the writer
  81         return;
  82     }
  83 
  84     if (status_changed || cib__element_in_patchset(patchset, PCMK_XE_NODES)) {
  85         /* An unsafe client modified the PCMK_XE_NODES or PCMK_XE_STATUS
  86          * section. Write transient attributes to ensure they're up-to-date in
  87          * the CIB.
  88          */
  89         if (client_name == NULL) {
  90             client_name = crm_element_value(msg, PCMK__XA_CIB_CLIENTID);
  91         }
  92         crm_notice("Updating all attributes after %s event triggered by %s",
  93                    event, pcmk__s(client_name, "(unidentified client)"));
  94 
  95         attrd_write_attributes(attrd_write_all);
  96     }
  97 }
  98 
  99 int
 100 attrd_cib_connect(int max_retry)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102     static int attempts = 0;
 103 
 104     int rc = -ENOTCONN;
 105 
 106     the_cib = cib_new();
 107     if (the_cib == NULL) {
 108         return -ENOTCONN;
 109     }
 110 
 111     do {
 112         if (attempts > 0) {
 113             sleep(attempts);
 114         }
 115         attempts++;
 116         crm_debug("Connection attempt %d to the CIB manager", attempts);
 117         rc = the_cib->cmds->signon(the_cib, PCMK__VALUE_ATTRD, cib_command);
 118 
 119     } while ((rc != pcmk_ok) && (attempts < max_retry));
 120 
 121     if (rc != pcmk_ok) {
 122         crm_err("Connection to the CIB manager failed: %s " CRM_XS " rc=%d",
 123                 pcmk_strerror(rc), rc);
 124         goto cleanup;
 125     }
 126 
 127     crm_debug("Connected to the CIB manager after %d attempts", attempts);
 128 
 129     rc = the_cib->cmds->set_connection_dnotify(the_cib, attrd_cib_destroy_cb);
 130     if (rc != pcmk_ok) {
 131         crm_err("Could not set disconnection callback");
 132         goto cleanup;
 133     }
 134 
 135     rc = the_cib->cmds->add_notify_callback(the_cib,
 136                                             PCMK__VALUE_CIB_DIFF_NOTIFY,
 137                                             attrd_cib_updated_cb);
 138     if (rc != pcmk_ok) {
 139         crm_err("Could not set CIB notification callback");
 140         goto cleanup;
 141     }
 142 
 143     return pcmk_ok;
 144 
 145 cleanup:
 146     cib__clean_up_connection(&the_cib);
 147     return -ENOTCONN;
 148 }
 149 
 150 void
 151 attrd_cib_disconnect(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 152 {
 153     CRM_CHECK(the_cib != NULL, return);
 154     the_cib->cmds->del_notify_callback(the_cib, PCMK__VALUE_CIB_DIFF_NOTIFY,
 155                                        attrd_cib_updated_cb);
 156     cib__clean_up_connection(&the_cib);
 157 }
 158 
 159 static void
 160 attrd_erase_cb(xmlNode *msg, int call_id, int rc, xmlNode *output,
     /* [previous][next][first][last][top][bottom][index][help] */
 161                void *user_data)
 162 {
 163     const char *node = pcmk__s((const char *) user_data, "a node");
 164 
 165     if (rc == pcmk_ok) {
 166         crm_info("Cleared transient node attributes for %s from CIB", node);
 167     } else {
 168         crm_err("Unable to clear transient node attributes for %s from CIB: %s",
 169                 node, pcmk_strerror(rc));
 170     }
 171 }
 172 
 173 #define XPATH_TRANSIENT "//" PCMK__XE_NODE_STATE    \
 174                         "[@" PCMK_XA_UNAME "='%s']" \
 175                         "/" PCMK__XE_TRANSIENT_ATTRIBUTES
 176 
 177 /*!
 178  * \internal
 179  * \brief Wipe all transient node attributes for a node from the CIB
 180  *
 181  * \param[in] node  Node to clear attributes for
 182  */
 183 void
 184 attrd_cib_erase_transient_attrs(const char *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     int call_id = 0;
 187     char *xpath = NULL;
 188 
 189     CRM_CHECK(node != NULL, return);
 190 
 191     xpath = crm_strdup_printf(XPATH_TRANSIENT, node);
 192 
 193     crm_debug("Clearing transient node attributes for %s from CIB using %s",
 194               node, xpath);
 195 
 196     call_id = the_cib->cmds->remove(the_cib, xpath, NULL, cib_xpath);
 197     free(xpath);
 198 
 199     the_cib->cmds->register_callback_full(the_cib, call_id, 120, FALSE,
 200                                           pcmk__str_copy(node),
 201                                           "attrd_erase_cb", attrd_erase_cb,
 202                                           free);
 203 }
 204 
 205 /*!
 206  * \internal
 207  * \brief Prepare the CIB after cluster is connected
 208  */
 209 void
 210 attrd_cib_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212     /* We have no attribute values in memory, so wipe the CIB to match. This is
 213      * normally done by the DC's controller when this node leaves the cluster, but
 214      * this handles the case where the node restarted so quickly that the
 215      * cluster layer didn't notice.
 216      *
 217      * \todo If pacemaker-attrd respawns after crashing (see PCMK_ENV_RESPAWNED),
 218      *       ideally we'd skip this and sync our attributes from the writer.
 219      *       However, currently we reject any values for us that the writer has, in
 220      *       attrd_peer_update().
 221      */
 222     attrd_cib_erase_transient_attrs(attrd_cluster->uname);
 223 
 224     // Set a trigger for reading the CIB (for the alerts section)
 225     attrd_config_read = mainloop_add_trigger(G_PRIORITY_HIGH, attrd_read_options, NULL);
 226 
 227     // Always read the CIB at start-up
 228     mainloop_set_trigger(attrd_config_read);
 229 }
 230 
 231 static gboolean
 232 attribute_timer_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 233 {
 234     attribute_t *a = data;
 235     crm_trace("Dampen interval expired for %s", a->id);
 236     attrd_write_or_elect_attribute(a);
 237     return FALSE;
 238 }
 239 
 240 static void
 241 attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243     int level = LOG_ERR;
 244     GHashTableIter iter;
 245     const char *peer = NULL;
 246     attribute_value_t *v = NULL;
 247 
 248     char *name = user_data;
 249     attribute_t *a = g_hash_table_lookup(attributes, name);
 250 
 251     if(a == NULL) {
 252         crm_info("Attribute %s no longer exists", name);
 253         return;
 254     }
 255 
 256     a->update = 0;
 257     if (rc == pcmk_ok && call_id < 0) {
 258         rc = call_id;
 259     }
 260 
 261     switch (rc) {
 262         case pcmk_ok:
 263             level = LOG_INFO;
 264             last_cib_op_done = call_id;
 265             if (a->timer && !a->timeout_ms) {
 266                 // Remove temporary dampening for failed writes
 267                 mainloop_timer_del(a->timer);
 268                 a->timer = NULL;
 269             }
 270             break;
 271 
 272         case -pcmk_err_diff_failed:    /* When an attr changes while the CIB is syncing */
 273         case -ETIME:           /* When an attr changes while there is a DC election */
 274         case -ENXIO:           /* When an attr changes while the CIB is syncing a
 275                                 *   newer config from a node that just came up
 276                                 */
 277             level = LOG_WARNING;
 278             break;
 279     }
 280 
 281     do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
 282                call_id, a->id, pcmk_strerror(rc), rc);
 283 
 284     g_hash_table_iter_init(&iter, a->values);
 285     while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
 286         if (rc == pcmk_ok) {
 287             crm_info("* Wrote %s[%s]=%s",
 288                      a->id, peer, pcmk__s(v->requested, "(unset)"));
 289             pcmk__str_update(&(v->requested), NULL);
 290         } else {
 291             do_crm_log(level, "* Could not write %s[%s]=%s",
 292                        a->id, peer, pcmk__s(v->requested, "(unset)"));
 293             /* Reattempt write below if we are still the writer */
 294             attrd_set_attr_flags(a, attrd_attr_changed);
 295         }
 296     }
 297 
 298     if (pcmk_is_set(a->flags, attrd_attr_changed) && attrd_election_won()) {
 299         if (rc == pcmk_ok) {
 300             /* We deferred a write of a new update because this update was in
 301              * progress. Write out the new value without additional delay.
 302              */
 303             crm_debug("Pending update for %s can be written now", a->id);
 304             write_attribute(a, false);
 305 
 306         /* We're re-attempting a write because the original failed; delay
 307          * the next attempt so we don't potentially flood the CIB manager
 308          * and logs with a zillion attempts per second.
 309          *
 310          * @TODO We could elect a new writer instead. However, we'd have to
 311          * somehow downgrade our vote, and we'd still need something like this
 312          * if all peers similarly fail to write this attribute (which may
 313          * indicate a corrupted attribute entry rather than a CIB issue).
 314          */
 315         } else if (a->timer) {
 316             // Attribute has a dampening value, so use that as delay
 317             if (!mainloop_timer_running(a->timer)) {
 318                 crm_trace("Delayed re-attempted write for %s by %s",
 319                           name, pcmk__readable_interval(a->timeout_ms));
 320                 mainloop_timer_start(a->timer);
 321             }
 322         } else {
 323             /* Set a temporary dampening of 2 seconds (timer will continue
 324              * to exist until the attribute's dampening gets set or the
 325              * write succeeds).
 326              */
 327             a->timer = attrd_add_timer(a->id, 2000, a);
 328             mainloop_timer_start(a->timer);
 329         }
 330     }
 331 }
 332 
 333 /*!
 334  * \internal
 335  * \brief Add a set-attribute update request to the current CIB transaction
 336  *
 337  * \param[in] attr     Attribute to update
 338  * \param[in] attr_id  ID of attribute to update
 339  * \param[in] node_id  ID of node for which to update attribute value
 340  * \param[in] set_id   ID of attribute set
 341  * \param[in] value    New value for attribute
 342  *
 343  * \return Standard Pacemaker return code
 344  */
 345 static int
 346 add_set_attr_update(const attribute_t *attr, const char *attr_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 347                     const char *node_id, const char *set_id, const char *value)
 348 {
 349     xmlNode *update = pcmk__xe_create(NULL, PCMK__XE_NODE_STATE);
 350     xmlNode *child = update;
 351     int rc = ENOMEM;
 352 
 353     crm_xml_add(child, PCMK_XA_ID, node_id);
 354 
 355     child = pcmk__xe_create(child, PCMK__XE_TRANSIENT_ATTRIBUTES);
 356     crm_xml_add(child, PCMK_XA_ID, node_id);
 357 
 358     child = pcmk__xe_create(child, attr->set_type);
 359     crm_xml_add(child, PCMK_XA_ID, set_id);
 360 
 361     child = pcmk__xe_create(child, PCMK_XE_NVPAIR);
 362     crm_xml_add(child, PCMK_XA_ID, attr_id);
 363     crm_xml_add(child, PCMK_XA_NAME, attr->id);
 364     crm_xml_add(child, PCMK_XA_VALUE, value);
 365 
 366     rc = the_cib->cmds->modify(the_cib, PCMK_XE_STATUS, update,
 367                                cib_can_create|cib_transaction);
 368     rc = pcmk_legacy2rc(rc);
 369 
 370     free_xml(update);
 371     return rc;
 372 }
 373 
 374 /*!
 375  * \internal
 376  * \brief Add an unset-attribute update request to the current CIB transaction
 377  *
 378  * \param[in] attr     Attribute to update
 379  * \param[in] attr_id  ID of attribute to update
 380  * \param[in] node_id  ID of node for which to update attribute value
 381  * \param[in] set_id   ID of attribute set
 382  *
 383  * \return Standard Pacemaker return code
 384  */
 385 static int
 386 add_unset_attr_update(const attribute_t *attr, const char *attr_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 387                       const char *node_id, const char *set_id)
 388 {
 389     char *xpath = crm_strdup_printf("/" PCMK_XE_CIB
 390                                     "/" PCMK_XE_STATUS
 391                                     "/" PCMK__XE_NODE_STATE
 392                                         "[@" PCMK_XA_ID "='%s']"
 393                                     "/" PCMK__XE_TRANSIENT_ATTRIBUTES
 394                                         "[@" PCMK_XA_ID "='%s']"
 395                                     "/%s[@" PCMK_XA_ID "='%s']"
 396                                     "/" PCMK_XE_NVPAIR
 397                                         "[@" PCMK_XA_ID "='%s' "
 398                                          "and @" PCMK_XA_NAME "='%s']",
 399                                     node_id, node_id, attr->set_type, set_id,
 400                                     attr_id, attr->id);
 401 
 402     int rc = the_cib->cmds->remove(the_cib, xpath, NULL,
 403                                    cib_xpath|cib_transaction);
 404 
 405     free(xpath);
 406     return pcmk_legacy2rc(rc);
 407 }
 408 
 409 /*!
 410  * \internal
 411  * \brief Add an attribute update request to the current CIB transaction
 412  *
 413  * \param[in] attr      Attribute to update
 414  * \param[in] value     New value for attribute
 415  * \param[in] node_id   ID of node for which to update attribute value
 416  *
 417  * \return Standard Pacemaker return code
 418  */
 419 static int
 420 add_attr_update(const attribute_t *attr, const char *value, const char *node_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     char *set_id = attrd_set_id(attr, node_id);
 423     char *nvpair_id = attrd_nvpair_id(attr, node_id);
 424     int rc = pcmk_rc_ok;
 425 
 426     if (value == NULL) {
 427         rc = add_unset_attr_update(attr, nvpair_id, node_id, set_id);
 428     } else {
 429         rc = add_set_attr_update(attr, nvpair_id, node_id, set_id, value);
 430     }
 431     free(set_id);
 432     free(nvpair_id);
 433     return rc;
 434 }
 435 
 436 static void
 437 send_alert_attributes_value(attribute_t *a, GHashTable *t)
     /* [previous][next][first][last][top][bottom][index][help] */
 438 {
 439     int rc = 0;
 440     attribute_value_t *at = NULL;
 441     GHashTableIter vIter;
 442 
 443     g_hash_table_iter_init(&vIter, t);
 444 
 445     while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
 446         rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
 447                                         a->id, at->current);
 448         crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
 449                   a->id, at->nodename, at->current, at->nodeid, rc);
 450     }
 451 }
 452 
 453 static void
 454 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
 455 {
 456     attribute_value_t *a_v = pcmk__assert_alloc(1, sizeof(attribute_value_t));
 457 
 458     a_v->nodeid = v->nodeid;
 459     a_v->nodename = pcmk__str_copy(v->nodename);
 460     a_v->current = pcmk__str_copy(v->current);
 461 
 462     g_hash_table_replace(t, a_v->nodename, a_v);
 463 }
 464 
 465 mainloop_timer_t *
 466 attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 467 {
 468    return mainloop_timer_add(id, timeout_ms, FALSE, attribute_timer_cb, attr);
 469 }
 470 
 471 /*!
 472  * \internal
 473  * \brief Write an attribute's values to the CIB if appropriate
 474  *
 475  * \param[in,out] a             Attribute to write
 476  * \param[in]     ignore_delay  If true, write attribute now regardless of any
 477  *                              configured delay
 478  */
 479 static void
 480 write_attribute(attribute_t *a, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
 481 {
 482     int private_updates = 0, cib_updates = 0;
 483     attribute_value_t *v = NULL;
 484     GHashTableIter iter;
 485     GHashTable *alert_attribute_value = NULL;
 486     int rc = pcmk_ok;
 487 
 488     if (a == NULL) {
 489         return;
 490     }
 491 
 492     /* If this attribute will be written to the CIB ... */
 493     if (!stand_alone && !pcmk_is_set(a->flags, attrd_attr_is_private)) {
 494         /* Defer the write if now's not a good time */
 495         if (a->update && (a->update < last_cib_op_done)) {
 496             crm_info("Write out of '%s' continuing: update %d considered lost",
 497                      a->id, a->update);
 498             a->update = 0; // Don't log this message again
 499 
 500         } else if (a->update) {
 501             crm_info("Write out of '%s' delayed: update %d in progress",
 502                      a->id, a->update);
 503             goto done;
 504 
 505         } else if (mainloop_timer_running(a->timer)) {
 506             if (ignore_delay) {
 507                 mainloop_timer_stop(a->timer);
 508                 crm_debug("Overriding '%s' write delay", a->id);
 509             } else {
 510                 crm_info("Delaying write of '%s'", a->id);
 511                 goto done;
 512             }
 513         }
 514 
 515         // Initiate a transaction for all the peer value updates
 516         CRM_CHECK(the_cib != NULL, goto done);
 517         the_cib->cmds->set_user(the_cib, a->user);
 518         rc = the_cib->cmds->init_transaction(the_cib);
 519         if (rc != pcmk_ok) {
 520             crm_err("Failed to write %s (set %s): Could not initiate "
 521                     "CIB transaction",
 522                     a->id, pcmk__s(a->set_id, "unspecified"));
 523             goto done;
 524         }
 525     }
 526 
 527     /* Attribute will be written shortly, so clear changed flag and force
 528      * write flag, and initialize UUID missing flag to false.
 529      */
 530     attrd_clear_attr_flags(a, attrd_attr_changed|attrd_attr_uuid_missing|attrd_attr_force_write);
 531 
 532     /* Make the table for the attribute trap */
 533     alert_attribute_value = pcmk__strikey_table(NULL,
 534                                                 attrd_free_attribute_value);
 535 
 536     /* Iterate over each peer value of this attribute */
 537     g_hash_table_iter_init(&iter, a->values);
 538     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
 539         const char *uuid = NULL;
 540 
 541         if (pcmk_is_set(v->flags, attrd_value_remote)) {
 542             /* If this is a Pacemaker Remote node, the node's UUID is the same
 543              * as its name, which we already have.
 544              */
 545             uuid = v->nodename;
 546 
 547         } else {
 548             // This will create a cluster node cache entry if none exists
 549             crm_node_t *peer = pcmk__get_node(v->nodeid, v->nodename, NULL,
 550                                               pcmk__node_search_any);
 551 
 552             uuid = peer->uuid;
 553 
 554             // Remember peer's node ID if we're just now learning it
 555             if ((peer->id != 0) && (v->nodeid == 0)) {
 556                 crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
 557                 v->nodeid = peer->id;
 558             }
 559         }
 560 
 561         /* If this is a private attribute, no update needs to be sent */
 562         if (stand_alone || pcmk_is_set(a->flags, attrd_attr_is_private)) {
 563             private_updates++;
 564             continue;
 565         }
 566 
 567         // Defer write if this is a cluster node that's never been seen
 568         if (uuid == NULL) {
 569             attrd_set_attr_flags(a, attrd_attr_uuid_missing);
 570             crm_notice("Cannot update %s[%s]='%s' now because node's UUID is "
 571                        "unknown (will retry if learned)",
 572                        a->id, v->nodename, v->current);
 573             continue;
 574         }
 575 
 576         // Update this value as part of the CIB transaction we're building
 577         rc = add_attr_update(a, v->current, uuid);
 578         if (rc != pcmk_rc_ok) {
 579             crm_err("Failed to update %s[%s]='%s': %s "
 580                     CRM_XS " node uuid=%s id=%" PRIu32,
 581                     a->id, v->nodename, v->current, pcmk_rc_str(rc),
 582                     uuid, v->nodeid);
 583             continue;
 584         }
 585 
 586         crm_debug("Writing %s[%s]=%s (node-state-id=%s node-id=%" PRIu32 ")",
 587                   a->id, v->nodename, pcmk__s(v->current, "(unset)"),
 588                   uuid, v->nodeid);
 589         cib_updates++;
 590 
 591         /* Preservation of the attribute to transmit alert */
 592         set_alert_attribute_value(alert_attribute_value, v);
 593 
 594         // Save this value so we can log it when write completes
 595         pcmk__str_update(&(v->requested), v->current);
 596     }
 597 
 598     if (private_updates) {
 599         crm_info("Processed %d private change%s for %s (set %s)",
 600                  private_updates, pcmk__plural_s(private_updates),
 601                  a->id, pcmk__s(a->set_id, "unspecified"));
 602     }
 603     if (cib_updates > 0) {
 604         char *id = pcmk__str_copy(a->id);
 605 
 606         // Commit transaction
 607         a->update = the_cib->cmds->end_transaction(the_cib, true, cib_none);
 608 
 609         crm_info("Sent CIB request %d with %d change%s for %s (set %s)",
 610                  a->update, cib_updates, pcmk__plural_s(cib_updates),
 611                  a->id, pcmk__s(a->set_id, "unspecified"));
 612 
 613         if (the_cib->cmds->register_callback_full(the_cib, a->update,
 614                                                   CIB_OP_TIMEOUT_S, FALSE, id,
 615                                                   "attrd_cib_callback",
 616                                                   attrd_cib_callback, free)) {
 617             // Transmit alert of the attribute
 618             send_alert_attributes_value(a, alert_attribute_value);
 619         }
 620     }
 621 
 622 done:
 623     // Discard transaction (if any)
 624     if (the_cib != NULL) {
 625         the_cib->cmds->end_transaction(the_cib, false, cib_none);
 626         the_cib->cmds->set_user(the_cib, NULL);
 627     }
 628 
 629     if (alert_attribute_value != NULL) {
 630         g_hash_table_destroy(alert_attribute_value);
 631     }
 632 }
 633 
 634 /*!
 635  * \internal
 636  * \brief Write out attributes
 637  *
 638  * \param[in] options  Group of enum attrd_write_options
 639  */
 640 void
 641 attrd_write_attributes(uint32_t options)
     /* [previous][next][first][last][top][bottom][index][help] */
 642 {
 643     GHashTableIter iter;
 644     attribute_t *a = NULL;
 645 
 646     crm_debug("Writing out %s attributes",
 647               pcmk_is_set(options, attrd_write_all)? "all" : "changed");
 648     g_hash_table_iter_init(&iter, attributes);
 649     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
 650         if (!pcmk_is_set(options, attrd_write_all) &&
 651             pcmk_is_set(a->flags, attrd_attr_uuid_missing)) {
 652             // Try writing this attribute again, in case peer ID was learned
 653             attrd_set_attr_flags(a, attrd_attr_changed);
 654         } else if (pcmk_is_set(a->flags, attrd_attr_force_write)) {
 655             /* If the force_write flag is set, write the attribute. */
 656             attrd_set_attr_flags(a, attrd_attr_changed);
 657         }
 658 
 659         if (pcmk_is_set(options, attrd_write_all) ||
 660             pcmk_is_set(a->flags, attrd_attr_changed)) {
 661             bool ignore_delay = pcmk_is_set(options, attrd_write_no_delay);
 662 
 663             if (pcmk_is_set(a->flags, attrd_attr_force_write)) {
 664                 // Always ignore delay when forced write flag is set
 665                 ignore_delay = true;
 666             }
 667             write_attribute(a, ignore_delay);
 668         } else {
 669             crm_trace("Skipping unchanged attribute %s", a->id);
 670         }
 671     }
 672 }
 673 
 674 void
 675 attrd_write_or_elect_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 676 {
 677     if (attrd_election_won()) {
 678         write_attribute(a, false);
 679     } else {
 680         attrd_start_election_if_needed();
 681     }
 682 }

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