Merge "Fix the -q/--query in threshold alarm creation"

This commit is contained in:
Jenkins 2016-03-14 12:40:34 +00:00 committed by Gerrit Code Review
commit 03e7a45e5e
4 changed files with 90 additions and 14 deletions

View File

@ -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'
}

View File

@ -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)

View File

@ -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

View File

@ -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',