diff --git a/cts/scheduler/exp/nvpair-id-ref.exp b/cts/scheduler/exp/nvpair-id-ref.exp index 27286c6ce6..f2043204aa 100644 --- a/cts/scheduler/exp/nvpair-id-ref.exp +++ b/cts/scheduler/exp/nvpair-id-ref.exp @@ -1,96 +1,96 @@ - + - + - + - + diff --git a/cts/scheduler/xml/nvpair-id-ref.xml b/cts/scheduler/xml/nvpair-id-ref.xml index 4f865f3062..8af026f597 100644 --- a/cts/scheduler/xml/nvpair-id-ref.xml +++ b/cts/scheduler/xml/nvpair-id-ref.xml @@ -1,56 +1,55 @@ - diff --git a/cts/schemas/test-3/ref.err/id-ref.ref.err-1 b/cts/schemas/test-3/ref.err/id-ref.ref.err-1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/no-validate-with.ref.err-0 b/cts/schemas/test-3/ref.err/no-validate-with.ref.err-0 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/no-validate-with.ref.err-1 b/cts/schemas/test-3/ref.err/no-validate-with.ref.err-1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/no-validate-with.ref.err-99 b/cts/schemas/test-3/ref.err/no-validate-with.ref.err-99 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-0 b/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-0 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-1 b/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-99 b/cts/schemas/test-3/ref.err/nvpair-no-value.ref.err-99 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-0 b/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-0 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-1 b/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-1 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-99 b/cts/schemas/test-3/ref.err/sort-nvsets.ref.err-99 new file mode 100644 index 0000000000..e69de29bb2 diff --git a/cts/schemas/test-3/ref/id-ref.ref-99 b/cts/schemas/test-3/ref/id-ref.ref-1 similarity index 65% copy from cts/schemas/test-3/ref/id-ref.ref-99 copy to cts/schemas/test-3/ref/id-ref.ref-1 index d8a2d3975a..6a43d728dd 100644 --- a/cts/schemas/test-3/ref/id-ref.ref-99 +++ b/cts/schemas/test-3/ref/id-ref.ref-1 @@ -1,66 +1,72 @@ - - + + - - - - - + + + + + + + + + + + + - - - - - - - - + + + + + + + - - - + + + - + - + diff --git a/cts/schemas/test-3/ref/id-ref.ref-99 b/cts/schemas/test-3/ref/id-ref.ref-99 index d8a2d3975a..cb4ea469b4 100644 --- a/cts/schemas/test-3/ref/id-ref.ref-99 +++ b/cts/schemas/test-3/ref/id-ref.ref-99 @@ -1,66 +1,66 @@ - + diff --git a/cts/schemas/test-3/ref/no-validate-with.ref-0 b/cts/schemas/test-3/ref/no-validate-with.ref-0 new file mode 100644 index 0000000000..37a26ffb89 --- /dev/null +++ b/cts/schemas/test-3/ref/no-validate-with.ref-0 @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/no-validate-with.ref-1 b/cts/schemas/test-3/ref/no-validate-with.ref-1 new file mode 100644 index 0000000000..392eb408f3 --- /dev/null +++ b/cts/schemas/test-3/ref/no-validate-with.ref-1 @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/no-validate-with.ref-99 b/cts/schemas/test-3/ref/no-validate-with.ref-99 new file mode 100644 index 0000000000..89ed5f8b11 --- /dev/null +++ b/cts/schemas/test-3/ref/no-validate-with.ref-99 @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/nvpair-no-value.ref-0 b/cts/schemas/test-3/ref/nvpair-no-value.ref-0 new file mode 100644 index 0000000000..b5076aaac5 --- /dev/null +++ b/cts/schemas/test-3/ref/nvpair-no-value.ref-0 @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/nvpair-no-value.ref-1 b/cts/schemas/test-3/ref/nvpair-no-value.ref-1 new file mode 100644 index 0000000000..5820a8429d --- /dev/null +++ b/cts/schemas/test-3/ref/nvpair-no-value.ref-1 @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/nvpair-no-value.ref-99 b/cts/schemas/test-3/ref/nvpair-no-value.ref-99 new file mode 100644 index 0000000000..a8e8d240b9 --- /dev/null +++ b/cts/schemas/test-3/ref/nvpair-no-value.ref-99 @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/sort-nvsets.ref-0 b/cts/schemas/test-3/ref/sort-nvsets.ref-0 new file mode 100644 index 0000000000..e3c3a26942 --- /dev/null +++ b/cts/schemas/test-3/ref/sort-nvsets.ref-0 @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/sort-nvsets.ref-1 b/cts/schemas/test-3/ref/sort-nvsets.ref-1 new file mode 100644 index 0000000000..38cfc6e4cc --- /dev/null +++ b/cts/schemas/test-3/ref/sort-nvsets.ref-1 @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/ref/sort-nvsets.ref-99 b/cts/schemas/test-3/ref/sort-nvsets.ref-99 new file mode 100644 index 0000000000..26d18812f7 --- /dev/null +++ b/cts/schemas/test-3/ref/sort-nvsets.ref-99 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/xml/no-validate-with.xml b/cts/schemas/test-3/xml/no-validate-with.xml new file mode 100644 index 0000000000..ba238aaf92 --- /dev/null +++ b/cts/schemas/test-3/xml/no-validate-with.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/cts/schemas/test-3/xml/nvpair-no-value.xml b/cts/schemas/test-3/xml/nvpair-no-value.xml new file mode 100644 index 0000000000..54b8793df0 --- /dev/null +++ b/cts/schemas/test-3/xml/nvpair-no-value.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cts/schemas/test-3/xml/sort-nvsets.xml b/cts/schemas/test-3/xml/sort-nvsets.xml new file mode 100644 index 0000000000..1b070d484a --- /dev/null +++ b/cts/schemas/test-3/xml/sort-nvsets.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pengine/rules.c b/lib/pengine/rules.c index 1e0436406a..374b43c9a8 100644 --- a/lib/pengine/rules.c +++ b/lib/pengine/rules.c @@ -1,319 +1,320 @@ /* * Copyright 2004-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU Lesser General Public License * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include CRM_TRACE_INIT_DATA(pe_rules); /*! * \internal * \brief Map pe_rule_eval_data_t to pcmk_rule_input_t * * \param[out] new New data struct * \param[in] old Old data struct */ static void map_rule_input(pcmk_rule_input_t *new, const pe_rule_eval_data_t *old) { if (old == NULL) { return; } new->now = old->now; new->node_attrs = old->node_hash; if (old->rsc_data != NULL) { new->rsc_standard = old->rsc_data->standard; new->rsc_provider = old->rsc_data->provider; new->rsc_agent = old->rsc_data->agent; } if (old->match_data != NULL) { new->rsc_params = old->match_data->params; new->rsc_meta = old->match_data->meta; if (old->match_data->re != NULL) { new->rsc_id = old->match_data->re->string; new->rsc_id_submatches = old->match_data->re->pmatch; new->rsc_id_nmatches = old->match_data->re->nregs; } } if (old->op_data != NULL) { new->op_name = old->op_data->op_name; new->op_interval_ms = old->op_data->interval; } } static gint sort_pairs(gconstpointer a, gconstpointer b, gpointer user_data) { const xmlNode *pair_a = a; const xmlNode *pair_b = b; pcmk__nvpair_unpack_t *unpack_data = user_data; const char *score = NULL; int score_a = 0; int score_b = 0; if (a == NULL && b == NULL) { return 0; } else if (a == NULL) { return 1; } else if (b == NULL) { return -1; } if (pcmk__str_eq(pcmk__xe_id(pair_a), unpack_data->first_id, pcmk__str_none)) { return -1; } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id, pcmk__str_none)) { return 1; } score = crm_element_value(pair_a, PCMK_XA_SCORE); score_a = char2score(score); score = crm_element_value(pair_b, PCMK_XA_SCORE); score_b = char2score(score); /* If we're overwriting values, we want lowest score first, so the highest * score is processed last; if we're not overwriting values, we want highest * score first, so nothing else overwrites it. */ if (score_a < score_b) { return unpack_data->overwrite? -1 : 1; } else if (score_a > score_b) { return unpack_data->overwrite? 1 : -1; } return 0; } static void populate_hash(xmlNode *nvpair_list, GHashTable *hash, bool overwrite) { if (pcmk__xe_is(nvpair_list->children, PCMK__XE_ATTRIBUTES)) { nvpair_list = nvpair_list->children; } for (xmlNode *nvpair = pcmk__xe_first_child(nvpair_list, PCMK_XE_NVPAIR, NULL, NULL); nvpair != NULL; nvpair = pcmk__xe_next_same(nvpair)) { xmlNode *ref_nvpair = pcmk__xe_resolve_idref(nvpair, NULL); const char *name = NULL; const char *value = NULL; const char *old_value = NULL; if (ref_nvpair == NULL) { /* Not possible with schema validation enabled (error already * logged) */ continue; } name = crm_element_value(nvpair, PCMK_XA_NAME); if (name == NULL) { - /* @TODO Always get name from ref_nvpair. Currently an nvpair with + /* @COMPAT Always get name from ref_nvpair. Currently an nvpair with * an id-ref is allowed to have a name, which overrides the name in * the referenced nvpair. * * This feature was added by commit 3912538 but is undocumented and * inconsistently implemented. The code often ignores name if there * is an id-ref, or in some places assumes that id and value exist * if name exists. * - * Disallow this in the schema first, and then update this function. + * PCMK_XA_NAME with PCMK_XA_ID_REF is already disallowed by the + * schema for PCMK_XE_NVPAIR. */ name = crm_element_value(ref_nvpair, PCMK_XA_NAME); } value = crm_element_value(ref_nvpair, PCMK_XA_VALUE); if ((name == NULL) || (value == NULL)) { continue; } old_value = g_hash_table_lookup(hash, name); if (pcmk__str_eq(value, "#default", pcmk__str_casei)) { // @COMPAT Deprecated since 2.1.8 pcmk__config_warn("Support for setting meta-attributes (such as " "%s) to the explicit value '#default' is " "deprecated and will be removed in a future " "release", name); if (old_value != NULL) { crm_trace("Letting %s default (removing explicit value \"%s\")", name, value); g_hash_table_remove(hash, name); } } else if (old_value == NULL) { crm_trace("Setting %s=\"%s\"", name, value); pcmk__insert_dup(hash, name, value); } else if (overwrite) { crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")", name, value, old_value); pcmk__insert_dup(hash, name, value); } } } static void unpack_attr_set(gpointer data, gpointer user_data) { xmlNode *pair = data; pcmk__nvpair_unpack_t *unpack_data = user_data; xmlNode *rule_xml = pcmk__xe_first_child(pair, PCMK_XE_RULE, NULL, NULL); if ((rule_xml != NULL) && (pcmk_evaluate_rule(rule_xml, &(unpack_data->rule_input), unpack_data->next_change) != pcmk_rc_ok)) { return; } crm_trace("Adding name/value pairs from %s %s overwrite", pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without")); populate_hash(pair, unpack_data->values, unpack_data->overwrite); } /*! * \internal * \brief Create a sorted list of nvpair blocks * * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name If not NULL, only get blocks of this element * * \return List of XML blocks of name/value pairs */ static GList * make_pairs(const xmlNode *xml_obj, const char *set_name) { GList *unsorted = NULL; if (xml_obj == NULL) { return NULL; } for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL); attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) { if ((set_name == NULL) || pcmk__xe_is(attr_set, set_name)) { xmlNode *expanded_attr_set = pcmk__xe_resolve_idref(attr_set, NULL); if (expanded_attr_set == NULL) { continue; // Not possible with schema validation enabled } unsorted = g_list_prepend(unsorted, expanded_attr_set); } } return unsorted; } /*! * \brief Extract nvpair blocks contained by an XML element into a hash table * * \param[in,out] top Ignored * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name If not NULL, only use blocks of this element * \param[in] rule_data Matching parameters to use when unpacking * \param[out] hash Where to store extracted name/value pairs * \param[in] always_first If not NULL, process block with this ID first * \param[in] overwrite Whether to replace existing values with same * name (all internal callers pass \c FALSE) * \param[out] next_change If not NULL, set to when evaluation will change */ void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, const pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change) { GList *pairs = make_pairs(xml_obj, set_name); if (pairs) { pcmk__nvpair_unpack_t data = { .values = hash, .first_id = always_first, .overwrite = overwrite, .next_change = next_change, }; map_rule_input(&(data.rule_input), rule_data); pairs = g_list_sort_with_data(pairs, sort_pairs, &data); g_list_foreach(pairs, unpack_attr_set, &data); g_list_free(pairs); } } /*! * \brief Extract nvpair blocks contained by an XML element into a hash table * * \param[in,out] top Ignored * \param[in] xml_obj XML element containing blocks of nvpair elements * \param[in] set_name Element name to identify nvpair blocks * \param[in] node_hash Node attributes to use when evaluating rules * \param[out] hash Where to store extracted name/value pairs * \param[in] always_first If not NULL, process block with this ID first * \param[in] overwrite Whether to replace existing values with same * name (all internal callers pass \c FALSE) * \param[in] now Time to use when evaluating rules * \param[out] next_change If not NULL, set to when evaluation will change */ void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change) { pe_rule_eval_data_t rule_data = { .node_hash = node_hash, .now = now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash, always_first, overwrite, next_change); } // Deprecated functions kept only for backward API compatibility // LCOV_EXCL_START #include gboolean test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now) { pcmk_rule_input_t rule_input = { .node_attrs = node_hash, .now = now, }; return pcmk_evaluate_rule(rule, &rule_input, NULL) == pcmk_rc_ok; } // LCOV_EXCL_STOP // End deprecated API diff --git a/xml/nvset-4.0.rng b/xml/nvset-4.0.rng index d259c78496..0a12a10a4f 100644 --- a/xml/nvset-4.0.rng +++ b/xml/nvset-4.0.rng @@ -1,68 +1,61 @@ - - - + - - - - - - + diff --git a/xml/upgrade-3.10-1.xsl b/xml/upgrade-3.10-1.xsl new file mode 100644 index 0000000000..22ef953146 --- /dev/null +++ b/xml/upgrade-3.10-1.xsl @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pacemaker-4.0 + + + + + + + + + + +