root/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. create_attribute
  7. attrd_client_peer_remove
  8. attrd_client_update
  9. attrd_client_clear_failure
  10. attrd_client_refresh
  11. build_query_reply
  12. attrd_client_query
  13. attrd_peer_clear_failure
  14. attrd_peer_message
  15. attrd_peer_sync
  16. attrd_peer_remove
  17. attrd_lookup_or_create_value
  18. attrd_peer_update
  19. write_or_elect_attribute
  20. attrd_election_cb
  21. attrd_peer_change_cb
  22. attrd_cib_callback
  23. write_attributes
  24. build_update_element
  25. set_alert_attribute_value
  26. send_alert_attributes_value
  27. write_attribute

   1 /*
   2  * Copyright (C) 2013 Andrew Beekhof <andrew@beekhof.net>
   3  *
   4  * This program is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2 of the License, or (at your option) any later version.
   8  *
   9  * This software is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * General Public License for more details.
  13  *
  14  * You should have received a copy of the GNU General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  */
  18 #include <crm_internal.h>
  19 
  20 #include <sys/types.h>
  21 #include <regex.h>
  22 #include <glib.h>
  23 
  24 #include <crm/msg_xml.h>
  25 #include <crm/cluster.h>
  26 #include <crm/cib.h>
  27 #include <crm/cluster/internal.h>
  28 #include <crm/cluster/election.h>
  29 #include <crm/cib/internal.h>
  30 
  31 #include <internal.h>
  32 
  33 /*
  34  * Legacy attrd (all pre-1.1.11 Pacemaker versions, plus all versions when using
  35  * heartbeat, CMAN, or corosync-plugin stacks) is unversioned.
  36  *
  37  * With atomic attrd, each attrd will send ATTRD_PROTOCOL_VERSION with every
  38  * peer request and reply. Currently, there is no way to know the minimum
  39  * version supported by all peers, which limits its usefulness.
  40  *
  41  * Protocol  Pacemaker  Significant changes
  42  * --------  ---------  -------------------
  43  *     1       1.1.11   ATTRD_OP_UPDATE (F_ATTRD_ATTRIBUTE only),
  44  *                      ATTRD_OP_PEER_REMOVE, ATTRD_OP_REFRESH, ATTRD_OP_FLUSH,
  45  *                      ATTRD_OP_SYNC, ATTRD_OP_SYNC_RESPONSE
  46  *     1       1.1.13   ATTRD_OP_UPDATE (with F_ATTR_REGEX), ATTRD_OP_QUERY
  47  *     1       1.1.15   ATTRD_OP_UPDATE_BOTH, ATTRD_OP_UPDATE_DELAY
  48  *     2       1.1.17   ATTRD_OP_CLEAR_FAILCOUNT
  49  */
  50 #define ATTRD_PROTOCOL_VERSION "2"
  51 
  52 int last_cib_op_done = 0;
  53 char *peer_writer = NULL;
  54 GHashTable *attributes = NULL;
  55 
  56 void write_attribute(attribute_t *a);
  57 void write_or_elect_attribute(attribute_t *a);
  58 void attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter);
  59 void attrd_peer_sync(crm_node_t *peer, xmlNode *xml);
  60 void attrd_peer_remove(const char *host, gboolean uncache, const char *source);
  61 
  62 static gboolean
  63 send_attrd_message(crm_node_t * node, xmlNode * data)
     /* [previous][next][first][last][top][bottom][index][help] */
  64 {
  65     crm_xml_add(data, F_TYPE, T_ATTRD);
  66     crm_xml_add(data, F_ATTRD_IGNORE_LOCALLY, "atomic-version"); /* Tell older versions to ignore our messages */
  67     crm_xml_add(data, F_ATTRD_VERSION, ATTRD_PROTOCOL_VERSION);
  68     crm_xml_add_int(data, F_ATTRD_WRITER, election_state(writer));
  69 
  70     return send_cluster_message(node, crm_msg_attrd, data, TRUE);
  71 }
  72 
  73 static gboolean
  74 attribute_timer_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  75 {
  76     attribute_t *a = data;
  77     crm_trace("Dampen interval expired for %s in state %d", a->id, election_state(writer));
  78     write_or_elect_attribute(a);
  79     return FALSE;
  80 }
  81 
  82 static void
  83 free_attribute_value(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  84 {
  85     attribute_value_t *v = data;
  86 
  87     free(v->nodename);
  88     free(v->current);
  89     free(v->requested);
  90     free(v);
  91 }
  92 
  93 void
  94 free_attribute(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  95 {
  96     attribute_t *a = data;
  97     if(a) {
  98         free(a->id);
  99         free(a->set);
 100         free(a->uuid);
 101         free(a->user);
 102 
 103         mainloop_timer_del(a->timer);
 104         g_hash_table_destroy(a->values);
 105 
 106         free(a);
 107     }
 108 }
 109 
 110 static xmlNode *
 111 build_attribute_xml(
     /* [previous][next][first][last][top][bottom][index][help] */
 112     xmlNode *parent, const char *name, const char *set, const char *uuid, unsigned int timeout_ms, const char *user,
 113     gboolean is_private, const char *peer, uint32_t peerid, const char *value)
 114 {
 115     xmlNode *xml = create_xml_node(parent, __FUNCTION__);
 116 
 117     crm_xml_add(xml, F_ATTRD_ATTRIBUTE, name);
 118     crm_xml_add(xml, F_ATTRD_SET, set);
 119     crm_xml_add(xml, F_ATTRD_KEY, uuid);
 120     crm_xml_add(xml, F_ATTRD_USER, user);
 121     crm_xml_add(xml, F_ATTRD_HOST, peer);
 122     crm_xml_add_int(xml, F_ATTRD_HOST_ID, peerid);
 123     crm_xml_add(xml, F_ATTRD_VALUE, value);
 124     crm_xml_add_int(xml, F_ATTRD_DAMPEN, timeout_ms/1000);
 125     crm_xml_add_int(xml, F_ATTRD_IS_PRIVATE, is_private);
 126 
 127     return xml;
 128 }
 129 
 130 static attribute_t *
 131 create_attribute(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 132 {
 133     int dampen = 0;
 134     const char *value = crm_element_value(xml, F_ATTRD_DAMPEN);
 135     attribute_t *a = calloc(1, sizeof(attribute_t));
 136 
 137     a->id      = crm_element_value_copy(xml, F_ATTRD_ATTRIBUTE);
 138     a->set     = crm_element_value_copy(xml, F_ATTRD_SET);
 139     a->uuid    = crm_element_value_copy(xml, F_ATTRD_KEY);
 140     a->values = g_hash_table_new_full(crm_strcase_hash, crm_strcase_equal, NULL, free_attribute_value);
 141 
 142     crm_element_value_int(xml, F_ATTRD_IS_PRIVATE, &a->is_private);
 143 
 144 #if ENABLE_ACL
 145     crm_trace("Performing all %s operations as user '%s'", a->id, a->user);
 146     a->user = crm_element_value_copy(xml, F_ATTRD_USER);
 147 #endif
 148 
 149     if(value) {
 150         dampen = crm_get_msec(value);
 151         crm_trace("Created attribute %s with delay %dms (%s)", a->id, dampen, value);
 152     } else {
 153         crm_trace("Created attribute %s with no delay", a->id);
 154     }
 155 
 156     if(dampen > 0) {
 157         a->timeout_ms = dampen;
 158         a->timer = mainloop_timer_add(a->id, a->timeout_ms, FALSE, attribute_timer_cb, a);
 159     } else if (dampen < 0) {
 160         crm_warn("Ignoring invalid delay %s for attribute %s", value, a->id);
 161     }
 162 
 163     g_hash_table_replace(attributes, a->id, a);
 164     return a;
 165 }
 166 
 167 /*!
 168  * \internal
 169  * \brief Respond to a client peer-remove request (i.e. propagate to all peers)
 170  *
 171  * \param[in] client_name Name of client that made request (for log messages)
 172  * \param[in] xml         Root of request XML
 173  *
 174  * \return void
 175  */
 176 void
 177 attrd_client_peer_remove(const char *client_name, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179     const char *host = crm_element_value(xml, F_ATTRD_HOST);
 180 
 181     if (host) {
 182         crm_info("Client %s is requesting all values for %s be removed",
 183                  client_name, host);
 184         send_attrd_message(NULL, xml); /* ends up at attrd_peer_message() */
 185     } else {
 186         crm_info("Ignoring request by client %s to remove all peer values without specifying peer",
 187                  client_name);
 188     }
 189 }
 190 
 191 /*!
 192  * \internal
 193  * \brief Respond to a client update request
 194  *
 195  * \param[in] xml         Root of request XML
 196  *
 197  * \return void
 198  */
 199 void
 200 attrd_client_update(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202     attribute_t *a = NULL;
 203     char *host = crm_element_value_copy(xml, F_ATTRD_HOST);
 204     const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
 205     const char *value = crm_element_value(xml, F_ATTRD_VALUE);
 206     const char *regex = crm_element_value(xml, F_ATTRD_REGEX);
 207 
 208     /* If a regex was specified, broadcast a message for each match */
 209     if ((attr == NULL) && regex) {
 210         GHashTableIter aIter;
 211         regex_t *r_patt = calloc(1, sizeof(regex_t));
 212 
 213         crm_debug("Setting %s to %s", regex, value);
 214         if (regcomp(r_patt, regex, REG_EXTENDED|REG_NOSUB)) {
 215             crm_err("Bad regex '%s' for update", regex);
 216 
 217         } else {
 218             g_hash_table_iter_init(&aIter, attributes);
 219             while (g_hash_table_iter_next(&aIter, (gpointer *) & attr, NULL)) {
 220                 int status = regexec(r_patt, attr, 0, NULL, 0);
 221 
 222                 if (status == 0) {
 223                     crm_trace("Matched %s with %s", attr, regex);
 224                     crm_xml_add(xml, F_ATTRD_ATTRIBUTE, attr);
 225                     send_attrd_message(NULL, xml);
 226                 }
 227             }
 228         }
 229 
 230         free(host);
 231         regfree(r_patt);
 232         free(r_patt);
 233         return;
 234 
 235     } else if (attr == NULL) {
 236         crm_err("Update request did not specify attribute or regular expression");
 237         free(host);
 238         return;
 239     }
 240 
 241     if (host == NULL) {
 242         crm_trace("Inferring host");
 243         host = strdup(attrd_cluster->uname);
 244         crm_xml_add(xml, F_ATTRD_HOST, host);
 245         crm_xml_add_int(xml, F_ATTRD_HOST_ID, attrd_cluster->nodeid);
 246     }
 247 
 248     a = g_hash_table_lookup(attributes, attr);
 249 
 250     /* If value was specified using ++ or += notation, expand to real value */
 251     if (value) {
 252         if (attrd_value_needs_expansion(value)) {
 253             int int_value;
 254             attribute_value_t *v = NULL;
 255 
 256             if (a) {
 257                 v = g_hash_table_lookup(a->values, host);
 258             }
 259             int_value = attrd_expand_value(value, (v? v->current : NULL));
 260 
 261             crm_info("Expanded %s=%s to %d", attr, value, int_value);
 262             crm_xml_add_int(xml, F_ATTRD_VALUE, int_value);
 263 
 264             /* Replacing the value frees the previous memory, so re-query it */
 265             value = crm_element_value(xml, F_ATTRD_VALUE);
 266         }
 267     }
 268 
 269     if ((peer_writer == NULL) && (election_state(writer) != election_in_progress)) {
 270         crm_info("Starting an election to determine the writer");
 271         election_vote(writer);
 272     }
 273 
 274     crm_debug("Broadcasting %s[%s] = %s%s", attr, host, value,
 275               ((election_state(writer) == election_won)? " (writer)" : ""));
 276 
 277     free(host);
 278 
 279     send_attrd_message(NULL, xml); /* ends up at attrd_peer_message() */
 280 }
 281 
 282 /*!
 283  * \internal
 284  * \brief Respond to client clear-failure request
 285  *
 286  * \param[in] xml         Request XML
 287  */
 288 void
 289 attrd_client_clear_failure(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 290 {
 291 #if 0
 292     /* @TODO This would be most efficient, but there is currently no way to
 293      * verify that all peers support the op. If that ever changes, we could
 294      * enable this code.
 295      */
 296     if (all_peers_support_clear_failure) {
 297         /* Propagate to all peers (including ourselves).
 298          * This ends up at attrd_peer_message().
 299          */
 300         send_attrd_message(NULL, xml);
 301         return;
 302     }
 303 #endif
 304 
 305     const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE);
 306     const char *op = crm_element_value(xml, F_ATTRD_OPERATION);
 307     const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL);
 308 
 309     /* Map this to an update */
 310     crm_xml_add(xml, F_ATTRD_TASK, ATTRD_OP_UPDATE);
 311 
 312     /* Add regular expression matching desired attributes */
 313 
 314     if (rsc) {
 315         char *pattern;
 316 
 317         if (op == NULL) {
 318             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_ONE, rsc);
 319 
 320         } else {
 321             int interval = crm_get_interval(interval_s);
 322 
 323             pattern = crm_strdup_printf(ATTRD_RE_CLEAR_OP,
 324                                         rsc, op, interval);
 325         }
 326 
 327         crm_xml_add(xml, F_ATTRD_REGEX, pattern);
 328         free(pattern);
 329 
 330     } else {
 331         crm_xml_add(xml, F_ATTRD_REGEX, ATTRD_RE_CLEAR_ALL);
 332     }
 333 
 334     /* Make sure attribute and value are not set, so we delete via regex */
 335     if (crm_element_value(xml, F_ATTRD_ATTRIBUTE)) {
 336         crm_xml_replace(xml, F_ATTRD_ATTRIBUTE, NULL);
 337     }
 338     if (crm_element_value(xml, F_ATTRD_VALUE)) {
 339         crm_xml_replace(xml, F_ATTRD_VALUE, NULL);
 340     }
 341 
 342     attrd_client_update(xml);
 343 }
 344 
 345 /*!
 346  * \internal
 347  * \brief Respond to a client refresh request (i.e. write out all attributes)
 348  *
 349  * \return void
 350  */
 351 void
 352 attrd_client_refresh(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     GHashTableIter iter;
 355     attribute_t *a = NULL;
 356 
 357     /* 'refresh' forces a write of the current value of all attributes
 358      * Cancel any existing timers, we're writing it NOW
 359      */
 360     g_hash_table_iter_init(&iter, attributes);
 361     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
 362         mainloop_timer_stop(a->timer);
 363     }
 364 
 365     crm_info("Updating all attributes");
 366     write_attributes(TRUE);
 367 }
 368 
 369 /*!
 370  * \internal
 371  * \brief Build the XML reply to a client query
 372  *
 373  * param[in] attr Name of requested attribute
 374  * param[in] host Name of requested host (or NULL for all hosts)
 375  *
 376  * \return New XML reply
 377  * \note Caller is responsible for freeing the resulting XML
 378  */
 379 static xmlNode *build_query_reply(const char *attr, const char *host)
     /* [previous][next][first][last][top][bottom][index][help] */
 380 {
 381     xmlNode *reply = create_xml_node(NULL, __FUNCTION__);
 382     attribute_t *a;
 383 
 384     if (reply == NULL) {
 385         return NULL;
 386     }
 387     crm_xml_add(reply, F_TYPE, T_ATTRD);
 388     crm_xml_add(reply, F_ATTRD_VERSION, ATTRD_PROTOCOL_VERSION);
 389 
 390     /* If desired attribute exists, add its value(s) to the reply */
 391     a = g_hash_table_lookup(attributes, attr);
 392     if (a) {
 393         attribute_value_t *v;
 394         xmlNode *host_value;
 395 
 396         crm_xml_add(reply, F_ATTRD_ATTRIBUTE, attr);
 397 
 398         /* Allow caller to use "localhost" to refer to local node */
 399         if (safe_str_eq(host, "localhost")) {
 400             host = attrd_cluster->uname;
 401             crm_trace("Mapped localhost to %s", host);
 402         }
 403 
 404         /* If a specific node was requested, add its value */
 405         if (host) {
 406             v = g_hash_table_lookup(a->values, host);
 407             host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
 408             if (host_value == NULL) {
 409                 free_xml(reply);
 410                 return NULL;
 411             }
 412             crm_xml_add(host_value, F_ATTRD_HOST, host);
 413             crm_xml_add(host_value, F_ATTRD_VALUE, (v? v->current : NULL));
 414 
 415         /* Otherwise, add all nodes' values */
 416         } else {
 417             GHashTableIter iter;
 418 
 419             g_hash_table_iter_init(&iter, a->values);
 420             while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &v)) {
 421                 host_value = create_xml_node(reply, XML_CIB_TAG_NODE);
 422                 if (host_value == NULL) {
 423                     free_xml(reply);
 424                     return NULL;
 425                 }
 426                 crm_xml_add(host_value, F_ATTRD_HOST, v->nodename);
 427                 crm_xml_add(host_value, F_ATTRD_VALUE, v->current);
 428             }
 429         }
 430     }
 431     return reply;
 432 }
 433 
 434 /*!
 435  * \internal
 436  * \brief Respond to a client query
 437  *
 438  * \param[in] client Who queried us
 439  * \param[in] query  Root of query XML
 440  *
 441  * \return void
 442  */
 443 void
 444 attrd_client_query(crm_client_t *client, uint32_t id, uint32_t flags, xmlNode *query)
     /* [previous][next][first][last][top][bottom][index][help] */
 445 {
 446     const char *attr;
 447     const char *origin = crm_element_value(query, F_ORIG);
 448     ssize_t rc;
 449     xmlNode *reply;
 450 
 451     if (origin == NULL) {
 452         origin = "unknown client";
 453     }
 454     crm_debug("Query arrived from %s", origin);
 455 
 456     /* Request must specify attribute name to query */
 457     attr = crm_element_value(query, F_ATTRD_ATTRIBUTE);
 458     if (attr == NULL) {
 459         crm_warn("Ignoring malformed query from %s (no attribute name given)",
 460                  origin);
 461         return;
 462     }
 463 
 464     /* Build the XML reply */
 465     reply = build_query_reply(attr, crm_element_value(query, F_ATTRD_HOST));
 466     if (reply == NULL) {
 467         crm_err("Could not respond to query from %s: could not create XML reply",
 468                  origin);
 469         return;
 470     }
 471     crm_log_xml_trace(reply, "Reply");
 472 
 473     /* Send the reply to the client */
 474     client->request_id = 0;
 475     if ((rc = crm_ipcs_send(client, id, reply, flags)) < 0) {
 476         crm_err("Could not respond to query from %s: %s (%d)",
 477                 origin, pcmk_strerror(-rc), -rc);
 478     }
 479     free_xml(reply);
 480 }
 481 
 482 /*!
 483  * \internal
 484  * \brief Clear failure-related attributes
 485  *
 486  * \param[in] peer  Peer that sent clear request
 487  * \param[in] xml   Request XML
 488  */
 489 static void
 490 attrd_peer_clear_failure(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 491 {
 492     const char *rsc = crm_element_value(xml, F_ATTRD_RESOURCE);
 493     const char *host = crm_element_value(xml, F_ATTRD_HOST);
 494     const char *op = crm_element_value(xml, F_ATTRD_OPERATION);
 495     const char *interval_s = crm_element_value(xml, F_ATTRD_INTERVAL);
 496     int interval = crm_get_interval(interval_s);
 497     char *attr = NULL;
 498     GHashTableIter iter;
 499     regex_t regex;
 500 
 501     if (attrd_failure_regex(&regex, rsc, op, interval) != pcmk_ok) {
 502         crm_info("Ignoring invalid request to clear failures for %s",
 503                  (rsc? rsc : "all resources"));
 504         return;
 505     }
 506 
 507     crm_xml_add(xml, F_ATTRD_TASK, ATTRD_OP_UPDATE);
 508 
 509     /* Make sure value is not set, so we delete */
 510     if (crm_element_value(xml, F_ATTRD_VALUE)) {
 511         crm_xml_replace(xml, F_ATTRD_VALUE, NULL);
 512     }
 513 
 514     g_hash_table_iter_init(&iter, attributes);
 515     while (g_hash_table_iter_next(&iter, (gpointer *) &attr, NULL)) {
 516         if (regexec(&regex, attr, 0, NULL, 0) == 0) {
 517             crm_trace("Matched %s when clearing %s",
 518                       attr, (rsc? rsc : "all resources"));
 519             crm_xml_add(xml, F_ATTRD_ATTRIBUTE, attr);
 520             attrd_peer_update(peer, xml, host, FALSE);
 521         }
 522     }
 523     regfree(&regex);
 524 }
 525 
 526 void
 527 attrd_peer_message(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 528 {
 529     int peer_state = 0;
 530     const char *v = crm_element_value(xml, F_ATTRD_VERSION);
 531     const char *op = crm_element_value(xml, F_ATTRD_TASK);
 532     const char *election_op = crm_element_value(xml, F_CRM_TASK);
 533     const char *host = crm_element_value(xml, F_ATTRD_HOST);
 534 
 535     if(election_op) {
 536         enum election_result rc = 0;
 537 
 538         crm_xml_add(xml, F_CRM_HOST_FROM, peer->uname);
 539         rc = election_count_vote(writer, xml, TRUE);
 540         switch(rc) {
 541             case election_start:
 542                 free(peer_writer);
 543                 peer_writer = NULL;
 544                 election_vote(writer);
 545                 break;
 546             case election_lost:
 547                 free(peer_writer);
 548                 peer_writer = strdup(peer->uname);
 549                 break;
 550             default:
 551                 election_check(writer);
 552                 break;
 553         }
 554         return;
 555 
 556     } else if(v == NULL) {
 557         /* From the non-atomic version */
 558         if (safe_str_eq(op, ATTRD_OP_UPDATE)) {
 559             const char *name = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
 560 
 561             crm_trace("Compatibility update of %s from %s", name, peer->uname);
 562             attrd_peer_update(peer, xml, host, FALSE);
 563 
 564         } else if (safe_str_eq(op, ATTRD_OP_FLUSH)) {
 565             const char *name = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
 566             attribute_t *a = g_hash_table_lookup(attributes, name);
 567 
 568             if(a) {
 569                 crm_trace("Compatibility write-out of %s for %s from %s", a->id, op, peer->uname);
 570                 write_or_elect_attribute(a);
 571             }
 572 
 573         } else if (safe_str_eq(op, ATTRD_OP_REFRESH)) {
 574             GHashTableIter aIter;
 575             attribute_t *a = NULL;
 576 
 577             g_hash_table_iter_init(&aIter, attributes);
 578             while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 579                 crm_trace("Compatibility write-out of %s for %s from %s", a->id, op, peer->uname);
 580                 write_or_elect_attribute(a);
 581             }
 582         }
 583     }
 584 
 585     crm_element_value_int(xml, F_ATTRD_WRITER, &peer_state);
 586     if(election_state(writer) == election_won
 587        && peer_state == election_won
 588        && safe_str_neq(peer->uname, attrd_cluster->uname)) {
 589         crm_notice("Detected another attribute writer: %s", peer->uname);
 590         election_vote(writer);
 591 
 592     } else if(peer_state == election_won) {
 593         if(peer_writer == NULL) {
 594             peer_writer = strdup(peer->uname);
 595             crm_notice("Recorded attribute writer: %s", peer->uname);
 596 
 597         } else if(safe_str_neq(peer->uname, peer_writer)) {
 598             crm_notice("Recorded new attribute writer: %s (was %s)", peer->uname, peer_writer);
 599             free(peer_writer);
 600             peer_writer = strdup(peer->uname);
 601         }
 602     }
 603 
 604     if (safe_str_eq(op, ATTRD_OP_UPDATE) || safe_str_eq(op, ATTRD_OP_UPDATE_BOTH) || safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) {
 605         attrd_peer_update(peer, xml, host, FALSE);
 606 
 607     } else if (safe_str_eq(op, ATTRD_OP_SYNC)) {
 608         attrd_peer_sync(peer, xml);
 609 
 610     } else if (safe_str_eq(op, ATTRD_OP_PEER_REMOVE)) {
 611         attrd_peer_remove(host, TRUE, peer->uname);
 612 
 613     } else if (safe_str_eq(op, ATTRD_OP_CLEAR_FAILURE)) {
 614         /* It is not currently possible to receive this as a peer command,
 615          * but will be, if we one day enable propagating this operation.
 616          */
 617         attrd_peer_clear_failure(peer, xml);
 618 
 619     } else if (safe_str_eq(op, ATTRD_OP_SYNC_RESPONSE)
 620               && safe_str_neq(peer->uname, attrd_cluster->uname)) {
 621         xmlNode *child = NULL;
 622 
 623         crm_info("Processing %s from %s", op, peer->uname);
 624         for (child = __xml_first_child(xml); child != NULL; child = __xml_next(child)) {
 625             host = crm_element_value(child, F_ATTRD_HOST);
 626             attrd_peer_update(peer, child, host, TRUE);
 627         }
 628     }
 629 }
 630 
 631 void
 632 attrd_peer_sync(crm_node_t *peer, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 633 {
 634     GHashTableIter aIter;
 635     GHashTableIter vIter;
 636 
 637     attribute_t *a = NULL;
 638     attribute_value_t *v = NULL;
 639     xmlNode *sync = create_xml_node(NULL, __FUNCTION__);
 640 
 641     crm_xml_add(sync, F_ATTRD_TASK, ATTRD_OP_SYNC_RESPONSE);
 642 
 643     g_hash_table_iter_init(&aIter, attributes);
 644     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 645         g_hash_table_iter_init(&vIter, a->values);
 646         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 647             crm_debug("Syncing %s[%s] = %s to %s", a->id, v->nodename, v->current, peer?peer->uname:"everyone");
 648             build_attribute_xml(sync, a->id, a->set, a->uuid, a->timeout_ms, a->user, a->is_private,
 649                                 v->nodename, v->nodeid, v->current);
 650         }
 651     }
 652 
 653     crm_debug("Syncing values to %s", peer?peer->uname:"everyone");
 654     send_attrd_message(peer, sync);
 655     free_xml(sync);
 656 }
 657 
 658 /*!
 659  * \internal
 660  * \brief Remove all attributes and optionally peer cache entries for a node
 661  *
 662  * \param[in] host     Name of node to purge
 663  * \param[in] uncache  If TRUE, remove node from peer caches
 664  * \param[in] source   Who requested removal (only used for logging)
 665  */
 666 void
 667 attrd_peer_remove(const char *host, gboolean uncache, const char *source)
     /* [previous][next][first][last][top][bottom][index][help] */
 668 {
 669     attribute_t *a = NULL;
 670     GHashTableIter aIter;
 671 
 672     CRM_CHECK(host != NULL, return);
 673     crm_notice("Removing all %s attributes for peer %s", host, source);
 674 
 675     g_hash_table_iter_init(&aIter, attributes);
 676     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 677         if(g_hash_table_remove(a->values, host)) {
 678             crm_debug("Removed %s[%s] for peer %s", a->id, host, source);
 679         }
 680     }
 681 
 682     if (uncache) {
 683         crm_remote_peer_cache_remove(host);
 684         reap_crm_member(0, host);
 685     }
 686 }
 687 
 688 /*!
 689  * \internal
 690  * \brief Return host's hash table entry (creating one if needed)
 691  *
 692  * \param[in] values Hash table of values
 693  * \param[in] host Name of peer to look up
 694  * \param[in] xml XML describing the attribute
 695  *
 696  * \return Pointer to new or existing hash table entry
 697  */
 698 static attribute_value_t *
 699 attrd_lookup_or_create_value(GHashTable *values, const char *host, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 700 {
 701     attribute_value_t *v = g_hash_table_lookup(values, host);
 702     int is_remote = 0;
 703 
 704     crm_element_value_int(xml, F_ATTRD_IS_REMOTE, &is_remote);
 705     if (is_remote) {
 706         /* If we previously assumed this node was an unseen cluster node,
 707          * remove its entry from the cluster peer cache.
 708          */
 709         crm_node_t *dup = crm_find_peer(0, host);
 710 
 711         if (dup && (dup->uuid == NULL)) {
 712             reap_crm_member(0, host);
 713         }
 714 
 715         /* Ensure this host is in the remote peer cache */
 716         crm_remote_peer_cache_add(host);
 717     }
 718 
 719     if (v == NULL) {
 720         v = calloc(1, sizeof(attribute_value_t));
 721         CRM_ASSERT(v != NULL);
 722 
 723         v->nodename = strdup(host);
 724         CRM_ASSERT(v->nodename != NULL);
 725 
 726         v->is_remote = is_remote;
 727         g_hash_table_replace(values, v->nodename, v);
 728     }
 729     return(v);
 730 }
 731 
 732 void
 733 attrd_peer_update(crm_node_t *peer, xmlNode *xml, const char *host, bool filter)
     /* [previous][next][first][last][top][bottom][index][help] */
 734 {
 735     bool update_both = FALSE;
 736     attribute_t *a;
 737     attribute_value_t *v = NULL;
 738 
 739     const char *op = crm_element_value(xml, F_ATTRD_TASK);
 740     const char *attr = crm_element_value(xml, F_ATTRD_ATTRIBUTE);
 741     const char *value = crm_element_value(xml, F_ATTRD_VALUE);
 742 
 743     if (attr == NULL) {
 744         crm_warn("Could not update attribute: peer did not specify name");
 745         return;
 746     }
 747 
 748     update_both = ((op == NULL) // ATTRD_OP_SYNC_RESPONSE has no F_ATTRD_TASK
 749                    || safe_str_eq(op, ATTRD_OP_UPDATE_BOTH));
 750 
 751     // Look up or create attribute entry
 752     a = g_hash_table_lookup(attributes, attr);
 753     if (a == NULL) {
 754         if (update_both || safe_str_eq(op, ATTRD_OP_UPDATE)) {
 755             a = create_attribute(xml);
 756         } else {
 757             crm_warn("Could not update %s: attribute not found", attr);
 758             return;
 759         }
 760     }
 761 
 762     // Update attribute dampening
 763     if (update_both || safe_str_eq(op, ATTRD_OP_UPDATE_DELAY)) {
 764         const char *dvalue = crm_element_value(xml, F_ATTRD_DAMPEN);
 765         int dampen = 0;
 766 
 767         if (dvalue == NULL) {
 768             crm_warn("Could not update %s: peer did not specify value for delay",
 769                      attr);
 770             return;
 771         }
 772 
 773         dampen = crm_get_msec(dvalue);
 774         if (dampen < 0) {
 775             crm_warn("Could not update %s: invalid delay value %dms (%s)",
 776                      attr, dampen, dvalue);
 777             return;
 778         }
 779 
 780         if (a->timeout_ms != dampen) {
 781             mainloop_timer_stop(a->timer);
 782             mainloop_timer_del(a->timer);
 783             a->timeout_ms = dampen;
 784             if (dampen > 0) {
 785                 a->timer = mainloop_timer_add(attr, a->timeout_ms, FALSE,
 786                                               attribute_timer_cb, a);
 787                 crm_info("Update attribute %s delay to %dms (%s)",
 788                          attr, dampen, dvalue);
 789             } else {
 790                 a->timer = NULL;
 791                 crm_info("Update attribute %s to remove delay", attr);
 792             }
 793 
 794             /* If dampening changed, do an immediate write-out,
 795              * otherwise repeated dampening changes would prevent write-outs
 796              */
 797             write_or_elect_attribute(a);
 798         }
 799 
 800         if (!update_both) {
 801             return;
 802         }
 803     }
 804 
 805     // If no host was specified, update all hosts recursively
 806     if (host == NULL) {
 807         GHashTableIter vIter;
 808 
 809         crm_debug("Setting %s for all hosts to %s", attr, value);
 810         xml_remove_prop(xml, F_ATTRD_HOST_ID);
 811         g_hash_table_iter_init(&vIter, a->values);
 812         while (g_hash_table_iter_next(&vIter, (gpointer *) & host, NULL)) {
 813             attrd_peer_update(peer, xml, host, filter);
 814         }
 815         return;
 816     }
 817 
 818     // Update attribute value for one host
 819 
 820     v = attrd_lookup_or_create_value(a->values, host, xml);
 821 
 822     if (filter && safe_str_neq(v->current, value)
 823         && safe_str_eq(host, attrd_cluster->uname)) {
 824 
 825         xmlNode *sync = create_xml_node(NULL, __FUNCTION__);
 826 
 827         crm_notice("%s[%s]: local value '%s' takes priority over '%s' from %s",
 828                    attr, host, v->current, value, peer->uname);
 829 
 830         crm_xml_add(sync, F_ATTRD_TASK, ATTRD_OP_SYNC_RESPONSE);
 831         v = g_hash_table_lookup(a->values, host);
 832         build_attribute_xml(sync, attr, a->set, a->uuid, a->timeout_ms, a->user,
 833                             a->is_private, v->nodename, v->nodeid, v->current);
 834 
 835         crm_xml_add_int(sync, F_ATTRD_WRITER, election_state(writer));
 836 
 837         /* Broadcast in case any other nodes had the inconsistent value */
 838         send_attrd_message(NULL, sync);
 839         free_xml(sync);
 840 
 841     } else if (safe_str_neq(v->current, value)) {
 842         crm_info("Setting %s[%s]: %s -> %s from %s",
 843                  attr, host, v->current, value, peer->uname);
 844         free(v->current);
 845         v->current = (value? strdup(value) : NULL);
 846         a->changed = TRUE;
 847 
 848         // Write out new value or start dampening timer
 849         if (a->timer) {
 850             crm_trace("Delayed write out (%dms) for %s", a->timeout_ms, attr);
 851             mainloop_timer_start(a->timer);
 852         } else {
 853             write_or_elect_attribute(a);
 854         }
 855 
 856     } else {
 857         crm_trace("Unchanged %s[%s] from %s is %s", attr, host, peer->uname, value);
 858     }
 859 
 860     /* If this is a cluster node whose node ID we are learning, remember it */
 861     if ((v->nodeid == 0) && (v->is_remote == FALSE)
 862         && (crm_element_value_int(xml, F_ATTRD_HOST_ID, (int*)&v->nodeid) == 0)) {
 863 
 864         crm_node_t *known_peer = crm_get_peer(v->nodeid, host);
 865 
 866         crm_trace("Learned %s has node id %s",
 867                   known_peer->uname, known_peer->uuid);
 868         if (election_state(writer) == election_won) {
 869             write_attributes(FALSE);
 870         }
 871     }
 872 }
 873 
 874 void
 875 write_or_elect_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 876 {
 877     enum election_result rc = election_state(writer);
 878     if(rc == election_won) {
 879         write_attribute(a);
 880 
 881     } else if(rc == election_in_progress) {
 882         crm_trace("Election in progress to determine who will write out %s", a->id);
 883 
 884     } else if(peer_writer == NULL) {
 885         crm_info("Starting an election to determine who will write out %s", a->id);
 886         election_vote(writer);
 887 
 888     } else {
 889         crm_trace("%s will write out %s, we are in state %d", peer_writer, a->id, rc);
 890     }
 891 }
 892 
 893 gboolean
 894 attrd_election_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 895 {
 896     free(peer_writer);
 897     peer_writer = strdup(attrd_cluster->uname);
 898 
 899     /* Update the peers after an election */
 900     attrd_peer_sync(NULL, NULL);
 901 
 902     /* Update the CIB after an election */
 903     write_attributes(TRUE);
 904     return FALSE;
 905 }
 906 
 907 
 908 void
 909 attrd_peer_change_cb(enum crm_status_type kind, crm_node_t *peer, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 910 {
 911     if ((kind == crm_status_nstate) || (kind == crm_status_rstate)) {
 912         if (safe_str_eq(peer->state, CRM_NODE_MEMBER)) {
 913             /* If we're the writer, send new peers a list of all attributes
 914              * (unless it's a remote node, which doesn't run its own attrd)
 915              */
 916             if ((election_state(writer) == election_won)
 917                 && !is_set(peer->flags, crm_remote_node)) {
 918                 attrd_peer_sync(peer, NULL);
 919             }
 920         } else {
 921             /* Remove all attribute values associated with lost nodes */
 922             attrd_peer_remove(peer->uname, FALSE, "loss");
 923             if (peer_writer && safe_str_eq(peer->uname, peer_writer)) {
 924                 free(peer_writer);
 925                 peer_writer = NULL;
 926                 crm_notice("Lost attribute writer %s", peer->uname);
 927             }
 928         }
 929     }
 930 }
 931 
 932 static void
 933 attrd_cib_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 934 {
 935     int level = LOG_ERR;
 936     GHashTableIter iter;
 937     const char *peer = NULL;
 938     attribute_value_t *v = NULL;
 939 
 940     char *name = user_data;
 941     attribute_t *a = g_hash_table_lookup(attributes, name);
 942 
 943     if(a == NULL) {
 944         crm_info("Attribute %s no longer exists", name);
 945         goto done;
 946     }
 947 
 948     a->update = 0;
 949     if (rc == pcmk_ok && call_id < 0) {
 950         rc = call_id;
 951     }
 952 
 953     switch (rc) {
 954         case pcmk_ok:
 955             level = LOG_INFO;
 956             last_cib_op_done = call_id;
 957             break;
 958         case -pcmk_err_diff_failed:    /* When an attr changes while the CIB is syncing */
 959         case -ETIME:           /* When an attr changes while there is a DC election */
 960         case -ENXIO:           /* When an attr changes while the CIB is syncing a
 961                                 *   newer config from a node that just came up
 962                                 */
 963             level = LOG_WARNING;
 964             break;
 965     }
 966 
 967     do_crm_log(level, "Update %d for %s: %s (%d)", call_id, name, pcmk_strerror(rc), rc);
 968 
 969     g_hash_table_iter_init(&iter, a->values);
 970     while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
 971         do_crm_log(level, "Update %d for %s[%s]=%s: %s (%d)", call_id, a->id, peer, v->requested, pcmk_strerror(rc), rc);
 972         free(v->requested);
 973         v->requested = NULL;
 974         if (rc != pcmk_ok) {
 975             a->changed = TRUE; /* Attempt write out again */
 976         }
 977     }
 978   done:
 979     if(a && a->changed && election_state(writer) == election_won) {
 980         write_attribute(a);
 981     }
 982 }
 983 
 984 void
 985 write_attributes(bool all)
     /* [previous][next][first][last][top][bottom][index][help] */
 986 {
 987     GHashTableIter iter;
 988     attribute_t *a = NULL;
 989 
 990     crm_debug("Writing out %s attributes", all? "all" : "changed");
 991     g_hash_table_iter_init(&iter, attributes);
 992     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
 993         if (!all && a->unknown_peer_uuids) {
 994             // Try writing this attribute again, in case peer ID was learned
 995             a->changed = TRUE;
 996         }
 997 
 998         if(all || a->changed) {
 999             write_attribute(a);
1000         } else {
1001             crm_debug("Skipping unchanged attribute %s", a->id);
1002         }
1003     }
1004 }
1005 
1006 static void
1007 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
1008 {
1009     const char *set = NULL;
1010     xmlNode *xml_obj = NULL;
1011 
1012     xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
1013     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1014 
1015     xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
1016     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
1017 
1018     xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
1019     if (a->set) {
1020         crm_xml_set_id(xml_obj, "%s", a->set);
1021     } else {
1022         crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
1023     }
1024     set = ID(xml_obj);
1025 
1026     xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
1027     if (a->uuid) {
1028         crm_xml_set_id(xml_obj, "%s", a->uuid);
1029     } else {
1030         crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
1031     }
1032     crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
1033 
1034     if(value) {
1035         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
1036 
1037     } else {
1038         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
1039         crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
1040     }
1041 }
1042 
1043 static void
1044 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
1045 {
1046     attribute_value_t *a_v = NULL;
1047     a_v = calloc(1, sizeof(attribute_value_t));
1048     CRM_ASSERT(a_v != NULL);
1049 
1050     a_v->nodeid = v->nodeid;
1051     a_v->nodename = strdup(v->nodename);
1052 
1053     if (v->current != NULL) {
1054         a_v->current = strdup(v->current);
1055     }
1056 
1057     g_hash_table_replace(t, a_v->nodename, a_v);
1058 }
1059 
1060 static void
1061 send_alert_attributes_value(attribute_t *a, GHashTable *t)
     /* [previous][next][first][last][top][bottom][index][help] */
