root/daemons/controld/controld_metadata.c

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

DEFINITIONS

This source file includes following definitions.
  1. ra_param_free
  2. metadata_free
  3. metadata_cache_new
  4. metadata_cache_free
  5. metadata_cache_reset
  6. valid_version_format
  7. metadata_cache_fini
  8. ra_version_from_xml
  9. ra_param_from_xml
  10. log_ra_ocf_version
  11. metadata_cache_update
  12. controld_get_rsc_metadata

   1 /*
   2  * Copyright 2017-2022 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 <stdio.h>
  13 #include <glib.h>
  14 #include <regex.h>
  15 
  16 #include <crm/crm.h>
  17 #include <crm/lrmd.h>
  18 
  19 #include <pacemaker-controld.h>
  20 
  21 #if ENABLE_VERSIONED_ATTRS
  22 static regex_t *version_format_regex = NULL;
  23 #endif
  24 
  25 static void
  26 ra_param_free(void *param)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     if (param) {
  29         struct ra_param_s *p = (struct ra_param_s *) param;
  30 
  31         if (p->rap_name) {
  32             free(p->rap_name);
  33         }
  34         free(param);
  35     }
  36 }
  37 
  38 static void
  39 metadata_free(void *metadata)
     /* [previous][next][first][last][top][bottom][index][help] */
  40 {
  41     if (metadata) {
  42         struct ra_metadata_s *md = (struct ra_metadata_s *) metadata;
  43 
  44         if (md->ra_version) {
  45             free(md->ra_version);
  46         }
  47         g_list_free_full(md->ra_params, ra_param_free);
  48         free(metadata);
  49     }
  50 }
  51 
  52 GHashTable *
  53 metadata_cache_new()
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55     return pcmk__strkey_table(free, metadata_free);
  56 }
  57 
  58 void
  59 metadata_cache_free(GHashTable *mdc)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61     if (mdc) {
  62         crm_trace("Destroying metadata cache with %d members", g_hash_table_size(mdc));
  63         g_hash_table_destroy(mdc);
  64     }
  65 }
  66 
  67 void
  68 metadata_cache_reset(GHashTable *mdc)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70     if (mdc) {
  71         crm_trace("Resetting metadata cache with %d members",
  72                   g_hash_table_size(mdc));
  73         g_hash_table_remove_all(mdc);
  74     }
  75 }
  76 
  77 #if ENABLE_VERSIONED_ATTRS
  78 static gboolean
  79 valid_version_format(const char *version)
     /* [previous][next][first][last][top][bottom][index][help] */
  80 {
  81     if (version == NULL) {
  82         return FALSE;
  83     }
  84 
  85     if (version_format_regex == NULL) {
  86         /* The OCF standard allows free-form versioning, but for our purposes of
  87          * versioned resource and operation attributes, we constrain it to
  88          * dot-separated numbers. Agents are still free to use other schemes,
  89          * but we can't determine attributes based on them.
  90          */
  91         const char *regex_string = "^[[:digit:]]+([.][[:digit:]]+)*$";
  92 
  93         version_format_regex = calloc(1, sizeof(regex_t));
  94         regcomp(version_format_regex, regex_string, REG_EXTENDED | REG_NOSUB);
  95 
  96         /* If our regex doesn't compile, it's a bug on our side, so CRM_CHECK()
  97          * will give us a core dump to catch it. Pretend the version is OK
  98          * because we don't want our mistake to break versioned attributes
  99          * (which should only ever happen in a development branch anyway).
 100          */
 101         CRM_CHECK(version_format_regex != NULL, return TRUE);
 102     }
 103 
 104     return regexec(version_format_regex, version, 0, NULL, 0) == 0;
 105 }
 106 #endif
 107 
 108 void
 109 metadata_cache_fini()
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111 #if ENABLE_VERSIONED_ATTRS
 112     if (version_format_regex) {
 113         regfree(version_format_regex);
 114         free(version_format_regex);
 115         version_format_regex = NULL;
 116     }
 117 #endif
 118 }
 119 
 120 #if ENABLE_VERSIONED_ATTRS
 121 static char *
 122 ra_version_from_xml(xmlNode *metadata_xml, const lrmd_rsc_info_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     const char *version = crm_element_value(metadata_xml, XML_ATTR_VERSION);
 125 
 126     if (version == NULL) {
 127         crm_debug("Metadata for %s:%s:%s does not specify a version",
 128                   rsc->standard, rsc->provider, rsc->type);
 129         version = PCMK_DEFAULT_AGENT_VERSION;
 130 
 131     } else if (!valid_version_format(version)) {
 132         crm_notice("%s:%s:%s metadata version has unrecognized format",
 133                   rsc->standard, rsc->provider, rsc->type);
 134         version = PCMK_DEFAULT_AGENT_VERSION;
 135 
 136     } else {
 137         crm_debug("Metadata for %s:%s:%s has version %s",
 138                   rsc->standard, rsc->provider, rsc->type, version);
 139     }
 140     return strdup(version);
 141 }
 142 #endif
 143 
 144 static struct ra_param_s *
 145 ra_param_from_xml(xmlNode *param_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147     const char *param_name = crm_element_value(param_xml, "name");
 148     struct ra_param_s *p;
 149 
 150     p = calloc(1, sizeof(struct ra_param_s));
 151     if (p == NULL) {
 152         crm_crit("Could not allocate memory for resource metadata");
 153         return NULL;
 154     }
 155 
 156     p->rap_name = strdup(param_name);
 157     if (p->rap_name == NULL) {
 158         crm_crit("Could not allocate memory for resource metadata");
 159         free(p);
 160         return NULL;
 161     }
 162 
 163     if (pcmk__xe_attr_is_true(param_xml, "reloadable")) {
 164         controld_set_ra_param_flags(p, ra_param_reloadable);
 165     }
 166 
 167     if (pcmk__xe_attr_is_true(param_xml, "unique")) {
 168         controld_set_ra_param_flags(p, ra_param_unique);
 169     }
 170 
 171     if (pcmk__xe_attr_is_true(param_xml, "private")) {
 172         controld_set_ra_param_flags(p, ra_param_private);
 173     }
 174     return p;
 175 }
 176 
 177 static void
 178 log_ra_ocf_version(const char *ra_key, const char *ra_ocf_version)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     if (pcmk__str_empty(ra_ocf_version)) {
 181         crm_warn("%s does not advertise OCF version supported", ra_key);
 182 
 183     } else if (compare_version(ra_ocf_version, "2") >= 0) {
 184         crm_warn("%s supports OCF version %s (this Pacemaker version supports "
 185                  PCMK_OCF_VERSION " and might not work properly with agent)",
 186                  ra_key, ra_ocf_version);
 187 
 188     } else if (compare_version(ra_ocf_version, PCMK_OCF_VERSION) > 0) {
 189         crm_info("%s supports OCF version %s (this Pacemaker version supports "
 190                  PCMK_OCF_VERSION " and might not use all agent features)",
 191                  ra_key, ra_ocf_version);
 192 
 193     } else {
 194         crm_debug("%s supports OCF version %s", ra_key, ra_ocf_version);
 195     }
 196 }
 197 
 198 struct ra_metadata_s *
 199 metadata_cache_update(GHashTable *mdc, lrmd_rsc_info_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 200                       const char *metadata_str)
 201 {
 202     char *key = NULL;
 203     xmlNode *metadata = NULL;
 204     xmlNode *match = NULL;
 205     struct ra_metadata_s *md = NULL;
 206     bool any_private_params = false;
 207     bool ocf1_1 = false;
 208 
 209     CRM_CHECK(mdc && rsc && metadata_str, return NULL);
 210 
 211     key = crm_generate_ra_key(rsc->standard, rsc->provider, rsc->type);
 212     if (!key) {
 213         crm_crit("Could not allocate memory for resource metadata");
 214         goto err;
 215     }
 216 
 217     metadata = string2xml(metadata_str);
 218     if (!metadata) {
 219         crm_err("Metadata for %s:%s:%s is not valid XML",
 220                 rsc->standard, rsc->provider, rsc->type);
 221         goto err;
 222     }
 223 
 224     md = calloc(1, sizeof(struct ra_metadata_s));
 225     if (md == NULL) {
 226         crm_crit("Could not allocate memory for resource metadata");
 227         goto err;
 228     }
 229 
 230 #if ENABLE_VERSIONED_ATTRS
 231     md->ra_version = ra_version_from_xml(metadata, rsc);
 232 #endif
 233 
 234     if (strcmp(rsc->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
 235         xmlChar *content = NULL;
 236         xmlNode *version_element = first_named_child(metadata, "version");
 237 
 238         if (version_element != NULL) {
 239             content = xmlNodeGetContent(version_element);
 240         }
 241         log_ra_ocf_version(key, (const char *) content);
 242         if (content != NULL) {
 243             ocf1_1 = (compare_version((const char *) content, "1.1") >= 0);
 244             xmlFree(content);
 245         }
 246     }
 247 
 248     // Check supported actions
 249     match = first_named_child(metadata, "actions");
 250     for (match = first_named_child(match, "action"); match != NULL;
 251          match = crm_next_same_xml(match)) {
 252 
 253         const char *action_name = crm_element_value(match, "name");
 254 
 255         if (pcmk__str_eq(action_name, CRMD_ACTION_RELOAD_AGENT,
 256                          pcmk__str_none)) {
 257             if (ocf1_1) {
 258                 controld_set_ra_flags(md, key, ra_supports_reload_agent);
 259             } else {
 260                 crm_notice("reload-agent action will not be used with %s "
 261                            "because it does not support OCF 1.1 or later", key);
 262             }
 263 
 264         } else if (!ocf1_1 && pcmk__str_eq(action_name, CRMD_ACTION_RELOAD,
 265                                            pcmk__str_casei)) {
 266             controld_set_ra_flags(md, key, ra_supports_legacy_reload);
 267         }
 268     }
 269 
 270     // Build a parameter list
 271     match = first_named_child(metadata, "parameters");
 272     for (match = first_named_child(match, "parameter"); match != NULL;
 273          match = crm_next_same_xml(match)) {
 274 
 275         const char *param_name = crm_element_value(match, "name");
 276 
 277         if (param_name == NULL) {
 278             crm_warn("Metadata for %s:%s:%s has parameter without a name",
 279                      rsc->standard, rsc->provider, rsc->type);
 280         } else {
 281             struct ra_param_s *p = ra_param_from_xml(match);
 282 
 283             if (p == NULL) {
 284                 goto err;
 285             }
 286             if (pcmk_is_set(p->rap_flags, ra_param_private)) {
 287                 any_private_params = true;
 288             }
 289             md->ra_params = g_list_prepend(md->ra_params, p);
 290         }
 291     }
 292 
 293     /* Newer resource agents support the "private" parameter attribute to
 294      * indicate sensitive parameters. For backward compatibility with older
 295      * agents, implicitly treat a few common names as private when the agent
 296      * doesn't specify any explicitly.
 297      */
 298     if (!any_private_params) {
 299         for (GList *iter = md->ra_params; iter != NULL; iter = iter->next) {
 300             struct ra_param_s *p = iter->data;
 301 
 302             if (pcmk__str_any_of(p->rap_name, "password", "passwd", "user",
 303                                  NULL)) {
 304                 controld_set_ra_param_flags(p, ra_param_private);
 305             }
 306         }
 307     }
 308 
 309     g_hash_table_replace(mdc, key, md);
 310     free_xml(metadata);
 311     return md;
 312 
 313 err:
 314     free(key);
 315     free_xml(metadata);
 316     metadata_free(md);
 317     return NULL;
 318 }
 319 
 320 /*!
 321  * \internal
 322  * \brief Get meta-data for a resource
 323  *
 324  * \param[in] lrm_state  Use meta-data cache from this executor connection
 325  * \param[in] rsc        Resource to get meta-data for
 326  * \param[in] source     Allowed meta-data sources (bitmask of enum
 327  *                       controld_metadata_source_e values)
 328  *
 329  * \return Meta-data cache entry for given resource, or NULL if not available
 330  */
 331 struct ra_metadata_s *
 332 controld_get_rsc_metadata(lrm_state_t *lrm_state, lrmd_rsc_info_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 333                           uint32_t source)
 334 {
 335     struct ra_metadata_s *metadata = NULL;
 336     char *metadata_str = NULL;
 337     char *key = NULL;
 338     int rc = pcmk_ok;
 339 
 340     CRM_CHECK((lrm_state != NULL) && (rsc != NULL), return NULL);
 341 
 342     if (pcmk_is_set(source, controld_metadata_from_cache)) {
 343         key = crm_generate_ra_key(rsc->standard, rsc->provider, rsc->type);
 344         if (key != NULL) {
 345             metadata = g_hash_table_lookup(lrm_state->metadata_cache, key);
 346             free(key);
 347         }
 348         if (metadata != NULL) {
 349             return metadata;
 350         }
 351     }
 352 
 353     if (!pcmk_is_set(source, controld_metadata_from_agent)) {
 354         return NULL;
 355     }
 356 
 357     /* For now, we always collect resource agent meta-data via a local,
 358      * synchronous, direct execution of the agent. This has multiple issues:
 359      * the executor should execute agents, not the controller; meta-data for
 360      * Pacemaker Remote nodes should be collected on those nodes, not
 361      * locally; and the meta-data call shouldn't eat into the timeout of the
 362      * real action being performed.
 363      *
 364      * These issues are planned to be addressed by having the scheduler
 365      * schedule a meta-data cache check at the beginning of each transition.
 366      * Once that is working, this block will only be a fallback in case the
 367      * initial collection fails.
 368      */
 369     rc = lrm_state_get_metadata(lrm_state, rsc->standard, rsc->provider,
 370                                 rsc->type, &metadata_str, 0);
 371     if (rc != pcmk_ok) {
 372         crm_warn("Failed to get metadata for %s (%s%s%s:%s): %s",
 373                  rsc->id, rsc->standard,
 374                  ((rsc->provider == NULL)? "" : ":"),
 375                  ((rsc->provider == NULL)? "" : rsc->provider),
 376                  rsc->type, pcmk_strerror(rc));
 377         return NULL;
 378     }
 379 
 380     metadata = metadata_cache_update(lrm_state->metadata_cache, rsc,
 381                                      metadata_str);
 382     free(metadata_str);
 383     if (metadata == NULL) {
 384         crm_warn("Failed to update metadata for %s (%s%s%s:%s)",
 385                  rsc->id, rsc->standard, ((rsc->provider == NULL)? "" : ":"),
 386                  ((rsc->provider == NULL)? "" : rsc->provider), rsc->type);
 387     }
 388     return metadata;
 389 }

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