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
|
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 implements alarming service of Aodh.
|
||||||
|
|
||||||
A resource that allows for the setting alarms based on threshold evaluation
|
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
|
kwargs['threshold_rule'] = rule
|
||||||
return kwargs
|
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):
|
def parse_live_resource_data(self, resource_properties, resource_data):
|
||||||
record_reality = {}
|
record_reality = {}
|
||||||
threshold_data = resource_data.get('threshold_rule').copy()
|
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.
|
"""A resource that implements event alarms.
|
||||||
|
|
||||||
Allows users to define alarms which can be evaluated based on events
|
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
|
kwargs['event_rule'] = rule
|
||||||
return kwargs
|
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):
|
class LBMemberHealthAlarm(AodhBaseActionsMixin, alarm_base.BaseAlarm):
|
||||||
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):
|
|
||||||
"""A resource that implements a Loadbalancer Member Health Alarm.
|
"""A resource that implements a Loadbalancer Member Health Alarm.
|
||||||
|
|
||||||
Allows setting alarms based on the health of load balancer pool members,
|
Allows setting alarms based on the health of load balancer pool members,
|
||||||
@ -411,18 +400,65 @@ class LBMemberHealthAlarm(alarm_base.BaseAlarm):
|
|||||||
]
|
]
|
||||||
return translation_rules
|
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):
|
class PrometheusAlarm(AodhBaseActionsMixin,
|
||||||
if prop_diff:
|
alarm_base.BaseAlarm):
|
||||||
new_props = json_snippet.properties(self.properties_schema,
|
"""A resource that implements Aodh alarm of type prometheus.
|
||||||
self.context)
|
|
||||||
self.client().alarm.update(self.resource_id,
|
An alarm that evaluates threshold based on metric data fetched from
|
||||||
self.get_alarm_props(new_props))
|
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():
|
def resource_mapping():
|
||||||
@ -431,4 +467,5 @@ def resource_mapping():
|
|||||||
'OS::Aodh::CombinationAlarm': CombinationAlarm,
|
'OS::Aodh::CombinationAlarm': CombinationAlarm,
|
||||||
'OS::Aodh::EventAlarm': EventAlarm,
|
'OS::Aodh::EventAlarm': EventAlarm,
|
||||||
'OS::Aodh::LBMemberHealthAlarm': LBMemberHealthAlarm,
|
'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',
|
FakeAodhAlarm = {'other_attrs': 'val',
|
||||||
'alarm_id': 'foo'}
|
'alarm_id': 'foo'}
|
||||||
|
|
||||||
@ -830,3 +852,98 @@ class LBMemberHealthAlarmTest(common.HeatTestCase):
|
|||||||
|
|
||||||
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
|
||||||
self.assertEqual(1, update_mock.call_count)
|
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