Add OS::Aodh::PrometheusAlarm resource
This patch adds support for Aodh alarms of type Prometheus. Depends-On: I72e124cca4398b78f7ed12e1db3f66bdbfcb196e Change-Id: I5bb7c4d9086715fc22c0f7abc36d9bbfc88a60c9
This commit is contained in:
parent
a4ac653e35
commit
f8a44f4904
@ -20,7 +20,22 @@ from heat.engine import support
|
||||
from heat.engine import translation
|
||||
|
||||
|
||||
class AodhAlarm(alarm_base.BaseAlarm):
|
||||
class AodhBaseActionsMixin:
|
||||
def handle_create(self):
|
||||
props = self.get_alarm_props(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
alarm = self.client().alarm.create(props)
|
||||
self.resource_id_set(alarm['alarm_id'])
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
new_props = json_snippet.properties(self.properties_schema,
|
||||
self.context)
|
||||
self.client().alarm.update(self.resource_id,
|
||||
self.get_alarm_props(new_props))
|
||||
|
||||
|
||||
class AodhAlarm(AodhBaseActionsMixin, alarm_base.BaseAlarm):
|
||||
"""A resource that implements alarming service of Aodh.
|
||||
|
||||
A resource that allows for the setting alarms based on threshold evaluation
|
||||
@ -176,19 +191,6 @@ class AodhAlarm(alarm_base.BaseAlarm):
|
||||
kwargs['threshold_rule'] = rule
|
||||
return kwargs
|
||||
|
||||
def handle_create(self):
|
||||
props = self.get_alarm_props(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
alarm = self.client().alarm.create(props)
|
||||
self.resource_id_set(alarm['alarm_id'])
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
new_props = json_snippet.properties(self.properties_schema,
|
||||
self.context)
|
||||
self.client().alarm.update(self.resource_id,
|
||||
self.get_alarm_props(new_props))
|
||||
|
||||
def parse_live_resource_data(self, resource_properties, resource_data):
|
||||
record_reality = {}
|
||||
threshold_data = resource_data.get('threshold_rule').copy()
|
||||
@ -232,7 +234,7 @@ class CombinationAlarm(none_resource.NoneResource):
|
||||
)
|
||||
|
||||
|
||||
class EventAlarm(alarm_base.BaseAlarm):
|
||||
class EventAlarm(AodhBaseActionsMixin, alarm_base.BaseAlarm):
|
||||
"""A resource that implements event alarms.
|
||||
|
||||
Allows users to define alarms which can be evaluated based on events
|
||||
@ -313,21 +315,8 @@ class EventAlarm(alarm_base.BaseAlarm):
|
||||
kwargs['event_rule'] = rule
|
||||
return kwargs
|
||||
|
||||
def handle_create(self):
|
||||
props = self.get_alarm_props(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
alarm = self.client().alarm.create(props)
|
||||
self.resource_id_set(alarm['alarm_id'])
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
new_props = json_snippet.properties(self.properties_schema,
|
||||
self.context)
|
||||
self.client().alarm.update(self.resource_id,
|
||||
self.get_alarm_props(new_props))
|
||||
|
||||
|
||||
class LBMemberHealthAlarm(alarm_base.BaseAlarm):
|
||||
class LBMemberHealthAlarm(AodhBaseActionsMixin, alarm_base.BaseAlarm):
|
||||
"""A resource that implements a Loadbalancer Member Health Alarm.
|
||||
|
||||
Allows setting alarms based on the health of load balancer pool members,
|
||||
@ -411,18 +400,65 @@ class LBMemberHealthAlarm(alarm_base.BaseAlarm):
|
||||
]
|
||||
return translation_rules
|
||||
|
||||
def handle_create(self):
|
||||
props = self.get_alarm_props(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
alarm = self.client().alarm.create(props)
|
||||
self.resource_id_set(alarm['alarm_id'])
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
if prop_diff:
|
||||
new_props = json_snippet.properties(self.properties_schema,
|
||||
self.context)
|
||||
self.client().alarm.update(self.resource_id,
|
||||
self.get_alarm_props(new_props))
|
||||
class PrometheusAlarm(AodhBaseActionsMixin,
|
||||
alarm_base.BaseAlarm):
|
||||
"""A resource that implements Aodh alarm of type prometheus.
|
||||
|
||||
An alarm that evaluates threshold based on metric data fetched from
|
||||
Prometheus.
|
||||
"""
|
||||
|
||||
support_status = support.SupportStatus(version='22.0.0')
|
||||
|
||||
PROPERTIES = (
|
||||
COMPARISON_OPERATOR, QUERY, THRESHOLD,
|
||||
) = (
|
||||
'comparison_operator', 'query', 'threshold',
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
COMPARISON_OPERATOR: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('Operator used to compare specified statistic with threshold.'),
|
||||
constraints=[alarm_base.BaseAlarm.QF_OP_VALS],
|
||||
update_allowed=True
|
||||
),
|
||||
QUERY: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_('The PromQL query string to fetch metrics data '
|
||||
'from Prometheus.'),
|
||||
required=True,
|
||||
update_allowed=True
|
||||
),
|
||||
THRESHOLD: properties.Schema(
|
||||
properties.Schema.NUMBER,
|
||||
_('Threshold to evaluate against.'),
|
||||
required=True,
|
||||
update_allowed=True
|
||||
),
|
||||
}
|
||||
properties_schema.update(alarm_base.common_properties_schema)
|
||||
|
||||
alarm_type = 'prometheus'
|
||||
|
||||
def get_alarm_props(self, props):
|
||||
kwargs = self.actions_to_urls(props)
|
||||
kwargs['type'] = self.alarm_type
|
||||
return self._reformat_properties(kwargs)
|
||||
|
||||
def parse_live_resource_data(self, resource_properties,
|
||||
resource_data):
|
||||
record_reality = {}
|
||||
rule = self.alarm_type + '_rule'
|
||||
data = resource_data.get(rule).copy()
|
||||
data.update(resource_data)
|
||||
for key in self.properties_schema.keys():
|
||||
if key in alarm_base.INTERNAL_PROPERTIES:
|
||||
continue
|
||||
if self.properties_schema[key].update_allowed:
|
||||
record_reality.update({key: data.get(key)})
|
||||
return record_reality
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
@ -431,4 +467,5 @@ def resource_mapping():
|
||||
'OS::Aodh::CombinationAlarm': CombinationAlarm,
|
||||
'OS::Aodh::EventAlarm': EventAlarm,
|
||||
'OS::Aodh::LBMemberHealthAlarm': LBMemberHealthAlarm,
|
||||
'OS::Aodh::PrometheusAlarm': PrometheusAlarm,
|
||||
}
|
||||
|
@ -166,6 +166,28 @@ lbmemberhealth_alarm_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
prometheus_alarm_template = '''
|
||||
{
|
||||
"heat_template_version" : "wallaby",
|
||||
"description" : "Prometheus alarm test",
|
||||
"parameters" : {},
|
||||
"resources" : {
|
||||
"test_prometheus_alarm": {
|
||||
"type": "OS::Aodh::PrometheusAlarm",
|
||||
"properties": {
|
||||
"alarm_actions": [],
|
||||
"threshold": "10",
|
||||
"comparison_operator": "gt",
|
||||
"query": "some_metric{some_label='some_value'}"
|
||||
}
|
||||
},
|
||||
"signal_handler" : {
|
||||
"type" : "SignalResourceType"
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
FakeAodhAlarm = {'other_attrs': 'val',
|
||||
'alarm_id': 'foo'}
|
||||
|
||||
@ -830,3 +852,98 @@ class LBMemberHealthAlarmTest(common.HeatTestCase):
|
||||
|
||||
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertEqual(1, update_mock.call_count)
|
||||
|
||||
|
||||
class PrometheusAlarmTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(PrometheusAlarmTest, self).setUp()
|
||||
self.fa = mock.Mock()
|
||||
|
||||
def create_stack(self, template=None):
|
||||
if template is None:
|
||||
template = prometheus_alarm_template
|
||||
temp = template_format.parse(template)
|
||||
template = tmpl.Template(temp)
|
||||
ctx = utils.dummy_context()
|
||||
ctx.tenant = 'test_tenant'
|
||||
stack = parser.Stack(ctx, utils.random_name(), template,
|
||||
disable_rollback=True)
|
||||
stack.store()
|
||||
|
||||
self.patchobject(aodh.AodhClientPlugin,
|
||||
'_create').return_value = self.fa
|
||||
|
||||
self.patchobject(self.fa.alarm, 'create').return_value = FakeAodhAlarm
|
||||
|
||||
return stack
|
||||
|
||||
def _prepare_resource(self, for_check=True):
|
||||
snippet = template_format.parse(prometheus_alarm_template)
|
||||
self.stack = utils.parse_stack(snippet)
|
||||
res = self.stack['test_prometheus_alarm']
|
||||
if for_check:
|
||||
res.state_set(res.CREATE, res.COMPLETE)
|
||||
res.client = mock.Mock()
|
||||
mock_alarm = mock.Mock(enabled=True, state='ok')
|
||||
res.client().alarm.get.return_value = mock_alarm
|
||||
return res
|
||||
|
||||
def test_delete(self):
|
||||
test_stack = self.create_stack()
|
||||
rsrc = test_stack['test_prometheus_alarm']
|
||||
rsrc.resource_id = '12345'
|
||||
|
||||
self.patchobject(aodh.AodhClientPlugin, 'client',
|
||||
return_value=self.fa)
|
||||
self.patchobject(self.fa.alarm, 'delete')
|
||||
|
||||
self.assertEqual('12345', rsrc.handle_delete())
|
||||
self.assertEqual(1, self.fa.alarm.delete.call_count)
|
||||
|
||||
def test_check(self):
|
||||
res = self._prepare_resource()
|
||||
scheduler.TaskRunner(res.check)()
|
||||
self.assertEqual((res.CHECK, res.COMPLETE), res.state)
|
||||
|
||||
def test_check_alarm_failure(self):
|
||||
res = self._prepare_resource()
|
||||
res.client().alarm.get.side_effect = Exception('Boom')
|
||||
|
||||
self.assertRaises(exception.ResourceFailure,
|
||||
scheduler.TaskRunner(res.check))
|
||||
self.assertEqual((res.CHECK, res.FAILED), res.state)
|
||||
self.assertIn('Boom', res.status_reason)
|
||||
|
||||
def test_show_resource(self):
|
||||
res = self._prepare_resource(for_check=False)
|
||||
res.client().alarm.create.return_value = FakeAodhAlarm
|
||||
res.client().alarm.get.return_value = FakeAodhAlarm
|
||||
scheduler.TaskRunner(res.create)()
|
||||
self.assertEqual(FakeAodhAlarm, res.FnGetAtt('show'))
|
||||
|
||||
def test_update(self):
|
||||
test_stack = self.create_stack()
|
||||
update_mock = self.patchobject(self.fa.alarm, 'update')
|
||||
test_stack.create()
|
||||
rsrc = test_stack['test_prometheus_alarm']
|
||||
|
||||
update_props = copy.deepcopy(rsrc.properties.data)
|
||||
update_props.update({
|
||||
'comparison_operator': 'lt',
|
||||
'enabled': True,
|
||||
'threshold': '9',
|
||||
'insufficient_data_actions': [],
|
||||
'alarm_actions': [],
|
||||
'ok_actions': ['signal_handler'],
|
||||
'query': "some_other_metric{some_other_label='value'}"
|
||||
})
|
||||
|
||||
snippet = rsrc_defn.ResourceDefinition(rsrc.name,
|
||||
rsrc.type(),
|
||||
update_props)
|
||||
|
||||
scheduler.TaskRunner(rsrc.update, snippet)()
|
||||
|
||||
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertEqual(1, update_mock.call_count)
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add OS::Aodh::PrometheusAlarm resource to enable autoscaling
|
||||
with Prometheus instead of Gnocchi.
|
Loading…
Reference in New Issue
Block a user