Add new conditions: matches and contains
Both check a value against a regular expression. The former requires full match, while the latter requires matching anywhere in the string representaion of value. Change-Id: Ia59d17d6f8383aed97696d678fc1e7e329242692
This commit is contained in:
parent
b7c61dee01
commit
d6ff54faf7
@ -63,7 +63,9 @@ A condition is represented by an object with fields:
|
||||
|
||||
``op`` the type of comparison operation, default available operators include :
|
||||
``eq``, ``le``, ``ge``, ``ne``, ``lt``, ``gt`` (basic comparison operators),
|
||||
``in-net`` (checks that IP address is in a given network).
|
||||
``in-net`` (checks that IP address is in a given network), ``contains``,
|
||||
``matches`` (check field against a regular expression, matches require a full
|
||||
match).
|
||||
|
||||
``field`` a `JSON path <http://goessner.net/articles/JsonPath/>`_ to the field
|
||||
in the introspection data to use in comparison.
|
||||
|
@ -14,9 +14,11 @@
|
||||
"""Standard plugins for rules API."""
|
||||
|
||||
import operator
|
||||
import re
|
||||
|
||||
import netaddr
|
||||
|
||||
from ironic_inspector.common.i18n import _
|
||||
from ironic_inspector.plugins import base
|
||||
from ironic_inspector import utils
|
||||
|
||||
@ -79,6 +81,27 @@ class NetCondition(base.RuleConditionPlugin):
|
||||
return netaddr.IPAddress(field) in network
|
||||
|
||||
|
||||
class ReCondition(base.RuleConditionPlugin):
|
||||
def validate(self, params, **kwargs):
|
||||
try:
|
||||
re.compile(params['value'])
|
||||
except re.error as exc:
|
||||
raise ValueError(_('invalid regular expression: %s') % exc)
|
||||
|
||||
|
||||
class MatchesCondition(ReCondition):
|
||||
def check(self, node_info, field, params, **kwargs):
|
||||
regexp = params['value']
|
||||
if regexp[-1] != '$':
|
||||
regexp += '$'
|
||||
return re.match(regexp, str(field)) is not None
|
||||
|
||||
|
||||
class ContainsCondition(ReCondition):
|
||||
def check(self, node_info, field, params, **kwargs):
|
||||
return re.search(params['value'], str(field)) is not None
|
||||
|
||||
|
||||
class FailAction(base.RuleActionPlugin):
|
||||
REQUIRED_PARAMS = {'message'}
|
||||
|
||||
|
@ -303,6 +303,8 @@ class Test(Base):
|
||||
{'field': 'memory_mb', 'op': 'eq', 'value': 12288},
|
||||
{'field': 'local_gb', 'op': 'gt', 'value': 998},
|
||||
{'field': 'local_gb', 'op': 'lt', 'value': 1000},
|
||||
{'field': 'local_gb', 'op': 'matches', 'value': '[0-9]+'},
|
||||
{'field': 'cpu_arch', 'op': 'contains', 'value': '[0-9]+'},
|
||||
],
|
||||
'actions': [
|
||||
{'action': 'set-attribute', 'path': '/extra/foo',
|
||||
|
@ -73,6 +73,34 @@ class TestSimpleConditions(test_base.BaseTest):
|
||||
self._test(cond, expected, *values)
|
||||
|
||||
|
||||
class TestReConditions(test_base.BaseTest):
|
||||
def test_validate(self):
|
||||
for cond in (rules_plugins.MatchesCondition(),
|
||||
rules_plugins.ContainsCondition()):
|
||||
cond.validate({'value': r'[a-z]?(foo|b.r).+'})
|
||||
self.assertRaises(ValueError, cond.validate,
|
||||
{'value': '**'})
|
||||
|
||||
def test_matches(self):
|
||||
cond = rules_plugins.MatchesCondition()
|
||||
for reg, field, res in [(r'.*', 'foo', True),
|
||||
(r'fo{1,2}', 'foo', True),
|
||||
(r'o{1,2}', 'foo', False),
|
||||
(r'[1-9]*', 42, True),
|
||||
(r'^(foo|bar)$', 'foo', True),
|
||||
(r'fo', 'foo', False)]:
|
||||
self.assertEqual(res, cond.check(None, field, {'value': reg}))
|
||||
|
||||
def test_contains(self):
|
||||
cond = rules_plugins.ContainsCondition()
|
||||
for reg, field, res in [(r'.*', 'foo', True),
|
||||
(r'fo{1,2}', 'foo', True),
|
||||
(r'o{1,2}', 'foo', True),
|
||||
(r'[1-9]*', 42, True),
|
||||
(r'bar', 'foo', False)]:
|
||||
self.assertEqual(res, cond.check(None, field, {'value': reg}))
|
||||
|
||||
|
||||
class TestNetCondition(test_base.BaseTest):
|
||||
cond = rules_plugins.NetCondition()
|
||||
|
||||
|
@ -0,0 +1,4 @@
|
||||
---
|
||||
features:
|
||||
- New condition plugins "contains" and "matches" allow to match value against
|
||||
regular expressions.
|
@ -43,6 +43,8 @@ ironic_inspector.rules.conditions =
|
||||
ge = ironic_inspector.plugins.rules:GeCondition
|
||||
ne = ironic_inspector.plugins.rules:NeCondition
|
||||
in-net = ironic_inspector.plugins.rules:NetCondition
|
||||
matches = ironic_inspector.plugins.rules:MatchesCondition
|
||||
contains = ironic_inspector.plugins.rules:ContainsCondition
|
||||
ironic_inspector.rules.actions =
|
||||
example = ironic_inspector.plugins.example:ExampleRuleAction
|
||||
fail = ironic_inspector.plugins.rules:FailAction
|
||||
|
Loading…
Reference in New Issue
Block a user