diff --git a/cts/cli/regression.rules.exp b/cts/cli/regression.rules.exp index a573b7770d..fff8d9e492 100644 --- a/cts/cli/regression.rules.exp +++ b/cts/cli/regression.rules.exp @@ -1,157 +1,157 @@ Created new pacemaker configuration Setting up shadow instance A new shadow instance was created. To begin using it paste the following into your shell: CIB_shadow=cts-cli ; export CIB_shadow =#=#=#= Begin test: crm_rule given no arguments =#=#=#= crm_rule: No mode operation given =#=#=#= End test: crm_rule given no arguments - Incorrect usage (64) =#=#=#= * Passed: crm_rule - crm_rule given no arguments =#=#=#= Begin test: crm_rule given no rule to check =#=#=#= crm_rule: --check requires use of --rule= =#=#=#= End test: crm_rule given no rule to check - Incorrect usage (64) =#=#=#= * Passed: crm_rule - crm_rule given no rule to check =#=#=#= Begin test: crm_rule given invalid input XML =#=#=#= (log_xmllib_err@xml.c:891) error: XML Error: Entity: line 1: parser error : Start tag expected, '<' not found (log_xmllib_err@xml.c:891) error: XML Error: invalidxml (log_xmllib_err@xml.c:891) error: XML Error: ^ crm_rule: Couldn't parse input string: invalidxml =#=#=#= End test: crm_rule given invalid input XML - Invalid data given (65) =#=#=#= * Passed: crm_rule - crm_rule given invalid input XML =#=#=#= Begin test: crm_rule given invalid input XML on stdin =#=#=#= (log_xmllib_err@xml.c:891) error: XML Error: Entity: line 1: parser error : Start tag expected, '<' not found (log_xmllib_err@xml.c:891) error: XML Error: invalidxml (log_xmllib_err@xml.c:891) error: XML Error: ^ crm_rule: Couldn't parse input from STDIN =#=#=#= End test: crm_rule given invalid input XML on stdin - Invalid data given (65) =#=#=#= * Passed: echo - crm_rule given invalid input XML on stdin =#=#=#= Begin test: Try to check a rule that doesn't exist =#=#=#= No rule found with ID=blahblah =#=#=#= Current cib after: Try to check a rule that doesn't exist =#=#=#= =#=#=#= End test: Try to check a rule that doesn't exist - No such object (105) =#=#=#= * Passed: crm_rule - Try to check a rule that doesn't exist =#=#=#= Begin test: Try to check a rule that doesn't exist, with XML output =#=#=#= No rule found with ID=blahblah =#=#=#= End test: Try to check a rule that doesn't exist, with XML output - No such object (105) =#=#=#= * Passed: crm_rule - Try to check a rule that doesn't exist, with XML output =#=#=#= Begin test: Try to check a rule that has too many date_expressions =#=#=#= Can't check rule cli-rule-too-many-date-expressions because it does not have exactly one date_expression =#=#=#= End test: Try to check a rule that has too many date_expressions - Unimplemented (3) =#=#=#= * Passed: crm_rule - Try to check a rule that has too many date_expressions =#=#=#= Begin test: Verify basic rule is expired =#=#=#= Rule cli-prefer-rule-dummy-expired is expired =#=#=#= End test: Verify basic rule is expired - Requested item has expired (110) =#=#=#= * Passed: crm_rule - Verify basic rule is expired =#=#=#= Begin test: Verify basic rule is expired, with XML output =#=#=#= =#=#=#= End test: Verify basic rule is expired, with XML output - Requested item has expired (110) =#=#=#= * Passed: crm_rule - Verify basic rule is expired, with XML output =#=#=#= Begin test: Verify basic rule worked in the past =#=#=#= Rule cli-prefer-rule-dummy-expired is still in effect =#=#=#= End test: Verify basic rule worked in the past - OK (0) =#=#=#= * Passed: crm_rule - Verify basic rule worked in the past =#=#=#= Begin test: Verify basic rule is not yet in effect =#=#=#= Rule cli-prefer-rule-dummy-not-yet has not yet taken effect =#=#=#= End test: Verify basic rule is not yet in effect - Requested item is not yet in effect (111) =#=#=#= * Passed: crm_rule - Verify basic rule is not yet in effect =#=#=#= Begin test: Verify date_spec rule with years has expired =#=#=#= Rule cli-prefer-rule-dummy-date_spec-only-years is expired =#=#=#= End test: Verify date_spec rule with years has expired - Requested item has expired (110) =#=#=#= * Passed: crm_rule - Verify date_spec rule with years has expired =#=#=#= Begin test: Verify multiple rules at once =#=#=#= Rule cli-prefer-rule-dummy-not-yet has not yet taken effect Rule cli-prefer-rule-dummy-date_spec-only-years is expired =#=#=#= End test: Verify multiple rules at once - Requested item has expired (110) =#=#=#= * Passed: crm_rule - Verify multiple rules at once =#=#=#= Begin test: Verify multiple rules at once, with XML output =#=#=#= =#=#=#= End test: Verify multiple rules at once, with XML output - Requested item has expired (110) =#=#=#= * Passed: crm_rule - Verify multiple rules at once, with XML output =#=#=#= Begin test: Verify date_spec rule with years is in effect =#=#=#= Rule cli-prefer-rule-dummy-date_spec-only-years satisfies conditions =#=#=#= End test: Verify date_spec rule with years is in effect - OK (0) =#=#=#= * Passed: crm_rule - Verify date_spec rule with years is in effect =#=#=#= Begin test: Try to check a rule whose date_spec does not contain years= =#=#=#= -Rule either must not use date_spec, or use date_spec with years= but not moon= +Rule cli-prefer-rule-dummy-date_spec-without-years must either not use date_spec, or use date_spec with years= but not moon= =#=#=#= End test: Try to check a rule whose date_spec does not contain years= - No such object (105) =#=#=#= * Passed: crm_rule - Try to check a rule whose date_spec does not contain years= =#=#=#= Begin test: Try to check a rule whose date_spec contains years= and moon= =#=#=#= -Rule either must not use date_spec, or use date_spec with years= but not moon= +Rule cli-prefer-rule-dummy-date_spec-years-moon must either not use date_spec, or use date_spec with years= but not moon= =#=#=#= End test: Try to check a rule whose date_spec contains years= and moon= - No such object (105) =#=#=#= * Passed: crm_rule - Try to check a rule whose date_spec contains years= and moon= =#=#=#= Begin test: Try to check a rule with no date_expression =#=#=#= Can't check rule cli-no-date_expression-rule because it does not have exactly one date_expression =#=#=#= End test: Try to check a rule with no date_expression - Unimplemented (3) =#=#=#= * Passed: crm_rule - Try to check a rule with no date_expression diff --git a/lib/pacemaker/pcmk_rule.c b/lib/pacemaker/pcmk_rule.c index f5045c93b7..c5da9af645 100644 --- a/lib/pacemaker/pcmk_rule.c +++ b/lib/pacemaker/pcmk_rule.c @@ -1,287 +1,285 @@ /* * Copyright 2022 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 /*! * \internal * \brief Evaluate a date expression for a specific time * * \param[in] expr date_expression XML * \param[in] now Time for which to evaluate expression * \param[out] next_change If not NULL, set to when evaluation will change * * \return Standard Pacemaker return code */ static int eval_date_expression(xmlNodePtr expr, crm_time_t *now, crm_time_t *next_change) { pe_rule_eval_data_t rule_data = { .node_hash = NULL, .role = RSC_ROLE_UNKNOWN, .now = now, .match_data = NULL, .rsc_data = NULL, .op_data = NULL }; return pe__eval_date_expr(expr, &rule_data, next_change); } /*! * \internal * \brief Initialize the cluster working set for checking rules * * Make our own copies of the CIB XML and date/time object, if they're not * \c NULL. This way we don't have to take ownership of the objects passed via * the API. * * \param[in,out] out Output object * \param[in] input The CIB XML to check (if \c NULL, use current CIB) * \param[in] date Check whether the rule is in effect at this date * and time (if \c NULL, use current date and time) * \param[out] data_set Where to store the cluster working set * * \return Standard Pacemaker return code */ static int init_rule_check(pcmk__output_t *out, xmlNodePtr input, crm_time_t *date, pe_working_set_t **data_set) { // Allows for cleaner syntax than dereferencing the data_set argument pe_working_set_t *new_data_set = NULL; new_data_set = pe_new_working_set(); if (new_data_set == NULL) { return ENOMEM; } pe__set_working_set_flags(new_data_set, pe_flag_no_counts|pe_flag_no_compat); // Populate the working set instance // Make our own copy of the given input or fetch the CIB and use that if (input != NULL) { new_data_set->input = copy_xml(input); if (new_data_set->input == NULL) { out->err(out, "Failed to copy input XML"); pe_free_working_set(new_data_set); return ENOMEM; } } else { int rc = cib__signon_query(NULL, &(new_data_set->input)); if (rc != pcmk_rc_ok) { out->err(out, "CIB query failed: %s", pcmk_rc_str(rc)); pe_free_working_set(new_data_set); return rc; } } // Make our own copy of the given crm_time_t object or use the current time if (date != NULL) { // pcmk_copy_time() guarantees non-NULL new_data_set->now = pcmk_copy_time(date); } else { // So does crm_time_new() new_data_set->now = crm_time_new(NULL); } // Unpack everything cluster_status(new_data_set); *data_set = new_data_set; return pcmk_rc_ok; } /*! * \internal * \brief Check whether a given rule is in effect * * \param[in,out] out Output object * \param[in] data_set Cluster working set * \param[in] rule_id The ID of the rule to check * \param[in] date Check whether the rule is in effect at this date and * time * * \return Standard Pacemaker return code */ static int -crm_rule_check(pcmk__output_t *out, pe_working_set_t *data_set, - const char *rule_id, crm_time_t *date) +eval_rule(pcmk__output_t *out, pe_working_set_t *data_set, const char *rule_id, + crm_time_t *date) { xmlNodePtr cib_constraints = NULL; xmlNodePtr match = NULL; xmlXPathObjectPtr xpath_obj = NULL; char *xpath = NULL; int rc = pcmk_rc_ok; - int max = 0; + int num_results = 0; /* Rules are under the constraints node in the XML, so first find that. */ cib_constraints = pcmk_find_cib_element(data_set->input, XML_CIB_TAG_CONSTRAINTS); /* Get all rules matching the given ID that are also simple enough for us * to check. For the moment, these rules must only have a single * date_expression child and: * - Do not have a date_spec operation, or * - Have a date_spec operation that contains years= but does not contain * moon=. * * We do this in steps to provide better error messages. First, check that * there's any rule with the given ID. */ xpath = crm_strdup_printf("//rule[@id='%s']", rule_id); xpath_obj = xpath_search(cib_constraints, xpath); - max = numXpathResults(xpath_obj); + num_results = numXpathResults(xpath_obj); - if (max == 0) { - rc = ENXIO; + free(xpath); + freeXpathObject(xpath_obj); + + if (num_results == 0) { out->err(out, "No rule found with ID=%s", rule_id); - goto done; - } else if (max > 1) { - rc = ENXIO; + return ENXIO; + } else if (num_results > 1) { + // Should not be possible; schema prevents this out->err(out, "More than one rule with ID=%s found", rule_id); - goto done; + return ENXIO; } - free(xpath); - freeXpathObject(xpath_obj); - /* Next, make sure it has exactly one date_expression. */ xpath = crm_strdup_printf("//rule[@id='%s']//date_expression", rule_id); xpath_obj = xpath_search(cib_constraints, xpath); - max = numXpathResults(xpath_obj); + num_results = numXpathResults(xpath_obj); - if (max != 1) { - rc = EOPNOTSUPP; + free(xpath); + freeXpathObject(xpath_obj); + + if (num_results != 1) { out->err(out, "Can't check rule %s because it does not have exactly " "one date_expression", rule_id); - goto done; + return EOPNOTSUPP; } - free(xpath); - freeXpathObject(xpath_obj); - /* Then, check that it's something we actually support. */ xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[" "@operation!='date_spec']", rule_id); xpath_obj = xpath_search(cib_constraints, xpath); - max = numXpathResults(xpath_obj); + num_results = numXpathResults(xpath_obj); - if (max == 0) { - free(xpath); + free(xpath); + + if (num_results == 0) { freeXpathObject(xpath_obj); xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[" "@operation='date_spec' " "and date_spec/@years " "and not(date_spec/@moon)]", rule_id); xpath_obj = xpath_search(cib_constraints, xpath); - max = numXpathResults(xpath_obj); + num_results = numXpathResults(xpath_obj); - if (max == 0) { - rc = ENXIO; - out->err(out, "Rule either must not use date_spec, or use " - "date_spec with years= but not moon="); - goto done; + free(xpath); + + if (num_results == 0) { + freeXpathObject(xpath_obj); + out->err(out, "Rule %s must either not use date_spec, or use " + "date_spec with years= but not moon=", rule_id); + return ENXIO; } } match = getXpathResult(xpath_obj, 0); /* We should have ensured this with the xpath query above, but double- * checking can't hurt. */ CRM_ASSERT(match != NULL); CRM_ASSERT(find_expression_type(match) == time_expr); rc = eval_date_expression(match, date, NULL); out->message(out, "rule-check", rule_id, rc); - -done: - free(xpath); freeXpathObject(xpath_obj); return rc; } /*! * \internal * \brief Check whether each rule in a list is in effect * * \param[in,out] out Output object * \param[in] input The CIB XML to check (if \c NULL, use current CIB) * \param[in] date Check whether the rule is in effect at this date and * time (if \c NULL, use current date and time) * \param[in] rule_ids The IDs of the rules to check, as a NULL- * terminated list. * * \return Standard Pacemaker return code */ int pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, crm_time_t *date, const char **rule_ids) { pe_working_set_t *data_set = NULL; int rc = pcmk_rc_ok; CRM_ASSERT(out != NULL); if (rule_ids == NULL) { // Trivial case; every rule specified is in effect return pcmk_rc_ok; } rc = init_rule_check(out, input, date, &data_set); if (rc != pcmk_rc_ok) { return rc; } for (const char **rule_id = rule_ids; *rule_id != NULL; rule_id++) { - int last_rc = crm_rule_check(out, data_set, *rule_id, date); + int last_rc = eval_rule(out, data_set, *rule_id, date); if (last_rc != pcmk_rc_ok) { rc = last_rc; } } pe_free_working_set(data_set); return rc; } // Documented in pacemaker.h int pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, crm_time_t *date, const char **rule_ids) { pcmk__output_t *out = NULL; int rc = pcmk_rc_ok; rc = pcmk__xml_output_new(&out, xml); if (rc != pcmk_rc_ok) { return rc; } pcmk__register_lib_messages(out); rc = pcmk__check_rules(out, input, date, rule_ids); pcmk__xml_output_finish(out, xml); return rc; }