1062 {
1063     int rc = 0;
1064     attribute_value_t *at = NULL;
1065     GHashTableIter vIter;
1066 
1067     g_hash_table_iter_init(&vIter, t);
1068 
1069     while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
1070         rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
1071                                         a->id, at->current);
1072         crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
1073                   a->id, at->nodename, at->current, at->nodeid, rc);
1074     }
1075 }
1076 
1077 void
1078 write_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
1079 {
1080     int private_updates = 0, cib_updates = 0;
1081     xmlNode *xml_top = NULL;
1082     attribute_value_t *v = NULL;
1083     GHashTableIter iter;
1084     enum cib_call_options flags = cib_quorum_override;
1085     GHashTable *alert_attribute_value = NULL;
1086 
1087     if (a == NULL) {
1088         return;
1089     }
1090 
1091     /* If this attribute will be written to the CIB ... */
1092     if (!a->is_private) {
1093 
1094         /* Defer the write if now's not a good time */
1095         if (the_cib == NULL) {
1096             crm_info("Write out of '%s' delayed: cib not connected", a->id);
1097             return;
1098 
1099         } else if (a->update && (a->update < last_cib_op_done)) {
1100             crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
1101 
1102         } else if (a->update) {
1103             crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
1104             return;
1105 
1106         } else if (mainloop_timer_running(a->timer)) {
1107             crm_info("Write out of '%s' delayed: timer is running", a->id);
1108             return;
1109         }
1110 
1111         /* Initialize the status update XML */
1112         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
1113     }
1114 
1115     /* Attribute will be written shortly, so clear changed flag */
1116     a->changed = FALSE;
1117 
1118     /* We will check all peers' uuids shortly, so initialize this to false */
1119     a->unknown_peer_uuids = FALSE;
1120 
1121     /* Make the table for the attribute trap */
1122     alert_attribute_value = g_hash_table_new_full(crm_strcase_hash,
1123                                                   crm_strcase_equal, NULL,
1124                                                   free_attribute_value);
1125 
1126     /* Iterate over each peer value of this attribute */
1127     g_hash_table_iter_init(&iter, a->values);
1128     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
1129         crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
1130 
1131         /* If the value's peer info does not correspond to a peer, ignore it */
1132         if (peer == NULL) {
1133             crm_notice("Update error (peer not found): %s[%s]=%s failed (host=%p)",
1134                        a->id, v->nodename, v->current, peer);
1135             continue;
1136         }
1137 
1138         /* If we're just learning the peer's node id, remember it */
1139         if (peer->id && (v->nodeid == 0)) {
1140             crm_trace("Updating value's nodeid");
1141             v->nodeid = peer->id;
1142         }
1143 
1144         /* If this is a private attribute, no update needs to be sent */
1145         if (a->is_private) {
1146             private_updates++;
1147             continue;
1148         }
1149 
1150         /* If the peer is found, but its uuid is unknown, defer write */
1151         if (peer->uuid == NULL) {
1152             a->unknown_peer_uuids = TRUE;
1153             crm_notice("Update %s[%s]=%s postponed: unknown peer UUID, will retry if UUID is learned",
1154                        a->id, v->nodename, v->current, peer);
1155             continue;
1156         }
1157 
1158         /* Add this value to status update XML */
1159         crm_debug("Update: %s[%s]=%s (%s %u %u %s)", a->id, v->nodename,
1160                   v->current, peer->uuid, peer->id, v->nodeid, peer->uname);
1161         build_update_element(xml_top, a, peer->uuid, v->current);
1162         cib_updates++;
1163 
1164         /* Preservation of the attribute to transmit alert */
1165         set_alert_attribute_value(alert_attribute_value, v);
1166 
1167         free(v->requested);
1168         v->requested = NULL;
1169         if (v->current) {
1170             v->requested = strdup(v->current);
1171         } else {
1172             /* Older attrd versions don't know about the cib_mixed_update
1173              * flag so make sure it goes to the local cib which does
1174              */
1175             flags |= cib_mixed_update|cib_scope_local;
1176         }
1177     }
1178 
1179     if (private_updates) {
1180         crm_info("Processed %d private change%s for %s, id=%s, set=%s",
1181                  private_updates, ((private_updates == 1)? "" : "s"),
1182                  a->id, (a->uuid? a->uuid : "<n/a>"), a->set);
1183     }
1184     if (cib_updates) {
1185         crm_log_xml_trace(xml_top, __FUNCTION__);
1186 
1187         a->update = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, XML_CIB_TAG_STATUS, xml_top, NULL,
1188                                     flags, a->user);
1189 
1190         crm_info("Sent update %d with %d changes for %s, id=%s, set=%s",
1191                  a->update, cib_updates, a->id, (a->uuid? a->uuid : "<n/a>"), a->set);
1192 
1193         the_cib->cmds->register_callback_full(the_cib, a->update, 120, FALSE,
1194                                               strdup(a->id),
1195                                               "attrd_cib_callback",
1196                                               attrd_cib_callback, free);
1197         /* Transmit alert of the attribute */
1198         send_alert_attributes_value(a, alert_attribute_value);
1199 
1200     }
1201 
1202     g_hash_table_destroy(alert_attribute_value);
1203     free_xml(xml_top);
1204 }

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