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. ra_param_from_xml
  7. log_ra_ocf_version
  8. controld_cache_metadata
  9. controld_get_rsc_metadata

   1 /*
   2  * Copyright 2017-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 <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 static void
  22 ra_param_free(void *param)
     /* [previous][next][first][last][top][bottom][index][help] */
  23 {
  24     if (param) {
  25         struct ra_param_s *p = (struct ra_param_s *) param;
  26 
  27         if (p->rap_name) {
  28             free(p->rap_name);
  29         }
  30         free(param);
  31     }
  32 }
  33 
  34 static void
  35 metadata_free(void *metadata)
     /* [previous][next][first][last][top][bottom][index][help] */
  36 {
  37     if (metadata) {
  38         struct ra_metadata_s *md = (struct ra_metadata_s *) metadata;
  39 
  40         g_list_free_full(md->ra_params, ra_param_free);
  41         free(metadata);
  42     }
  43 }
  44 
  45 GHashTable *
  46 metadata_cache_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     return pcmk__strkey_table(free, metadata_free);
  49 }
  50 
  51 void
  52 metadata_cache_free(GHashTable *mdc)
     /* [previous][next][first][last][top][bottom][index][help] */
  53 {
  54     if (mdc) {
  55         crm_trace("Destroying metadata cache with %d members", g_hash_table_size(mdc));
  56         g_hash_table_destroy(mdc);
  57     }
  58 }
  59 
  60 void
  61 metadata_cache_reset(GHashTable *mdc)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     if (mdc) {
  64         crm_trace("Resetting metadata cache with %d members",
  65                   g_hash_table_size(mdc));
  66         g_hash_table_remove_all(mdc);
  67     }
  68 }
  69 
  70 static struct ra_param_s *
  71 ra_param_from_xml(xmlNode *param_xml)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73     const char *param_name = crm_element_value(param_xml, PCMK_XA_NAME);
  74     struct ra_param_s *p;
  75 
  76     p = pcmk__assert_alloc(1, sizeof(struct ra_param_s));
  77 
  78     p->rap_name = pcmk__str_copy(param_name);
  79 
  80     if (pcmk__xe_attr_is_true(param_xml, PCMK_XA_RELOADABLE)) {
  81         controld_set_ra_param_flags(p, ra_param_reloadable);
  82     }
  83 
  84     if (pcmk__xe_attr_is_true(param_xml, PCMK_XA_UNIQUE)) {
  85         controld_set_ra_param_flags(p, ra_param_unique);
  86     }
  87 
  88     if (pcmk__xe_attr_is_true(param_xml, "private")) {
  89         controld_set_ra_param_flags(p, ra_param_private);
  90     }
  91     return p;
  92 }
  93 
  94 static void
  95 log_ra_ocf_version(const char *ra_key, const char *ra_ocf_version)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97     if (pcmk__str_empty(ra_ocf_version)) {
  98         crm_warn("%s does not advertise OCF version supported", ra_key);
  99 
 100     } else if (compare_version(ra_ocf_version, "2") >= 0) {
 101         crm_warn("%s supports OCF version %s (this Pacemaker version supports "
 102                  PCMK_OCF_VERSION " and might not work properly with agent)",
 103                  ra_key, ra_ocf_version);
 104 
 105     } else if (compare_version(ra_ocf_version, PCMK_OCF_VERSION) > 0) {
 106         crm_info("%s supports OCF version %s (this Pacemaker version supports "
 107                  PCMK_OCF_VERSION " and might not use all agent features)",
 108                  ra_key, ra_ocf_version);
 109 
 110     } else {
 111         crm_debug("%s supports OCF version %s", ra_key, ra_ocf_version);
 112     }
 113 }
 114 
 115 struct ra_metadata_s *
 116 controld_cache_metadata(GHashTable *mdc, const lrmd_rsc_info_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 117                         const char *metadata_str)
 118 {
 119     char *key = NULL;
 120     const char *reason = NULL;
 121     xmlNode *metadata = NULL;
 122     xmlNode *match = NULL;
 123     struct ra_metadata_s *md = NULL;
 124     bool any_private_params = false;
 125     bool ocf1_1 = false;
 126 
 127     CRM_CHECK(mdc && rsc && metadata_str, return NULL);
 128 
 129     key = crm_generate_ra_key(rsc->standard, rsc->provider, rsc->type);
 130     if (!key) {
 131         reason = "Invalid resource agent standard or type";
 132         goto err;
 133     }
 134 
 135     metadata = pcmk__xml_parse(metadata_str);
 136     if (!metadata) {
 137         reason = "Metadata is not valid XML";
 138         goto err;
 139     }
 140 
 141     md = pcmk__assert_alloc(1, sizeof(struct ra_metadata_s));
 142 
 143     if (strcmp(rsc->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
 144         xmlChar *content = NULL;
 145         xmlNode *version_element = pcmk__xe_first_child(metadata,
 146                                                         PCMK_XE_VERSION, NULL,
 147                                                         NULL);
 148 
 149         if (version_element != NULL) {
 150             content = xmlNodeGetContent(version_element);
 151         }
 152         log_ra_ocf_version(key, (const char *) content);
 153         if (content != NULL) {
 154             ocf1_1 = (compare_version((const char *) content, "1.1") >= 0);
 155             xmlFree(content);
 156         }
 157     }
 158 
 159     // Check supported actions
 160     match = pcmk__xe_first_child(metadata, PCMK_XE_ACTIONS, NULL, NULL);
 161     for (match = pcmk__xe_first_child(match, PCMK_XE_ACTION, NULL, NULL);
 162          match != NULL; match = pcmk__xe_next_same(match)) {
 163 
 164         const char *action_name = crm_element_value(match, PCMK_XA_NAME);
 165 
 166         if (pcmk__str_eq(action_name, PCMK_ACTION_RELOAD_AGENT,
 167                          pcmk__str_none)) {
 168             if (ocf1_1) {
 169                 controld_set_ra_flags(md, key, ra_supports_reload_agent);
 170             } else {
 171                 crm_notice("reload-agent action will not be used with %s "
 172                            "because it does not support OCF 1.1 or later", key);
 173             }
 174 
 175         } else if (!ocf1_1 && pcmk__str_eq(action_name, PCMK_ACTION_RELOAD,
 176                                            pcmk__str_casei)) {
 177             controld_set_ra_flags(md, key, ra_supports_legacy_reload);
 178         }
 179     }
 180 
 181     // Build a parameter list
 182     match = pcmk__xe_first_child(metadata, PCMK_XE_PARAMETERS, NULL, NULL);
 183     for (match = pcmk__xe_first_child(match, PCMK_XE_PARAMETER, NULL, NULL);
 184          match != NULL; match = pcmk__xe_next_same(match)) {
 185 
 186         const char *param_name = crm_element_value(match, PCMK_XA_NAME);
 187 
 188         if (param_name == NULL) {
 189             crm_warn("Metadata for %s:%s:%s has parameter without a "
 190                      PCMK_XA_NAME, rsc->standard, rsc->provider, rsc->type);
 191         } else {
 192             struct ra_param_s *p = ra_param_from_xml(match);
 193 
 194             if (p == NULL) {
 195                 reason = "Could not allocate memory";
 196                 goto err;
 197             }
 198             if (pcmk_is_set(p->rap_flags, ra_param_private)) {
 199                 any_private_params = true;
 200             }
 201             md->ra_params = g_list_prepend(md->ra_params, p);
 202         }
 203     }
 204 
 205     /* Newer resource agents support the "private" parameter attribute to
 206      * indicate sensitive parameters. For backward compatibility with older
 207      * agents, implicitly treat a few common names as private when the agent
 208      * doesn't specify any explicitly.
 209      */
 210     if (!any_private_params) {
 211         for (GList *iter = md->ra_params; iter != NULL; iter = iter->next) {
 212             struct ra_param_s *p = iter->data;
 213 
 214             if (pcmk__str_any_of(p->rap_name, "password", "passwd", "user",
 215                                  NULL)) {
 216                 controld_set_ra_param_flags(p, ra_param_private);
 217             }
 218         }
 219     }
 220 
 221     g_hash_table_replace(mdc, key, md);
 222     free_xml(metadata);
 223     return md;
 224 
 225 err:
 226     crm_warn("Unable to update metadata for %s (%s%s%s:%s): %s",
 227              rsc->id, rsc->standard, ((rsc->provider == NULL)? "" : ":"),
 228              pcmk__s(rsc->provider, ""), rsc->type, reason);
 229     free(key);
 230     free_xml(metadata);
 231     metadata_free(md);
 232     return NULL;
 233 }
 234 
 235 /*!
 236  * \internal
 237  * \brief Get meta-data for a resource
 238  *
 239  * \param[in,out] lrm_state  Use meta-data cache from this executor connection
 240  * \param[in]     rsc        Resource to get meta-data for
 241  * \param[in]     source     Allowed meta-data sources (bitmask of
 242  *                           enum controld_metadata_source_e values)
 243  *
 244  * \return Meta-data cache entry for given resource, or NULL if not available
 245  */
 246 struct ra_metadata_s *
 247 controld_get_rsc_metadata(lrm_state_t *lrm_state, const lrmd_rsc_info_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 248                           uint32_t source)
 249 {
 250     struct ra_metadata_s *metadata = NULL;
 251     char *metadata_str = NULL;
 252     char *key = NULL;
 253     int rc = pcmk_ok;
 254 
 255     CRM_CHECK((lrm_state != NULL) && (rsc != NULL), return NULL);
 256 
 257     if (pcmk_is_set(source, controld_metadata_from_cache)) {
 258         key = crm_generate_ra_key(rsc->standard, rsc->provider, rsc->type);
 259         if (key != NULL) {
 260             metadata = g_hash_table_lookup(lrm_state->metadata_cache, key);
 261             free(key);
 262         }
 263         if (metadata != NULL) {
 264             crm_debug("Retrieved metadata for %s (%s%s%s:%s) from cache",
 265                       rsc->id, rsc->standard,
 266                       ((rsc->provider == NULL)? "" : ":"),
 267                       ((rsc->provider == NULL)? "" : rsc->provider),
 268                       rsc->type);
 269             return metadata;
 270         }
 271     }
 272 
 273     if (!pcmk_is_set(source, controld_metadata_from_agent)) {
 274         return NULL;
 275     }
 276 
 277     /* For most actions, metadata was cached asynchronously before action
 278      * execution (via metadata_complete()).
 279      *
 280      * However if that failed, and for other actions, retrieve the metadata now
 281      * via a local, synchronous, direct execution of the agent.
 282      *
 283      * This has multiple issues, which is why this is just a fallback: the
 284      * executor should execute agents, not the controller; metadata for
 285      * Pacemaker Remote nodes should be collected on those nodes, not locally;
 286      * the metadata call shouldn't eat into the timeout of the real action being
 287      * performed; and the synchronous call blocks the controller (which also
 288      * means that if the metadata action tries to contact the controller,
 289      * everything will hang until the timeout).
 290      */
 291     crm_debug("Retrieving metadata for %s (%s%s%s:%s) synchronously",
 292               rsc->id, rsc->standard,
 293               ((rsc->provider == NULL)? "" : ":"),
 294               ((rsc->provider == NULL)? "" : rsc->provider),
 295               rsc->type);
 296     rc = lrm_state_get_metadata(lrm_state, rsc->standard, rsc->provider,
 297                                 rsc->type, &metadata_str, 0);
 298     if (rc != pcmk_ok) {
 299         crm_warn("Failed to get metadata for %s (%s%s%s:%s): %s",
 300                  rsc->id, rsc->standard,
 301                  ((rsc->provider == NULL)? "" : ":"),
 302                  ((rsc->provider == NULL)? "" : rsc->provider),
 303                  rsc->type, pcmk_strerror(rc));
 304         return NULL;
 305     }
 306 
 307     metadata = controld_cache_metadata(lrm_state->metadata_cache, rsc,
 308                                        metadata_str);
 309     free(metadata_str);
 310     return metadata;
 311 }

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