root/daemons/attrd/attrd_attributes.c

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

DEFINITIONS

This source file includes following definitions.
  1. attrd_create_attribute
  2. attrd_update_dampening
  3. attrd_add_value_xml
  4. attrd_clear_value_seen
  5. attrd_populate_attribute
  6. attrd_set_id
  7. attrd_nvpair_id

   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 <stdbool.h>
  14 #include <stdlib.h>
  15 #include <glib.h>
  16 
  17 #include <crm/common/logging.h>
  18 #include <crm/common/results.h>
  19 #include <crm/common/strings_internal.h>
  20 #include <crm/common/xml.h>
  21 
  22 #include "pacemaker-attrd.h"
  23 
  24 static attribute_t *
  25 attrd_create_attribute(xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  26 {
  27     int is_private = 0;
  28     long long dampen = 0;
  29     const char *name = crm_element_value(xml, PCMK__XA_ATTR_NAME);
  30     const char *set_type = crm_element_value(xml, PCMK__XA_ATTR_SET_TYPE);
  31     const char *dampen_s = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
  32     attribute_t *a = NULL;
  33 
  34     if (set_type == NULL) {
  35         set_type = PCMK_XE_INSTANCE_ATTRIBUTES;
  36     }
  37 
  38     /* Set type is meaningful only when writing to the CIB. Private
  39      * attributes are not written.
  40      */
  41     crm_element_value_int(xml, PCMK__XA_ATTR_IS_PRIVATE, &is_private);
  42     if (!is_private && !pcmk__str_any_of(set_type,
  43                                          PCMK_XE_INSTANCE_ATTRIBUTES,
  44                                          PCMK_XE_UTILIZATION, NULL)) {
  45         crm_warn("Ignoring attribute %s with invalid set type %s",
  46                  pcmk__s(name, "(unidentified)"), set_type);
  47         return NULL;
  48     }
  49 
  50     a = pcmk__assert_alloc(1, sizeof(attribute_t));
  51 
  52     a->id = pcmk__str_copy(name);
  53     a->set_type = pcmk__str_copy(set_type);
  54     a->set_id = crm_element_value_copy(xml, PCMK__XA_ATTR_SET);
  55     a->user = crm_element_value_copy(xml, PCMK__XA_ATTR_USER);
  56     a->values = pcmk__strikey_table(NULL, attrd_free_attribute_value);
  57 
  58     if (is_private) {
  59         attrd_set_attr_flags(a, attrd_attr_is_private);
  60     }
  61 
  62     if (dampen_s != NULL) {
  63         dampen = crm_get_msec(dampen_s);
  64     }
  65 
  66     if (dampen > 0) {
  67         a->timeout_ms = (int) QB_MIN(dampen, INT_MAX);
  68         a->timer = attrd_add_timer(a->id, a->timeout_ms, a);
  69     } else if (dampen < 0) {
  70         crm_warn("Ignoring invalid delay %s for attribute %s", dampen_s, a->id);
  71     }
  72 
  73     crm_trace("Created attribute %s with %s write delay and %s CIB user",
  74               a->id,
  75               ((dampen > 0)? pcmk__readable_interval(a->timeout_ms) : "no"),
  76               pcmk__s(a->user, "default"));
  77 
  78     g_hash_table_replace(attributes, a->id, a);
  79     return a;
  80 }
  81 
  82 static int
  83 attrd_update_dampening(attribute_t *a, xmlNode *xml, const char *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
  84 {
  85     const char *dvalue = crm_element_value(xml, PCMK__XA_ATTR_DAMPENING);
  86     long long dampen = 0;
  87 
  88     if (dvalue == NULL) {
  89         crm_warn("Could not update %s: peer did not specify value for delay",
  90                  attr);
  91         return EINVAL;
  92     }
  93 
  94     dampen = crm_get_msec(dvalue);
  95     if (dampen < 0) {
  96         crm_warn("Could not update %s: invalid delay value %dms (%s)",
  97                  attr, dampen, dvalue);
  98         return EINVAL;
  99     }
 100 
 101     if (a->timeout_ms != dampen) {
 102         mainloop_timer_del(a->timer);
 103         a->timeout_ms = (int) QB_MIN(dampen, INT_MAX);
 104         if (dampen > 0) {
 105             a->timer = attrd_add_timer(attr, a->timeout_ms, a);
 106             crm_info("Update attribute %s delay to %dms (%s)",
 107                      attr, dampen, dvalue);
 108         } else {
 109             a->timer = NULL;
 110             crm_info("Update attribute %s to remove delay", attr);
 111         }
 112 
 113         /* If dampening changed, do an immediate write-out,
 114          * otherwise repeated dampening changes would prevent write-outs
 115          */
 116         attrd_write_or_elect_attribute(a);
 117     }
 118 
 119     return pcmk_rc_ok;
 120 }
 121 
 122 GHashTable *attributes = NULL;
 123 
 124 /*!
 125  * \internal
 126  * \brief Create an XML representation of an attribute for use in peer messages
 127  *
 128  * \param[in,out] parent      Create attribute XML as child element of this
 129  * \param[in]     a           Attribute to represent
 130  * \param[in]     v           Attribute value to represent
 131  * \param[in]     force_write If true, value should be written even if unchanged
 132  *
 133  * \return XML representation of attribute
 134  */
 135 xmlNode *
 136 attrd_add_value_xml(xmlNode *parent, const attribute_t *a,
     /* [previous][next][first][last][top][bottom][index][help] */
 137                     const attribute_value_t *v, bool force_write)
 138 {
 139     xmlNode *xml = pcmk__xe_create(parent, __func__);
 140 
 141     crm_xml_add(xml, PCMK__XA_ATTR_NAME, a->id);
 142     crm_xml_add(xml, PCMK__XA_ATTR_SET_TYPE, a->set_type);
 143     crm_xml_add(xml, PCMK__XA_ATTR_SET, a->set_id);
 144     crm_xml_add(xml, PCMK__XA_ATTR_USER, a->user);
 145     pcmk__xe_add_node(xml, v->nodename, v->nodeid);
 146     if (pcmk_is_set(v->flags, attrd_value_remote)) {
 147         crm_xml_add_int(xml, PCMK__XA_ATTR_IS_REMOTE, 1);
 148     }
 149     crm_xml_add(xml, PCMK__XA_ATTR_VALUE, v->current);
 150     crm_xml_add_int(xml, PCMK__XA_ATTR_DAMPENING,
 151                     pcmk__timeout_ms2s(a->timeout_ms));
 152     crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE,
 153                     pcmk_is_set(a->flags, attrd_attr_is_private));
 154     crm_xml_add_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE, force_write);
 155 
 156     return xml;
 157 }
 158 
 159 void
 160 attrd_clear_value_seen(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162     GHashTableIter aIter;
 163     GHashTableIter vIter;
 164     attribute_t *a;
 165     attribute_value_t *v = NULL;
 166 
 167     g_hash_table_iter_init(&aIter, attributes);
 168     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 169         g_hash_table_iter_init(&vIter, a->values);
 170         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 171             attrd_clear_value_flags(v, attrd_value_from_peer);
 172         }
 173     }
 174 }
 175 
 176 attribute_t *
 177 attrd_populate_attribute(xmlNode *xml, const char *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179     attribute_t *a = NULL;
 180     bool update_both = false;
 181 
 182     const char *op = crm_element_value(xml, PCMK_XA_TASK);
 183 
 184     // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK_XA_TASK
 185     update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
 186                                pcmk__str_null_matches);
 187 
 188     // Look up or create attribute entry
 189     a = g_hash_table_lookup(attributes, attr);
 190     if (a == NULL) {
 191         if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_none)) {
 192             a = attrd_create_attribute(xml);
 193             if (a == NULL) {
 194                 return NULL;
 195             }
 196 
 197         } else {
 198             crm_warn("Could not update %s: attribute not found", attr);
 199             return NULL;
 200         }
 201     }
 202 
 203     // Update attribute dampening
 204     if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_none)) {
 205         int rc = attrd_update_dampening(a, xml, attr);
 206 
 207         if (rc != pcmk_rc_ok || !update_both) {
 208             return NULL;
 209         }
 210     }
 211 
 212     return a;
 213 }
 214 
 215 /*!
 216  * \internal
 217  * \brief Get the XML ID used to write out an attribute set
 218  *
 219  * \param[in] attr           Attribute to get set ID for
 220  * \param[in] node_state_id  XML ID of node state that attribute value is for
 221  *
 222  * \return Newly allocated string with XML ID to use for \p attr set
 223  */
 224 char *
 225 attrd_set_id(const attribute_t *attr, const char *node_state_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227     char *set_id = NULL;
 228 
 229     pcmk__assert((attr != NULL) && (node_state_id != NULL));
 230 
 231     if (pcmk__str_empty(attr->set_id)) {
 232         /* @COMPAT This should really take the set type into account. Currently
 233          * we use the same XML ID for transient attributes and utilization
 234          * attributes. It doesn't cause problems because the status section is
 235          * not limited by the schema in any way, but it's still unfortunate.
 236          * For backward compatibility reasons, we can't change this.
 237          */
 238         set_id = crm_strdup_printf("%s-%s", PCMK_XE_STATUS, node_state_id);
 239     } else {
 240         /* @COMPAT When the user specifies a set ID for an attribute, it is the
 241          * same for every node. That is less than ideal, but again, the schema
 242          * doesn't enforce anything for the status section. We couldn't change
 243          * it without allowing the set ID to vary per value rather than per
 244          * attribute, which would break backward compatibility, pose design
 245          * challenges, and potentially cause problems in rolling upgrades.
 246          */
 247         set_id = pcmk__str_copy(attr->set_id);
 248     }
 249     pcmk__xml_sanitize_id(set_id);
 250     return set_id;
 251 }
 252 
 253 /*!
 254  * \internal
 255  * \brief Get the XML ID used to write out an attribute value
 256  *
 257  * \param[in] attr           Attribute to get value XML ID for
 258  * \param[in] node_state_id  UUID of node that attribute value is for
 259  *
 260  * \return Newly allocated string with XML ID of \p attr value
 261  */
 262 char *
 263 attrd_nvpair_id(const attribute_t *attr, const char *node_state_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265     char *nvpair_id = NULL;
 266 
 267     if (attr->set_id != NULL) {
 268         nvpair_id = crm_strdup_printf("%s-%s", attr->set_id, attr->id);
 269 
 270     } else {
 271         nvpair_id = crm_strdup_printf(PCMK_XE_STATUS "-%s-%s",
 272                                       node_state_id, attr->id);
 273     }
 274     pcmk__xml_sanitize_id(nvpair_id);
 275     return nvpair_id;
 276 }

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