LCOV - code coverage report
Current view: top level - common - options_display.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 189 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 9 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 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 Lesser General Public License
       7             :  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
       8             :  */
       9             : 
      10             : #include <crm_internal.h>
      11             : 
      12             : #include <glib.h>   // GSList, GString
      13             : 
      14             : #include "crmcommon_private.h"
      15             : 
      16             : /*!
      17             :  * \internal
      18             :  * \brief Output an option's possible values
      19             :  *
      20             :  * \param[in,out] out     Output object
      21             :  * \param[in]     option  Option whose possible values to add
      22             :  */
      23             : static void
      24           0 : add_possible_values_default(pcmk__output_t *out,
      25             :                             const pcmk__cluster_option_t *option)
      26             : {
      27           0 :     const char *id = _("Possible values");
      28           0 :     GString *buf = g_string_sized_new(256);
      29             : 
      30           0 :     CRM_ASSERT(option->type != NULL);
      31             : 
      32           0 :     if (pcmk_is_set(option->flags, pcmk__opt_generated)) {
      33           0 :         id = _("Possible values (generated by Pacemaker)");
      34             :     }
      35             : 
      36           0 :     if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) {
      37           0 :         const char *delim = ", ";
      38           0 :         bool found_default = (option->default_value == NULL);
      39           0 :         char *str = pcmk__str_copy(option->values);
      40             : 
      41           0 :         for (const char *value = strtok(str, delim); value != NULL;
      42           0 :              value = strtok(NULL, delim)) {
      43             : 
      44           0 :             if (buf->len > 0) {
      45             :                 g_string_append(buf, delim);
      46             :             }
      47             :             g_string_append_c(buf, '"');
      48             :             g_string_append(buf, value);
      49             :             g_string_append_c(buf, '"');
      50             : 
      51           0 :             if (!found_default && (strcmp(value, option->default_value) == 0)) {
      52           0 :                 found_default = true;
      53           0 :                 g_string_append(buf, _(" (default)"));
      54             :             }
      55             :         }
      56           0 :         free(str);
      57             : 
      58           0 :     } else if (option->default_value != NULL) {
      59           0 :         pcmk__g_strcat(buf,
      60           0 :                        option->type, _(" (default: \""), option->default_value,
      61             :                        "\")", NULL);
      62             : 
      63             :     } else {
      64           0 :         pcmk__g_strcat(buf, option->type, _(" (no default)"), NULL);
      65             :     }
      66             : 
      67           0 :     out->list_item(out, id, "%s", buf->str);
      68           0 :     g_string_free(buf, TRUE);
      69           0 : }
      70             : 
      71             : /*!
      72             :  * \internal
      73             :  * \brief Output a single option's metadata
      74             :  *
      75             :  * \param[in,out] out     Output object
      76             :  * \param[in]     option  Option to add
      77             :  */
      78             : static void
      79           0 : add_option_metadata_default(pcmk__output_t *out,
      80             :                             const pcmk__cluster_option_t *option)
      81             : {
      82           0 :     const char *desc_short = option->description_short;
      83           0 :     const char *desc_long = option->description_long;
      84             : 
      85           0 :     CRM_ASSERT((desc_short != NULL) || (desc_long != NULL));
      86             : 
      87           0 :     if (desc_short == NULL) {
      88           0 :         desc_short = desc_long;
      89           0 :         desc_long = NULL;
      90             :     }
      91             : 
      92           0 :     out->list_item(out, option->name, "%s", _(desc_short));
      93             : 
      94           0 :     out->begin_list(out, NULL, NULL, NULL);
      95             : 
      96           0 :     if (desc_long != NULL) {
      97           0 :         out->list_item(out, NULL, "%s", _(desc_long));
      98             :     }
      99           0 :     add_possible_values_default(out, option);
     100           0 :     out->end_list(out);
     101           0 : }
     102             : 
     103             : /*!
     104             :  * \internal
     105             :  * \brief Output the metadata for a list of options
     106             :  *
     107             :  * \param[in,out] out   Output object
     108             :  * \param[in]     args  Message-specific arguments
     109             :  *
     110             :  * \return Standard Pacemaker return code
     111             :  *
     112             :  * \note \p args should contain the following:
     113             :  *       -# Fake resource agent name for the option list (ignored)
     114             :  *       -# Short description of option list
     115             :  *       -# Long description of option list
     116             :  *       -# Filter: Group of <tt>enum pcmk__opt_flags</tt>; output an option
     117             :  *          only if its \c flags member has all these flags set
     118             :  *       -# <tt>NULL</tt>-terminated list of options whose metadata to format
     119             :  *       -# All: If \c true, output all options; otherwise, exclude advanced and
     120             :  *          deprecated options unless \c pcmk__opt_advanced and
     121             :  *          \c pcmk__opt_deprecated flags (respectively) are set in the filter.
     122             :  */
     123             : PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *",
     124             :                   "uint32_t", "const pcmk__cluster_option_t *", "bool")
     125             : static int
     126           0 : option_list_default(pcmk__output_t *out, va_list args)
     127             : {
     128           0 :     const char *name G_GNUC_UNUSED = va_arg(args, const char *);
     129           0 :     const char *desc_short = va_arg(args, const char *);
     130           0 :     const char *desc_long = va_arg(args, const char *);
     131           0 :     const uint32_t filter = va_arg(args, uint32_t);
     132           0 :     const pcmk__cluster_option_t *option_list =
     133             :         va_arg(args, pcmk__cluster_option_t *);
     134           0 :     const bool all = (bool) va_arg(args, int);
     135             : 
     136           0 :     const bool show_deprecated = all
     137           0 :                                  || pcmk_is_set(filter, pcmk__opt_deprecated);
     138           0 :     const bool show_advanced = all || pcmk_is_set(filter, pcmk__opt_advanced);
     139           0 :     bool old_fancy = false;
     140             : 
     141           0 :     GSList *deprecated = NULL;
     142           0 :     GSList *advanced = NULL;
     143             : 
     144           0 :     CRM_ASSERT((out != NULL) && (desc_short != NULL) && (desc_long != NULL)
     145             :                && (option_list != NULL));
     146             : 
     147           0 :     old_fancy = pcmk__output_text_get_fancy(out);
     148           0 :     pcmk__output_text_set_fancy(out, true);
     149             : 
     150           0 :     out->info(out, "%s", _(desc_short));
     151           0 :     out->spacer(out);
     152           0 :     out->info(out, "%s", _(desc_long));
     153           0 :     out->begin_list(out, NULL, NULL, NULL);
     154             : 
     155           0 :     for (const pcmk__cluster_option_t *option = option_list;
     156           0 :          option->name != NULL; option++) {
     157             : 
     158             :         // Store deprecated and advanced options to display later if appropriate
     159           0 :         if (pcmk_all_flags_set(option->flags, filter)) {
     160           0 :             if (pcmk_is_set(option->flags, pcmk__opt_deprecated)) {
     161           0 :                 if (show_deprecated) {
     162           0 :                     deprecated = g_slist_prepend(deprecated, (gpointer) option);
     163             :                 }
     164             : 
     165           0 :             } else if (pcmk_is_set(option->flags, pcmk__opt_advanced)) {
     166           0 :                 if (show_advanced) {
     167           0 :                     advanced = g_slist_prepend(advanced, (gpointer) option);
     168             :                 }
     169             : 
     170             :             } else {
     171           0 :                 out->spacer(out);
     172           0 :                 add_option_metadata_default(out, option);
     173             :             }
     174             :         }
     175             :     }
     176             : 
     177           0 :     if (advanced != NULL) {
     178           0 :         advanced = g_slist_reverse(advanced);
     179             : 
     180           0 :         out->spacer(out);
     181           0 :         out->begin_list(out, NULL, NULL, _("ADVANCED OPTIONS"));
     182           0 :         for (const GSList *iter = advanced; iter != NULL; iter = iter->next) {
     183           0 :             const pcmk__cluster_option_t *option = iter->data;
     184             : 
     185           0 :             out->spacer(out);
     186           0 :             add_option_metadata_default(out, option);
     187             :         }
     188           0 :         out->end_list(out);
     189           0 :         g_slist_free(advanced);
     190             :     }
     191             : 
     192           0 :     if (deprecated != NULL) {
     193           0 :         deprecated = g_slist_reverse(deprecated);
     194             : 
     195           0 :         out->spacer(out);
     196           0 :         out->begin_list(out, NULL, NULL,
     197             :                         _("DEPRECATED OPTIONS (will be removed in a future "
     198             :                           "release)"));
     199           0 :         for (const GSList *iter = deprecated; iter != NULL; iter = iter->next) {
     200           0 :             const pcmk__cluster_option_t *option = iter->data;
     201             : 
     202           0 :             out->spacer(out);
     203           0 :             add_option_metadata_default(out, option);
     204             :         }
     205           0 :         out->end_list(out);
     206           0 :         g_slist_free(deprecated);
     207             :     }
     208             : 
     209           0 :     out->end_list(out);
     210           0 :     pcmk__output_text_set_fancy(out, old_fancy);
     211           0 :     return pcmk_rc_ok;
     212             : }
     213             : 
     214             : /*!
     215             :  * \internal
     216             :  * \brief Add a description element to an OCF-like metadata XML node
     217             :  *
     218             :  * Include a translation based on the current locale if \c ENABLE_NLS is
     219             :  * defined.
     220             :  *
     221             :  * \param[in,out] out       Output object
     222             :  * \param[in]     for_long  If \c true, add long description; otherwise, add
     223             :  *                          short description
     224             :  * \param[in]     desc      Textual description to add
     225             :  */
     226             : static void
     227           0 : add_desc_xml(pcmk__output_t *out, bool for_long, const char *desc)
     228             : {
     229           0 :     const char *tag = (for_long? PCMK_XE_LONGDESC : PCMK_XE_SHORTDESC);
     230           0 :     xmlNode *node = pcmk__output_create_xml_text_node(out, tag, desc);
     231             : 
     232           0 :     crm_xml_add(node, PCMK_XA_LANG, PCMK__VALUE_EN);
     233             : 
     234             : #ifdef ENABLE_NLS
     235             :     {
     236             :         static const char *locale = NULL;
     237             : 
     238             :         if (strcmp(desc, _(desc)) == 0) {
     239             :             return;
     240             :         }
     241             : 
     242             :         if (locale == NULL) {
     243             :             locale = strtok(setlocale(LC_ALL, NULL), "_");
     244             :         }
     245             :         node = pcmk__output_create_xml_text_node(out, tag, _(desc));
     246             :         crm_xml_add(node, PCMK_XA_LANG, locale);
     247             :     }
     248             : #endif
     249           0 : }
     250             : 
     251             : /*!
     252             :  * \internal
     253             :  * \brief Output an option's possible values
     254             :  *
     255             :  * Add a \c PCMK_XE_OPTION element for each of the option's possible values.
     256             :  *
     257             :  * \param[in,out] out     Output object
     258             :  * \param[in]     option  Option whose possible values to add
     259             :  */
     260             : static void
     261           0 : add_possible_values_xml(pcmk__output_t *out,
     262             :                         const pcmk__cluster_option_t *option)
     263             : {
     264           0 :     if ((option->values != NULL) && (strcmp(option->type, "select") == 0)) {
     265           0 :         const char *delim = ", ";
     266           0 :         char *str = pcmk__str_copy(option->values);
     267           0 :         const char *ptr = strtok(str, delim);
     268             : 
     269           0 :         while (ptr != NULL) {
     270           0 :             pcmk__output_create_xml_node(out, PCMK_XE_OPTION,
     271             :                                          PCMK_XA_VALUE, ptr,
     272             :                                          NULL);
     273           0 :             ptr = strtok(NULL, delim);
     274             :         }
     275           0 :         free(str);
     276             :     }
     277           0 : }
     278             : 
     279             : /*!
     280             :  * \internal
     281             :  * \brief Map an option type to one suitable for daemon metadata
     282             :  *
     283             :  * \param[in] type  Option type to map
     284             :  *
     285             :  * \return String suitable for daemon metadata to display as an option type
     286             :  */
     287             : static const char *
     288           0 : map_legacy_option_type(const char *type)
     289             : {
     290             :     // @COMPAT Drop this function when we drop daemon metadata commands
     291           0 :     if (pcmk__str_any_of(type, PCMK_VALUE_DURATION, PCMK_VALUE_TIMEOUT, NULL)) {
     292           0 :         return PCMK__VALUE_TIME;
     293             : 
     294           0 :     } else if (pcmk__str_any_of(type,
     295             :                                 PCMK_VALUE_NONNEGATIVE_INTEGER,
     296             :                                 PCMK_VALUE_SCORE, NULL)) {
     297           0 :         return PCMK_VALUE_INTEGER;
     298             : 
     299           0 :     } else if (pcmk__str_eq(type, PCMK_VALUE_VERSION, pcmk__str_none)) {
     300           0 :         return PCMK_VALUE_STRING;
     301             : 
     302             :     } else {
     303           0 :         return type;
     304             :     }
     305             : }
     306             : 
     307             : /*!
     308             :  * \internal
     309             :  * \brief Add a \c PCMK_XE_PARAMETER element to an OCF-like metadata XML node
     310             :  *
     311             :  * \param[in,out] out     Output object
     312             :  * \param[in]     option  Option to add as a \c PCMK_XE_PARAMETER element
     313             :  */
     314             : static void
     315           0 : add_option_metadata_xml(pcmk__output_t *out,
     316             :                         const pcmk__cluster_option_t *option)
     317             : {
     318           0 :     const char *type = option->type;
     319           0 :     const char *desc_long = option->description_long;
     320           0 :     const char *desc_short = option->description_short;
     321           0 :     const bool advanced = pcmk_is_set(option->flags, pcmk__opt_advanced);
     322           0 :     const bool deprecated = pcmk_is_set(option->flags, pcmk__opt_deprecated);
     323           0 :     const bool generated = pcmk_is_set(option->flags, pcmk__opt_generated);
     324             : 
     325             :     // OCF requires "1"/"0" and does not allow "true"/"false
     326             :     // @COMPAT Variables no longer needed after we drop legacy mode
     327           0 :     const char *advanced_s = advanced? "1" : "0";
     328           0 :     const char *generated_s = generated? "1" : "0";
     329             : 
     330             :     // @COMPAT For daemon metadata only; drop when daemon metadata is dropped
     331           0 :     const bool legacy = pcmk__output_get_legacy_xml(out);
     332           0 :     char *desc_long_legacy = NULL;
     333           0 :     GString *desc_short_legacy = NULL;
     334             : 
     335             :     // The standard requires a parameter type
     336           0 :     CRM_ASSERT(type != NULL);
     337             : 
     338             :     // The standard requires long and short parameter descriptions
     339           0 :     CRM_ASSERT((desc_long != NULL) || (desc_short != NULL));
     340             : 
     341           0 :     if (desc_long == NULL) {
     342           0 :         desc_long = desc_short;
     343           0 :     } else if (desc_short == NULL) {
     344           0 :         desc_short = desc_long;
     345             :     }
     346             : 
     347           0 :     if (legacy) {
     348             :         // This is ugly but it will go away at a major release bump
     349           0 :         type = map_legacy_option_type(type);
     350             : 
     351           0 :         if (option->values != NULL) {
     352           0 :             desc_long_legacy = crm_strdup_printf("%s  Allowed values: %s",
     353           0 :                                                  desc_long, option->values);
     354           0 :             desc_long = desc_long_legacy;
     355             :         }
     356             : 
     357           0 :         if (deprecated || advanced) {
     358           0 :             const size_t init_sz = 1023;
     359             : 
     360           0 :             if (desc_long != option->description_long) {
     361             :                 /* desc_long was NULL and got assigned desc_short, which was
     362             :                  * non-empty. Let desc_long have the "real" description, and put
     363             :                  * the flag in desc_short.
     364             :                  */
     365           0 :                 desc_short = "";
     366             :             } else {
     367           0 :                 desc_short = pcmk__s(option->description_short, "");
     368             :             }
     369             : 
     370           0 :             if (deprecated) {
     371           0 :                 pcmk__add_separated_word(&desc_short_legacy, init_sz,
     372             :                                          "*** Deprecated ***", NULL);
     373             :             }
     374           0 :             if (advanced) {
     375           0 :                 pcmk__add_separated_word(&desc_short_legacy, init_sz,
     376             :                                          "*** Advanced Use Only ***", NULL);
     377             :             }
     378           0 :             pcmk__add_separated_word(&desc_short_legacy, 0, desc_short, NULL);
     379             : 
     380           0 :             desc_short = desc_short_legacy->str;
     381             :         }
     382             : 
     383             :         /* These must be NULL when used as attribute values later.
     384             :          * PCMK_XA_ADVANCED and PCMK_XA_GENERATED break validation for some
     385             :          * legacy tools.
     386             :          */
     387           0 :         advanced_s = NULL;
     388           0 :         generated_s = NULL;
     389             :     }
     390             : 
     391           0 :     pcmk__output_xml_create_parent(out, PCMK_XE_PARAMETER,
     392           0 :                                    PCMK_XA_NAME, option->name,
     393             :                                    PCMK_XA_ADVANCED, advanced_s,
     394             :                                    PCMK_XA_GENERATED, generated_s,
     395             :                                    NULL);
     396             : 
     397           0 :     if (deprecated && !legacy) {
     398             :         // No need yet to support "replaced-with" or "desc"; add if needed
     399           0 :         pcmk__output_create_xml_node(out, PCMK_XE_DEPRECATED, NULL);
     400             :     }
     401           0 :     add_desc_xml(out, true, desc_long);
     402           0 :     add_desc_xml(out, false, desc_short);
     403             : 
     404           0 :     pcmk__output_xml_create_parent(out, PCMK_XE_CONTENT,
     405             :                                    PCMK_XA_TYPE, type,
     406           0 :                                    PCMK_XA_DEFAULT, option->default_value,
     407             :                                    NULL);
     408             : 
     409           0 :     add_possible_values_xml(out, option);
     410             : 
     411           0 :     pcmk__output_xml_pop_parent(out);
     412           0 :     pcmk__output_xml_pop_parent(out);
     413             : 
     414           0 :     free(desc_long_legacy);
     415           0 :     if (desc_short_legacy != NULL) {
     416           0 :         g_string_free(desc_short_legacy, TRUE);
     417             :     }
     418           0 : }
     419             : 
     420             : /*!
     421             :  * \internal
     422             :  * \brief Output the metadata for a list of options as OCF-like XML
     423             :  *
     424             :  * \param[in,out] out   Output object
     425             :  * \param[in]     args  Message-specific arguments
     426             :  *
     427             :  * \return Standard Pacemaker return code
     428             :  *
     429             :  * \note \p args should contain the following:
     430             :  *       -# Fake resource agent name for the option list
     431             :  *       -# Short description of option list
     432             :  *       -# Long description of option list
     433             :  *       -# Filter: Group of <tt>enum pcmk__opt_flags</tt>; output an option
     434             :  *          only if its \c flags member has all these flags set
     435             :  *       -# <tt>NULL</tt>-terminated list of options whose metadata to format
     436             :  *       -# Whether to output all options (ignored, treated as \c true)
     437             :  */
     438             : PCMK__OUTPUT_ARGS("option-list", "const char *", "const char *", "const char *",
     439             :                   "uint32_t", "const pcmk__cluster_option_t *", "bool")
     440             : static int
     441           0 : option_list_xml(pcmk__output_t *out, va_list args)
     442             : {
     443           0 :     const char *name = va_arg(args, const char *);
     444           0 :     const char *desc_short = va_arg(args, const char *);
     445           0 :     const char *desc_long = va_arg(args, const char *);
     446           0 :     const uint32_t filter = va_arg(args, uint32_t);
     447           0 :     const pcmk__cluster_option_t *option_list =
     448             :         va_arg(args, pcmk__cluster_option_t *);
     449             : 
     450           0 :     CRM_ASSERT((out != NULL) && (name != NULL) && (desc_short != NULL)
     451             :                && (desc_long != NULL) && (option_list != NULL));
     452             : 
     453           0 :     pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT,
     454             :                                    PCMK_XA_NAME, name,
     455             :                                    PCMK_XA_VERSION, PACEMAKER_VERSION,
     456             :                                    NULL);
     457             : 
     458           0 :     pcmk__output_create_xml_text_node(out, PCMK_XE_VERSION, PCMK_OCF_VERSION);
     459           0 :     add_desc_xml(out, true, desc_long);
     460           0 :     add_desc_xml(out, false, desc_short);
     461             : 
     462           0 :     pcmk__output_xml_create_parent(out, PCMK_XE_PARAMETERS, NULL);
     463             : 
     464           0 :     for (const pcmk__cluster_option_t *option = option_list;
     465           0 :          option->name != NULL; option++) {
     466             : 
     467           0 :         if (pcmk_all_flags_set(option->flags, filter)) {
     468           0 :             add_option_metadata_xml(out, option);
     469             :         }
     470             :     }
     471             : 
     472           0 :     pcmk__output_xml_pop_parent(out);
     473           0 :     pcmk__output_xml_pop_parent(out);
     474           0 :     return pcmk_rc_ok;
     475             : }
     476             : 
     477             : static pcmk__message_entry_t fmt_functions[] = {
     478             :     { "option-list", "default", option_list_default },
     479             :     { "option-list", "xml", option_list_xml },
     480             : 
     481             :     { NULL, NULL, NULL }
     482             : };
     483             : 
     484             : /*!
     485             :  * \internal
     486             :  * \brief Register the formatting functions for option lists
     487             :  *
     488             :  * \param[in,out] out  Output object
     489             :  */
     490             : void
     491           0 : pcmk__register_option_messages(pcmk__output_t *out) {
     492           0 :     pcmk__register_messages(out, fmt_functions);
     493           0 : }

Generated by: LCOV version 1.14