Merge "Fix the -q/--query in threshold alarm creation"
This commit is contained in:
commit
03e7a45e5e
@ -125,7 +125,7 @@ class CliAlarmCreateTest(testtools.TestCase):
|
||||
'--comparison-operator', 'le',
|
||||
'--threshold', '80',
|
||||
'--event-type', 'event',
|
||||
'--query', '{}',
|
||||
'--query', 'resource=fake-resource-id',
|
||||
'--granularity', '60',
|
||||
'--aggregation-method', 'last',
|
||||
'--metric', 'cpu',
|
||||
@ -158,12 +158,18 @@ class CliAlarmCreateTest(testtools.TestCase):
|
||||
'statistic': 'max',
|
||||
'comparison_operator': 'le',
|
||||
'threshold': 80.0,
|
||||
'query': '{}'
|
||||
},
|
||||
'query': [{'field': 'resource',
|
||||
'op': 'eq',
|
||||
'type': '',
|
||||
'value': 'fake-resource-id'}]
|
||||
},
|
||||
'event_rule': {
|
||||
'event_type': 'event',
|
||||
'query': '{}'
|
||||
},
|
||||
'query': [{'field': 'resource',
|
||||
'op': 'eq',
|
||||
'type': '',
|
||||
'value': 'fake-resource-id'}]
|
||||
},
|
||||
'gnocchi_resources_threshold_rule': {
|
||||
'granularity': '60',
|
||||
'metric': 'cpu',
|
||||
@ -173,14 +179,14 @@ class CliAlarmCreateTest(testtools.TestCase):
|
||||
'comparison_operator': 'le',
|
||||
'threshold': 80.0,
|
||||
'resource_type': 'generic'
|
||||
},
|
||||
},
|
||||
'gnocchi_aggregation_by_metrics_threshold_rule': {
|
||||
'granularity': '60',
|
||||
'aggregation_method': 'last',
|
||||
'evaluation_periods': 60,
|
||||
'comparison_operator': 'le',
|
||||
'threshold': 80.0
|
||||
},
|
||||
},
|
||||
'gnocchi_aggregation_by_resources_threshold_rule': {
|
||||
'granularity': '60',
|
||||
'metric': 'cpu',
|
||||
@ -188,9 +194,12 @@ class CliAlarmCreateTest(testtools.TestCase):
|
||||
'evaluation_periods': 60,
|
||||
'comparison_operator': 'le',
|
||||
'threshold': 80.0,
|
||||
'query': '{}',
|
||||
'query': [{'field': 'resource',
|
||||
'op': 'eq',
|
||||
'type': '',
|
||||
'value': 'fake-resource-id'}],
|
||||
'resource_type': 'generic'
|
||||
},
|
||||
},
|
||||
'composite_rule': None,
|
||||
'type': 'threshold'
|
||||
}
|
||||
|
@ -81,3 +81,13 @@ class SearchQueryBuilderTest(base.BaseTestCase):
|
||||
]},
|
||||
{"=": {"foo": "quote"}},
|
||||
]})
|
||||
|
||||
|
||||
class CliQueryToArray(base.BaseTestCase):
|
||||
def test_cli_query_to_arrary(self):
|
||||
cli_query = "this<=34;that=string::foo"
|
||||
ret_array = utils.cli_to_array(cli_query)
|
||||
expected_query = [
|
||||
{"field": "this", "type": "", "value": "34", "op": "le"},
|
||||
{"field": "that", "type": "string", "value": "foo", "op": "eq"}]
|
||||
self.assertEqual(expected_query, ret_array)
|
||||
|
@ -11,7 +11,7 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import re
|
||||
|
||||
import pyparsing as pp
|
||||
|
||||
@ -48,6 +48,16 @@ expr = pp.operatorPrecedence(condition, [
|
||||
("∨", 2, pp.opAssoc.LEFT, ),
|
||||
])
|
||||
|
||||
OP_LOOKUP = {'!=': 'ne',
|
||||
'>=': 'ge',
|
||||
'<=': 'le',
|
||||
'>': 'gt',
|
||||
'<': 'lt',
|
||||
'=': 'eq'}
|
||||
|
||||
OP_LOOKUP_KEYS = '|'.join(sorted(OP_LOOKUP.keys(), key=len, reverse=True))
|
||||
OP_SPLIT_RE = re.compile(r'(%s)' % OP_LOOKUP_KEYS)
|
||||
|
||||
|
||||
def _parsed_query2dict(parsed_query):
|
||||
result = None
|
||||
@ -125,3 +135,44 @@ def dict_to_querystring(objs):
|
||||
return "&".join(["%s=%s" % (k, v)
|
||||
for k, v in objs.items()
|
||||
if v is not None])
|
||||
|
||||
|
||||
def cli_to_array(cli_query):
|
||||
"""Convert CLI list of queries to the Python API format.
|
||||
|
||||
This will convert the following:
|
||||
"this<=34;that=string::foo"
|
||||
to
|
||||
"[{field=this,op=le,value=34,type=''},
|
||||
{field=that,op=eq,value=foo,type=string}]"
|
||||
|
||||
"""
|
||||
|
||||
opts = []
|
||||
queries = cli_query.split(';')
|
||||
for q in queries:
|
||||
try:
|
||||
field, q_operator, type_value = OP_SPLIT_RE.split(q, maxsplit=1)
|
||||
except ValueError:
|
||||
raise ValueError('Invalid or missing operator in query %(q)s,'
|
||||
'the supported operators are: %(k)s' %
|
||||
{'q': q, 'k': OP_LOOKUP.keys()})
|
||||
if not field:
|
||||
raise ValueError('Missing field in query %s' % q)
|
||||
if not type_value:
|
||||
raise ValueError('Missing value in query %s' % q)
|
||||
opt = dict(field=field, op=OP_LOOKUP[q_operator])
|
||||
|
||||
if '::' not in type_value:
|
||||
opt['type'], opt['value'] = '', type_value
|
||||
else:
|
||||
opt['type'], _, opt['value'] = type_value.partition('::')
|
||||
|
||||
if opt['type'] and opt['type'] not in (
|
||||
'string', 'integer', 'float', 'datetime', 'boolean'):
|
||||
err = ('Invalid value type %(type)s, the type of value'
|
||||
'should be one of: integer, string, float, datetime,'
|
||||
' boolean.' % opt['type'])
|
||||
raise ValueError(err)
|
||||
opts.append(opt)
|
||||
return opts
|
||||
|
@ -156,10 +156,14 @@ class CliAlarmCreate(show.ShowOne):
|
||||
|
||||
common_group = parser.add_argument_group('common alarm rules')
|
||||
common_group.add_argument(
|
||||
'-q', '--query', metavar='<QUERY>', dest='query',
|
||||
help='key[op]data_type::value; list. data_type is optional, '
|
||||
'but if supplied must be string, integer, float, or boolean. '
|
||||
'Used by threshold and event alarms')
|
||||
'--query', metavar='<QUERY>', dest='query',
|
||||
help="For alarms of type threshold or event: "
|
||||
"key[op]data_type::value; list. data_type is optional, "
|
||||
"but if supplied must be string, integer, float, or boolean. "
|
||||
'For alarms of '
|
||||
'type gnocchi_aggregation_by_resources_threshold: '
|
||||
'need to specify a complex query json string, like:'
|
||||
' {"and": [{"=": {"ended_at": null}}, ...]}.')
|
||||
common_group.add_argument(
|
||||
'--comparison-operator', metavar='<OPERATOR>',
|
||||
dest='comparison_operator', choices=ALARM_OPERATORS,
|
||||
@ -285,6 +289,8 @@ class CliAlarmCreate(show.ShowOne):
|
||||
'state', 'severity', 'enabled', 'alarm_actions',
|
||||
'ok_actions', 'insufficient_data_actions',
|
||||
'time_constraints', 'repeat_actions'])
|
||||
if parsed_args.type in ('threshold', 'event') and parsed_args.query:
|
||||
parsed_args.query = utils.cli_to_array(parsed_args.query)
|
||||
alarm['threshold_rule'] = utils.dict_from_parsed_args(
|
||||
parsed_args, ['meter_name', 'period', 'evaluation_periods',
|
||||
'statistic', 'comparison_operator', 'threshold',
|
||||
|
Loading…
x
Reference in New Issue
Block a user