Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: libpe_status, xml: Support integer as rule type attribute #2135

Merged
merged 18 commits into from
Aug 17, 2020

Conversation

nrwahl2
Copy link
Contributor

@nrwahl2 nrwahl2 commented Aug 5, 2020

The documentation specifies three potential values for the type
attribute of rule expressions:

  • string
  • integer
  • version

Yet the rule.rng schema and pe__eval_attr_expr() expect "number" instead
of "integer".

This pull corrects the documentation to align with the schema and code.

Thanks @tomjelinek for reporting this.


Edited summary:

This pull does the following:

  • Creates a pcmk_parse_double() function (strtod() wrapper) and unit tests
  • Creates a pcmk__char_in_any_str() function (multi-strchr() wrapper) and unit tests
  • Adds "integer" to the rule schema as a valid type attribute
  • Adds "integer" support to libpe_status, updates "number" to specify a floating-point comparison, and uses "integer" for integer comparisons. (Previously, "number" specified an integer comparison.)
  • Updates the documentation to align with the schema and libpe_status

@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 5, 2020

Alternative options:

  1. Would it make sense to parse the values with strtod() and treat them as doubles? It seems like that wouldn't be too complicated. Maybe add a pcmk_parse_double() to the strings library.

  2. If Medium: cib: Fix compilation of ACL code #1 is not worthwhile, what about changing the schema to line up with the doc and deprecate "number" in favor of "integer"? Floating-point values get truncated to ints currently. IMO it's better to name the type choice accordingly and make it clear that we can only do integer comparisons.

Changing the schema seems like a tricky process due to compatibility concerns. I hit a roadblock during an initial attempt at that (mostly due to CTS's use of the rule-2.9 schema, which was last updated 3 years ago). I read over the schema readme but still not sure how to approach it if we were to go that route.

@kgaillot
Copy link
Contributor

kgaillot commented Aug 6, 2020

I'm thinking we should leave the documentation as-is, and modify the schema and code to accept either "integer" or "number" interchangeably. Perhaps at a future Pacemaker major-version bump we can make "number" behave as a floating point.

To modify the schema, see xml/Readme.md. This will be a compatible change.

@kgaillot
Copy link
Contributor

kgaillot commented Aug 6, 2020

Another possibility would be to treat "number" as a double if it contains a '.', and an integer otherwise. That would keep backward compatibility for anyone using it as an integer currently while allowing users to start using it for floating points. In that case the documentation, code and schema could be brought in line as:

  • "integer", or "number" with a value without '.': integer
  • "number" with a value with '.': double-precision floating point

@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 7, 2020

To modify the schema, see xml/Readme.md. This will be a compatible change.

Ack. As we discussed in chat, the source of confusion was the use of an old rule schema in the current constraints schema.


Another possibility would be to treat "number" as a double if it contains a '.', and an integer otherwise. That would keep backward compatibility for anyone using it as an integer currently while allowing users to start using it for floating points.

Would this actually do anything to alleviate your concern in the RHBZ? You said:

If anyone's using it, they're using "number" with integers, and they may reasonably expect values not to have any of the unusual corner cases of floating-point arithmetic, so we should keep "number" syntax and integer behavior.

If they're currently using integer strings (i.e., without a '.'), then they shouldn't be prone to any floating-point corner cases AFAIK. The floating point promotion would just add zeroes after the decimal place anyway.

Users would only hit a floating-point corner case (e.g., a comparison failing due to a precision issue in FP arithmetic) after the suggested change if they're currently using floating-point strings (i.e., with a '.') and relying on Pacemaker to truncate the value to an int. Then after the change, those values would be parsed as doubles and may compare incorrectly, in theory.

So it seems to me that parsing numbers with '.' as floating-point would be just the same as parsing ALL numbers as floating-point, when it comes to the risk of hitting unexpected behavior in a corner case.

@nrwahl2 nrwahl2 force-pushed the nrwahl2-rule_int branch 3 times, most recently from b9b48dd to a2a8b02 Compare August 7, 2020 09:40
@nrwahl2 nrwahl2 changed the title Doc: Pacemaker Explained: Fix rules type spec Fix: libpe_status, xml: Support integer as rule type attribute Aug 7, 2020
@kgaillot
Copy link
Contributor

kgaillot commented Aug 7, 2020

So it seems to me that parsing numbers with '.' as floating-point would be just the same as parsing ALL numbers as floating-point, when it comes to the risk of hitting unexpected behavior in a corner case.

A bit contrived, but:

#include <stdio.h>
#include <stdlib.h>


int main(int argc, char **argv)
{
    unsigned long integer = 9223372036854775809UL;
    double number = strtod("9223372036854775809.0", NULL);

    printf("%lu %f\n", integer, number);
    return 0;
}

However that wouldn't affect our situation since we currently parse the values as 32-bit integers (which I didn't realize until checking just now), so technically you're right. :) Which makes me think we should indeed parse "number" as double, and start parsing "integer" with crm_parse_ll(). That would clean things up nicely and shouldn't affect any existing usage adversely.

@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 7, 2020

A bit contrived, but:

The best corner cases are contrived :)
And ah yes, large ints that require high precision. I forgot about losses when converting those to doubles.

That would clean things up nicely and shouldn't affect any existing usage adversely.

Ack. So I take it we're not worrying about any odd use cases where the expected result of a comparison depends on truncation to int. I support going with double for "number" as long as you're good with it, since it is more flexible.

I was working on a pcmk__scan_double() and pcmk_parse_double(). I back-burnered them to focus on commits for the main focus of this issue, after having some initial difficulty detecting overflow and underflow in a way that's reliable on both ANSI and C99. (The strtod() return value for underflow is 0 and ERANGE on ANSI, while it's "<= smallest positive normalized number and maybe ERANGE (implementation-dependent)" on C99.)

