root/daemons/attrd/attrd_cib.c

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

DEFINITIONS

This source file includes following definitions.
  1. attribute_timer_cb
  2. attrd_cib_callback
  3. build_update_element
  4. send_alert_attributes_value
  5. set_alert_attribute_value
  6. attrd_add_timer
  7. attrd_write_attribute
  8. attrd_write_attributes
  9. attrd_write_or_elect_attribute

   1 /*
   2  * Copyright 2013-2023 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 <stdbool.h>
  14 #include <stdlib.h>
  15 #include <glib.h>
  16 
  17 #include <crm/msg_xml.h>
  18 #include <crm/common/logging.h>
  19 #include <crm/common/results.h>
  20 #include <crm/common/strings_internal.h>
  21 #include <crm/common/xml.h>
  22 
  23 #include "pacemaker-attrd.h"
  24 
  25 static int last_cib_op_done = 0;
  26 
  27 static gboolean
  28 attribute_timer_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  29 {
  30     attribute_t *a = data;
  31     crm_trace("Dampen interval expired for %s", a->id);
  32     attrd_write_or_elect_attribute(a);
  33     return FALSE;
  34 }
  35 
  36 static void
  37 attrd_cib_callback(xmlNode *msg, int call_id, int rc, xmlNode *output, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39     int level = LOG_ERR;
  40     GHashTableIter iter;
  41     const char *peer = NULL;
  42     attribute_value_t *v = NULL;
  43 
  44     char *name = user_data;
  45     attribute_t *a = g_hash_table_lookup(attributes, name);
  46 
  47     if(a == NULL) {
  48         crm_info("Attribute %s no longer exists", name);
  49         return;
  50     }
  51 
  52     a->update = 0;
  53     if (rc == pcmk_ok && call_id < 0) {
  54         rc = call_id;
  55     }
  56 
  57     switch (rc) {
  58         case pcmk_ok:
  59             level = LOG_INFO;
  60             last_cib_op_done = call_id;
  61             if (a->timer && !a->timeout_ms) {
  62                 // Remove temporary dampening for failed writes
  63                 mainloop_timer_del(a->timer);
  64                 a->timer = NULL;
  65             }
  66             break;
  67 
  68         case -pcmk_err_diff_failed:    /* When an attr changes while the CIB is syncing */
  69         case -ETIME:           /* When an attr changes while there is a DC election */
  70         case -ENXIO:           /* When an attr changes while the CIB is syncing a
  71                                 *   newer config from a node that just came up
  72                                 */
  73             level = LOG_WARNING;
  74             break;
  75     }
  76 
  77     do_crm_log(level, "CIB update %d result for %s: %s " CRM_XS " rc=%d",
  78                call_id, a->id, pcmk_strerror(rc), rc);
  79 
  80     g_hash_table_iter_init(&iter, a->values);
  81     while (g_hash_table_iter_next(&iter, (gpointer *) & peer, (gpointer *) & v)) {
  82         do_crm_log(level, "* %s[%s]=%s", a->id, peer, v->requested);
  83         free(v->requested);
  84         v->requested = NULL;
  85         if (rc != pcmk_ok) {
  86             a->changed = true; /* Attempt write out again */
  87         }
  88     }
  89 
  90     if (a->changed && attrd_election_won()) {
  91         if (rc == pcmk_ok) {
  92             /* We deferred a write of a new update because this update was in
  93              * progress. Write out the new value without additional delay.
  94              */
  95             attrd_write_attribute(a, false);
  96 
  97         /* We're re-attempting a write because the original failed; delay
  98          * the next attempt so we don't potentially flood the CIB manager
  99          * and logs with a zillion attempts per second.
 100          *
 101          * @TODO We could elect a new writer instead. However, we'd have to
 102          * somehow downgrade our vote, and we'd still need something like this
 103          * if all peers similarly fail to write this attribute (which may
 104          * indicate a corrupted attribute entry rather than a CIB issue).
 105          */
 106         } else if (a->timer) {
 107             // Attribute has a dampening value, so use that as delay
 108             if (!mainloop_timer_running(a->timer)) {
 109                 crm_trace("Delayed re-attempted write for %s by %s",
 110                           name, pcmk__readable_interval(a->timeout_ms));
 111                 mainloop_timer_start(a->timer);
 112             }
 113         } else {
 114             /* Set a temporary dampening of 2 seconds (timer will continue
 115              * to exist until the attribute's dampening gets set or the
 116              * write succeeds).
 117              */
 118             a->timer = attrd_add_timer(a->id, 2000, a);
 119             mainloop_timer_start(a->timer);
 120         }
 121     }
 122 }
 123 
 124 static void
 125 build_update_element(xmlNode *parent, attribute_t *a, const char *nodeid, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 {
 127     const char *set = NULL;
 128     xmlNode *xml_obj = NULL;
 129 
 130     xml_obj = create_xml_node(parent, XML_CIB_TAG_STATE);
 131     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
 132 
 133     xml_obj = create_xml_node(xml_obj, XML_TAG_TRANSIENT_NODEATTRS);
 134     crm_xml_add(xml_obj, XML_ATTR_ID, nodeid);
 135 
 136     if (pcmk__str_eq(a->set_type, XML_TAG_ATTR_SETS, pcmk__str_null_matches)) {
 137         xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
 138     } else if (pcmk__str_eq(a->set_type, XML_TAG_UTILIZATION, pcmk__str_none)) {
 139         xml_obj = create_xml_node(xml_obj, XML_TAG_UTILIZATION);
 140     } else {
 141         crm_err("Unknown set type attribute: %s", a->set_type);
 142     }
 143 
 144     if (a->set_id) {
 145         crm_xml_set_id(xml_obj, "%s", a->set_id);
 146     } else {
 147         crm_xml_set_id(xml_obj, "%s-%s", XML_CIB_TAG_STATUS, nodeid);
 148     }
 149     set = ID(xml_obj);
 150 
 151     xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
 152     if (a->uuid) {
 153         crm_xml_set_id(xml_obj, "%s", a->uuid);
 154     } else {
 155         crm_xml_set_id(xml_obj, "%s-%s", set, a->id);
 156     }
 157     crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, a->id);
 158 
 159     if(value) {
 160         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
 161 
 162     } else {
 163         crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, "");
 164         crm_xml_add(xml_obj, "__delete__", XML_NVPAIR_ATTR_VALUE);
 165     }
 166 }
 167 
 168 static void
 169 send_alert_attributes_value(attribute_t *a, GHashTable *t)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     int rc = 0;
 172     attribute_value_t *at = NULL;
 173     GHashTableIter vIter;
 174 
 175     g_hash_table_iter_init(&vIter, t);
 176 
 177     while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & at)) {
 178         rc = attrd_send_attribute_alert(at->nodename, at->nodeid,
 179                                         a->id, at->current);
 180         crm_trace("Sent alerts for %s[%s]=%s: nodeid=%d rc=%d",
 181                   a->id, at->nodename, at->current, at->nodeid, rc);
 182     }
 183 }
 184 
 185 static void
 186 set_alert_attribute_value(GHashTable *t, attribute_value_t *v)
     /* [previous][next][first][last][top][bottom][index][help] */
 187 {
 188     attribute_value_t *a_v = NULL;
 189     a_v = calloc(1, sizeof(attribute_value_t));
 190     CRM_ASSERT(a_v != NULL);
 191 
 192     a_v->nodeid = v->nodeid;
 193     a_v->nodename = strdup(v->nodename);
 194     pcmk__str_update(&a_v->current, v->current);
 195 
 196     g_hash_table_replace(t, a_v->nodename, a_v);
 197 }
 198 
 199 mainloop_timer_t *
 200 attrd_add_timer(const char *id, int timeout_ms, attribute_t *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202    return mainloop_timer_add(id, timeout_ms, FALSE, attribute_timer_cb, attr);
 203 }
 204 
 205 void
 206 attrd_write_attribute(attribute_t *a, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
 207 {
 208     int private_updates = 0, cib_updates = 0;
 209     xmlNode *xml_top = NULL;
 210     attribute_value_t *v = NULL;
 211     GHashTableIter iter;
 212     enum cib_call_options flags = cib_none;
 213     GHashTable *alert_attribute_value = NULL;
 214 
 215     if (a == NULL) {
 216         return;
 217     }
 218 
 219     /* If this attribute will be written to the CIB ... */
 220     if (!stand_alone && !a->is_private) {
 221 
 222         /* Defer the write if now's not a good time */
 223         CRM_CHECK(the_cib != NULL, return);
 224         if (a->update && (a->update < last_cib_op_done)) {
 225             crm_info("Write out of '%s' continuing: update %d considered lost", a->id, a->update);
 226             a->update = 0; // Don't log this message again
 227 
 228         } else if (a->update) {
 229             crm_info("Write out of '%s' delayed: update %d in progress", a->id, a->update);
 230             return;
 231 
 232         } else if (mainloop_timer_running(a->timer)) {
 233             if (ignore_delay) {
 234                 /* 'refresh' forces a write of the current value of all attributes
 235                  * Cancel any existing timers, we're writing it NOW
 236                  */
 237                 mainloop_timer_stop(a->timer);
 238                 crm_debug("Write out of '%s': timer is running but ignore delay", a->id);
 239             } else {
 240                 crm_info("Write out of '%s' delayed: timer is running", a->id);
 241                 return;
 242             }
 243         }
 244 
 245         /* Initialize the status update XML */
 246         xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS);
 247     }
 248 
 249     /* Attribute will be written shortly, so clear changed flag */
 250     a->changed = false;
 251 
 252     /* We will check all peers' uuids shortly, so initialize this to false */
 253     a->unknown_peer_uuids = false;
 254 
 255     /* Attribute will be written shortly, so clear forced write flag */
 256     a->force_write = FALSE;
 257 
 258     /* Make the table for the attribute trap */
 259     alert_attribute_value = pcmk__strikey_table(NULL, attrd_free_attribute_value);
 260 
 261     /* Iterate over each peer value of this attribute */
 262     g_hash_table_iter_init(&iter, a->values);
 263     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & v)) {
 264         crm_node_t *peer = crm_get_peer_full(v->nodeid, v->nodename, CRM_GET_PEER_ANY);
 265 
 266         /* If the value's peer info does not correspond to a peer, ignore it */
 267         if (peer == NULL) {
 268             crm_notice("Cannot update %s[%s]=%s because peer not known",
 269                        a->id, v->nodename, v->current);
 270             continue;
 271         }
 272 
 273         /* If we're just learning the peer's node id, remember it */
 274         if (peer->id && (v->nodeid == 0)) {
 275             crm_trace("Learned ID %u for node %s", peer->id, v->nodename);
 276             v->nodeid = peer->id;
 277         }
 278 
 279         /* If this is a private attribute, no update needs to be sent */
 280         if (stand_alone || a->is_private) {
 281             private_updates++;
 282             continue;
 283         }
 284 
 285         /* If the peer is found, but its uuid is unknown, defer write */
 286         if (peer->uuid == NULL) {
 287             a->unknown_peer_uuids = true;
 288             crm_notice("Cannot update %s[%s]=%s because peer UUID not known "
 289                        "(will retry if learned)",
 290                        a->id, v->nodename, v->current);
 291             continue;
 292         }
 293 
 294         /* Add this value to status update XML */
 295         crm_debug("Updating %s[%s]=%s (peer known as %s, UUID %s, ID %u/%u)",
 296                   a->id, v->nodename, v->current,
 297                   peer->uname, peer->uuid, peer->id, v->nodeid);
 298         build_update_element(xml_top, a, peer->uuid, v->current);
 299         cib_updates++;
 300 
 301         /* Preservation of the attribute to transmit alert */
 302         set_alert_attribute_value(alert_attribute_value, v);
 303 
 304         free(v->requested);
 305         v->requested = NULL;
 306         if (v->current) {
 307             v->requested = strdup(v->current);
 308         } else {
 309             /* Older attrd versions don't know about the cib_mixed_update
 310              * flag so make sure it goes to the local cib which does
 311              */
 312             cib__set_call_options(flags, crm_system_name,
 313                                   cib_mixed_update|cib_scope_local);
 314         }
 315     }
 316 
 317     if (private_updates) {
 318         crm_info("Processed %d private change%s for %s, id=%s, set=%s",
 319                  private_updates, pcmk__plural_s(private_updates),
 320                  a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
 321     }
 322     if (cib_updates) {
 323         crm_log_xml_trace(xml_top, __func__);
 324 
 325         a->update = cib_internal_op(the_cib, PCMK__CIB_REQUEST_MODIFY, NULL,
 326                                     XML_CIB_TAG_STATUS, xml_top, NULL, flags,
 327                                     a->user);
 328 
 329         crm_info("Sent CIB request %d with %d change%s for %s (id %s, set %s)",
 330                  a->update, cib_updates, pcmk__plural_s(cib_updates),
 331                  a->id, pcmk__s(a->uuid, "n/a"), pcmk__s(a->set_id, "n/a"));
 332 
 333         the_cib->cmds->register_callback_full(the_cib, a->update,
 334                                               CIB_OP_TIMEOUT_S, FALSE,
 335                                               strdup(a->id),
 336                                               "attrd_cib_callback",
 337                                               attrd_cib_callback, free);
 338         /* Transmit alert of the attribute */
 339         send_alert_attributes_value(a, alert_attribute_value);
 340     }
 341 
 342     g_hash_table_destroy(alert_attribute_value);
 343     free_xml(xml_top);
 344 }
 345 
 346 void
 347 attrd_write_attributes(bool all, bool ignore_delay)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     GHashTableIter iter;
 350     attribute_t *a = NULL;
 351 
 352     crm_debug("Writing out %s attributes", all? "all" : "changed");
 353     g_hash_table_iter_init(&iter, attributes);
 354     while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & a)) {
 355         if (!all && a->unknown_peer_uuids) {
 356             // Try writing this attribute again, in case peer ID was learned
 357             a->changed = true;
 358         } else if (a->force_write) {
 359             /* If the force_write flag is set, write the attribute. */
 360             a->changed = true;
 361         }
 362 
 363         if(all || a->changed) {
 364             /* When forced write flag is set, ignore delay. */
 365             attrd_write_attribute(a, (a->force_write ? true : ignore_delay));
 366         } else {
 367             crm_trace("Skipping unchanged attribute %s", a->id);
 368         }
 369     }
 370 }
 371 
 372 void
 373 attrd_write_or_elect_attribute(attribute_t *a)
     /* [previous][next][first][last][top][bottom][index][help] */
 374 {
 375     if (attrd_election_won()) {
 376         attrd_write_attribute(a, false);
 377     } else {
 378         attrd_start_election_if_needed();
 379     }
 380 }

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