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;
}