If we've settled on parsing "number" as double, I'll get back to work on that and add the result to the PR.

@kgaillot
Copy link
Contributor

kgaillot commented Aug 7, 2020

Ack. So I take it we're not worrying about any odd use cases where the expected result of a comparison depends on truncation to int. I support going with double for "number" as long as you're good with it, since it is more flexible.

Yep. Using a "." and expecting an integer comparison would be twisted enough that I'd consider it a bug.

I was working on a pcmk__scan_double() and pcmk_parse_double(). I back-burnered them to focus on commits for the main focus of this issue, after having some initial difficulty detecting overflow and underflow in a way that's reliable on both ANSI and C99. (The strtod() return value for underflow is 0 and ERANGE on ANSI, while it's "<= smallest positive normalized number and maybe ERANGE (implementation-dependent)" on C99.)

If we've settled on parsing "number" as double, I'll get back to work on that and add the result to the PR.

Sounds good

@nrwahl2 nrwahl2 force-pushed the nrwahl2-rule_int branch 5 times, most recently from 85a73e5 to ebb493d Compare August 9, 2020 02:26
@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 9, 2020

This has grown pretty large. I think I'm finished, until there's feedback on things that need to be changed.

A lot of the commits are prep work (e.g., adding new utilities and their tests, copying schemas, etc.). Four of them are minor issues and opportunities I happened upon while doing the prep work. The last few commits directly address the issue.

@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 10, 2020

One other thing: Does any of this require a feature set bump?

Copy link
Contributor

@kgaillot kgaillot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, very thorough. I think a feature bump is worthwhile since otherwise the behavior of floating-point attribute values could change depending on which node is DC. Also, IIRC the schema version bump protects us from the user being able to upgrade the CIB before all nodes support it, but technically users can run without schema verification, so it's best not to rely solely on that (without schema verification someone could specify integer in a mixed-version cluster and get really different behavior).

@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 11, 2020

By the way, we can't rely on GCC and Clang extensions, right? GCC (and I believe Clang as well) has an extension for variadic macro arguments that would eliminate the need to pass NULL as a sentinel value to some of our newer string functions. It's not part of the standard though.

@nrwahl2 nrwahl2 force-pushed the nrwahl2-rule_int branch 2 times, most recently from 36379c3 to 54585bf Compare August 11, 2020 08:19
nrwahl2 added 12 commits August 13, 2020 13:55
Add new function to parse a string to a double. Modeled after
crm_parse_ll().

Signed-off-by: Reid Wahl <[email protected]>
Instead of ERANGE. Also document return values for scan_ll().

Signed-off-by: Reid Wahl <[email protected]>
Calls strchr() to check whether a character is in any of a variable
argument list of strings.

Signed-off-by: Reid Wahl <[email protected]>
For function pcmk_numeric_strcasecmp().

Signed-off-by: Reid Wahl <[email protected]>
"...Similarly, it [g_assert()] must not be used in unit tests, otherwise
the unit tests will be ineffective if compiled with G_DISABLE_ASSERT.
Use g_assert_true() and related macros in unit tests instead."

https://developer.gnome.org/glib/unstable/glib-Testing.html#g-assert

Also remove redundant test for pcmk__str_any_of().

Signed-off-by: Reid Wahl <[email protected]>
Will be adding support for "integer" as rule "type" attribute.

Signed-off-by: Reid Wahl <[email protected]>
The Pacemaker Explained doc has long described "integer" as a valid
value for the "type" attribute of a rule. The rule schema includes
"number" but does not include "integer" in this position.

This commit adds "integer" to the schema alongside "number".

Signed-off-by: Reid Wahl <[email protected]>
@nrwahl2 nrwahl2 force-pushed the nrwahl2-rule_int branch 2 times, most recently from 79573e8 to bfcf5bd Compare August 13, 2020 21:23
For readability and to prep for next changes.

Signed-off-by: Reid Wahl <[email protected]>
Copy link
Contributor

@kgaillot kgaillot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a couple minor comments

For node attribute expressions in a rule.

Use long long instead of int, and default to string if numbers fail to
parse as integers.

Signed-off-by: Reid Wahl <[email protected]>
The Pacemaker Explained doc has long described "integer" as a valid
value for the "type" attribute of a rule. However, libpe_status does not
support "integer" as the type attribute for a rule. Yet it parses
all "number"-type values as integers.

This commit updates libpe_status to accept "integer" as a rule's type
attribute and to parse integers as long longs instead of as ints.
"number"-type values will now be parsed as doubles. If the rule
evaluation **defaults** to a numeric comparison, then the type will be
set to "number" if either value contains a decimal point or to "integer"
otherwise. If a numeric parse fails, then the values will be compared as
strings.

Bump CRM_FEATURE_SET to 3.5.0.

Signed-off-by: Reid Wahl <[email protected]>
@nrwahl2
Copy link
Contributor Author

nrwahl2 commented Aug 17, 2020

I just noticed that I updated cts-scheduler instead of cts-scheduler.in for some of the added regression tests. Going to have to rewrite the list additions and re-push.

Add "number" as an allowed value for the type attribute of a rule
expression. Add description of how "integer" and "number" comparisons
differ and of how a default comparison type is chosen.

Signed-off-by: Reid Wahl <[email protected]>
@kgaillot kgaillot merged commit 77c78c2 into ClusterLabs:master Aug 17, 2020
@kgaillot
Copy link
Contributor

Awesome :)

@nrwahl2 nrwahl2 deleted the nrwahl2-rule_int branch October 10, 2023 07:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants