Merge "Add query property to threshold alarms."
This commit is contained in:
@@ -116,12 +116,21 @@ class CeilometerAlarm(resource.Resource):
|
||||
|
||||
PROPERTIES = (
|
||||
COMPARISON_OPERATOR, EVALUATION_PERIODS, METER_NAME, PERIOD,
|
||||
STATISTIC, THRESHOLD, MATCHING_METADATA,
|
||||
STATISTIC, THRESHOLD, MATCHING_METADATA, QUERY,
|
||||
) = (
|
||||
'comparison_operator', 'evaluation_periods', 'meter_name', 'period',
|
||||
'statistic', 'threshold', 'matching_metadata',
|
||||
'statistic', 'threshold', 'matching_metadata', 'query',
|
||||
)
|
||||
|
||||
QUERY_FACTOR_FIELDS = (
|
||||
QF_FIELD, QF_OP, QF_VALUE,
|
||||
) = (
|
||||
'field', 'op', 'value',
|
||||
)
|
||||
|
||||
QF_OP_VALS = constraints.AllowedValues(['le', 'ge', 'eq',
|
||||
'lt', 'gt', 'ne'])
|
||||
|
||||
properties_schema = {
|
||||
COMPARISON_OPERATOR: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
@@ -166,41 +175,89 @@ class CeilometerAlarm(resource.Resource):
|
||||
properties.Schema.MAP,
|
||||
_('Meter should match this resource metadata (key=value) '
|
||||
'additionally to the meter_name.'),
|
||||
default={}
|
||||
default={},
|
||||
update_allowed=True
|
||||
),
|
||||
QUERY: properties.Schema(
|
||||
properties.Schema.LIST,
|
||||
_('A list of query factors, each comparing '
|
||||
'a Sample attribute with a value. '
|
||||
'Implicitly combined with matching_metadata, if any.'),
|
||||
update_allowed=True,
|
||||
support_status=support.SupportStatus(version='2015.1'),
|
||||
schema=properties.Schema(
|
||||
properties.Schema.MAP,
|
||||
schema={
|
||||
QF_FIELD: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Name of attribute to compare. '
|
||||
'Names of the form metadata.user_metadata.X '
|
||||
'or metadata.metering.X are equivalent to what '
|
||||
'you can address through matching_metadata; '
|
||||
'the former for Nova meters, '
|
||||
'the latter for all others. '
|
||||
'To see the attributes of your Samples, '
|
||||
'use `ceilometer --debug sample-list`. ')
|
||||
),
|
||||
QF_OP: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Comparison operator'),
|
||||
constraints=[QF_OP_VALS]
|
||||
),
|
||||
QF_VALUE: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('String value with which to compare')
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
properties_schema.update(common_properties_schema)
|
||||
|
||||
default_client_name = 'ceilometer'
|
||||
|
||||
def cfn_to_ceilometer(self, stack, properties):
|
||||
"""Apply all relevant compatibility xforms."""
|
||||
|
||||
kwargs = actions_to_urls(stack, properties)
|
||||
if self.MATCHING_METADATA not in properties:
|
||||
return kwargs
|
||||
kwargs['type'] = 'threshold'
|
||||
rule = {}
|
||||
for field in ['period', 'evaluation_periods', 'threshold',
|
||||
'statistic', 'comparison_operator', 'meter_name']:
|
||||
if field in kwargs:
|
||||
rule[field] = kwargs[field]
|
||||
del kwargs[field]
|
||||
mmd = properties.get(self.MATCHING_METADATA) or {}
|
||||
query = properties.get(self.QUERY) or []
|
||||
|
||||
if kwargs.get(self.METER_NAME) in NOVA_METERS:
|
||||
prefix = 'user_metadata.'
|
||||
else:
|
||||
prefix = 'metering.'
|
||||
|
||||
# make sure we have matching_metadata that looks like this:
|
||||
# matching_metadata: {metadata.$prefix.x}
|
||||
kwargs[self.MATCHING_METADATA] = {}
|
||||
for m_k, m_v in six.iteritems(properties.get(
|
||||
self.MATCHING_METADATA, {})):
|
||||
# make sure the matching_metadata appears in the query like this:
|
||||
# {field: metadata.$prefix.x, ...}
|
||||
for m_k, m_v in six.iteritems(mmd):
|
||||
if m_k.startswith('metadata.%s' % prefix):
|
||||
key = m_k
|
||||
elif m_k.startswith(prefix):
|
||||
key = 'metadata.%s' % m_k
|
||||
else:
|
||||
key = 'metadata.%s%s' % (prefix, m_k)
|
||||
kwargs[self.MATCHING_METADATA][key] = m_v
|
||||
query.append(dict(field=key, op='eq', value=m_v))
|
||||
if self.MATCHING_METADATA in kwargs:
|
||||
del kwargs[self.MATCHING_METADATA]
|
||||
if self.QUERY in kwargs:
|
||||
del kwargs[self.QUERY]
|
||||
if query:
|
||||
rule['query'] = query
|
||||
kwargs['threshold_rule'] = rule
|
||||
return kwargs
|
||||
|
||||
def handle_create(self):
|
||||
props = self.cfn_to_ceilometer(self.stack,
|
||||
self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
|
||||
alarm = self.ceilometer().alarms.create(**props)
|
||||
self.resource_id_set(alarm.alarm_id)
|
||||
|
||||
@@ -218,6 +275,7 @@ class CeilometerAlarm(resource.Resource):
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
kwargs = {'alarm_id': self.resource_id}
|
||||
kwargs.update(self.properties)
|
||||
kwargs.update(prop_diff)
|
||||
alarms_client = self.ceilometer().alarms
|
||||
alarms_client.update(**self.cfn_to_ceilometer(self.stack, kwargs))
|
||||
|
@@ -161,15 +161,31 @@ class CeilometerAlarmTest(HeatTestCase):
|
||||
al['ok_actions'] = None
|
||||
al['repeat_actions'] = True
|
||||
al['enabled'] = True
|
||||
al['evaluation_periods'] = 1
|
||||
al['period'] = 60
|
||||
al['threshold'] = 50
|
||||
if 'matching_metadata' in al:
|
||||
al['matching_metadata'] = dict(
|
||||
('metadata.metering.%s' % k, v)
|
||||
for k, v in al['matching_metadata'].items())
|
||||
rule = dict(
|
||||
period=60,
|
||||
evaluation_periods=1,
|
||||
threshold=50)
|
||||
for field in ['period', 'evaluation_periods', 'threshold']:
|
||||
del al[field]
|
||||
for field in ['statistic', 'comparison_operator', 'meter_name']:
|
||||
rule[field] = al[field]
|
||||
del al[field]
|
||||
if 'query' in al and al['query']:
|
||||
query = al['query']
|
||||
else:
|
||||
al['matching_metadata'] = {}
|
||||
query = []
|
||||
if 'query' in al:
|
||||
del al['query']
|
||||
if 'matching_metadata' in al and al['matching_metadata']:
|
||||
for k, v in al['matching_metadata'].items():
|
||||
key = 'metadata.metering.' + k
|
||||
query.append(dict(field=key, op='eq', value=v))
|
||||
if 'matching_metadata' in al:
|
||||
del al['matching_metadata']
|
||||
if query:
|
||||
rule['query'] = query
|
||||
al['threshold_rule'] = rule
|
||||
al['type'] = 'threshold'
|
||||
self.m.StubOutWithMock(self.fa.alarms, 'create')
|
||||
self.fa.alarms.create(**al).AndReturn(FakeCeilometerAlarm())
|
||||
return stack
|
||||
@@ -184,15 +200,30 @@ class CeilometerAlarmTest(HeatTestCase):
|
||||
properties = t['Resources']['MEMAlarmHigh']['Properties']
|
||||
properties['alarm_actions'] = ['signal_handler']
|
||||
properties['matching_metadata'] = {'a': 'v'}
|
||||
properties['query'] = [dict(field='b', op='eq', value='w')]
|
||||
|
||||
self.stack = self.create_stack(template=json.dumps(t))
|
||||
self.m.StubOutWithMock(self.fa.alarms, 'update')
|
||||
schema = schemata(alarm.CeilometerAlarm.properties_schema)
|
||||
exns = ['period', 'evaluation_periods', 'threshold',
|
||||
'statistic', 'comparison_operator', 'meter_name',
|
||||
'matching_metadata', 'query']
|
||||
al2 = dict((k, mox.IgnoreArg())
|
||||
for k, s in schema.items() if s.update_allowed)
|
||||
for k, s in schema.items()
|
||||
if s.update_allowed and k not in exns)
|
||||
al2['alarm_id'] = mox.IgnoreArg()
|
||||
del al2['enabled']
|
||||
del al2['repeat_actions']
|
||||
al2['type'] = 'threshold'
|
||||
al2['threshold_rule'] = dict(
|
||||
meter_name=properties['meter_name'],
|
||||
period=90,
|
||||
evaluation_periods=2,
|
||||
threshold=39,
|
||||
statistic='max',
|
||||
comparison_operator='lt',
|
||||
query=[
|
||||
dict(field='c', op='ne', value='z'),
|
||||
dict(field='metadata.metering.x', op='eq', value='y')
|
||||
])
|
||||
self.fa.alarms.update(**al2).AndReturn(None)
|
||||
|
||||
self.m.ReplayAll()
|
||||
@@ -212,6 +243,8 @@ class CeilometerAlarmTest(HeatTestCase):
|
||||
'insufficient_data_actions': [],
|
||||
'alarm_actions': [],
|
||||
'ok_actions': ['signal_handler'],
|
||||
'matching_metadata': {'x': 'y'},
|
||||
'query': [dict(field='c', op='ne', value='z')]
|
||||
})
|
||||
snippet = rsrc_defn.ResourceDefinition(rsrc.name,
|
||||
rsrc.type(),
|
||||
|
Reference in New Issue
Block a user