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, a->timeout_ms / 1000);
 151     crm_xml_add_int(xml, PCMK__XA_ATTR_IS_PRIVATE,
 152                     pcmk_is_set(a->flags, attrd_attr_is_private));
 153     crm_xml_add_int(xml, PCMK__XA_ATTRD_IS_FORCE_WRITE, force_write);
 154 
 155     return xml;
 156 }
 157 
 158 void
 159 attrd_clear_value_seen(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161     GHashTableIter aIter;
 162     GHashTableIter vIter;
 163     attribute_t *a;
 164     attribute_value_t *v = NULL;
 165 
 166     g_hash_table_iter_init(&aIter, attributes);
 167     while (g_hash_table_iter_next(&aIter, NULL, (gpointer *) & a)) {
 168         g_hash_table_iter_init(&vIter, a->values);
 169         while (g_hash_table_iter_next(&vIter, NULL, (gpointer *) & v)) {
 170             attrd_clear_value_flags(v, attrd_value_from_peer);
 171         }
 172     }
 173 }
 174 
 175 attribute_t *
 176 attrd_populate_attribute(xmlNode *xml, const char *attr)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178     attribute_t *a = NULL;
 179     bool update_both = false;
 180 
 181     const char *op = crm_element_value(xml, PCMK_XA_TASK);
 182 
 183     // NULL because PCMK__ATTRD_CMD_SYNC_RESPONSE has no PCMK_XA_TASK
 184     update_both = pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_BOTH,
 185                                pcmk__str_null_matches);
 186 
 187     // Look up or create attribute entry
 188     a = g_hash_table_lookup(attributes, attr);
 189     if (a == NULL) {
 190         if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE, pcmk__str_none)) {
 191             a = attrd_create_attribute(xml);
 192             if (a == NULL) {
 193                 return NULL;
 194             }
 195 
 196         } else {
 197             crm_warn("Could not update %s: attribute not found", attr);
 198             return NULL;
 199         }
 200     }
 201 
 202     // Update attribute dampening
 203     if (update_both || pcmk__str_eq(op, PCMK__ATTRD_CMD_UPDATE_DELAY, pcmk__str_none)) {
 204         int rc = attrd_update_dampening(a, xml, attr);
 205 
 206         if (rc != pcmk_rc_ok || !update_both) {
 207             return NULL;
 208         }
 209     }
 210 
 211     return a;
 212 }
 213 
 214 /*!
 215  * \internal
 216  * \brief Get the XML ID used to write out an attribute set
 217  *
 218  * \param[in] attr           Attribute to get set ID for
 219  * \param[in] node_state_id  XML ID of node state that attribute value is for
 220  *
 221  * \return Newly allocated string with XML ID to use for \p attr set
 222  */
 223 char *
 224 attrd_set_id(const attribute_t *attr, const char *node_state_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     char *set_id = NULL;
 227 
 228     CRM_ASSERT((attr != NULL) && (node_state_id != NULL));
 229 
 230     if (attr->set_id == NULL) {
 231         /* @COMPAT This should really take the set type into account. Currently
 232          * we use the same XML ID for transient attributes and utilization
 233          * attributes. It doesn't cause problems because the status section is
 234          * not limited by the schema in any way, but it's still unfortunate.
 235          * For backward compatibility reasons, we can't change this.
 236          */
 237         set_id = crm_strdup_printf("%s-%s", PCMK_XE_STATUS, node_state_id);
 238     } else {
 239         /* @COMPAT When the user specifies a set ID for an attribute, it is the
 240          * same for every node. That is less than ideal, but again, the schema
 241          * doesn't enforce anything for the status section. We couldn't change
 242          * it without allowing the set ID to vary per value rather than per
 243          * attribute, which would break backward compatibility, pose design
 244          * challenges, and potentially cause problems in rolling upgrades.
 245          */
 246         set_id = pcmk__str_copy(attr->set_id);
 247     }
 248     crm_xml_sanitize_id(set_id);
 249     return set_id;
 250 }
 251 
 252 /*!
 253  * \internal
 254  * \brief Get the XML ID used to write out an attribute value
 255  *
 256  * \param[in] attr           Attribute to get value XML ID for
 257  * \param[in] node_state_id  UUID of node that attribute value is for
 258  *
 259  * \return Newly allocated string with XML ID of \p attr value
 260  */
 261 char *
 262 attrd_nvpair_id(const attribute_t *attr, const char *node_state_id)
     /* [previous][next][first][last][top][bottom][index][help] */
 263 {
 264     char *nvpair_id = NULL;
 265 
 266     if (attr->set_id != NULL) {
 267         nvpair_id = crm_strdup_printf("%s-%s", attr->set_id, attr->id);
 268 
 269     } else {
 270         nvpair_id = crm_strdup_printf(PCMK_XE_STATUS "-%s-%s",
 271                                       node_state_id, attr->id);
 272     }
 273     crm_xml_sanitize_id(nvpair_id);
 274     return nvpair_id;
 275 }

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