Add heat resource for creating Aodh loadbalancer_member_health alarm type[1]
Aodh loadbalancer_member_health alarm was implemented in 654221[2]. This patch adds a corresponding heat resource for managing it's lifecycle. - Add new resource OS:Aodh:LBMemberHealthAlarm - Add unit test for LoadBalancerMemberHealthAlarms [1]: https://docs.openstack.org/aodh/latest/contributor/architecture.html#id5 [2]: https://review.opendev.org/#/c/654221/ Change-Id: I26582fbd0b980d848f7e4cdb8bb2c8833385fe9b Task: 34767
This commit is contained in:
parent
70d7a1bc86
commit
3fda946ff3
@ -19,6 +19,7 @@ from heat.engine import properties
|
||||
from heat.engine.resources import alarm_base
|
||||
from heat.engine.resources.openstack.heat import none_resource
|
||||
from heat.engine import support
|
||||
from heat.engine import translation
|
||||
|
||||
|
||||
class AodhAlarm(alarm_base.BaseAlarm):
|
||||
@ -328,9 +329,108 @@ class EventAlarm(alarm_base.BaseAlarm):
|
||||
self.get_alarm_props(new_props))
|
||||
|
||||
|
||||
class LBMemberHealthAlarm(alarm_base.BaseAlarm):
|
||||
"""A resource that implements a Loadbalancer Member Health Alarm.
|
||||
|
||||
Allows setting alarms based on the health of load balancer pool members,
|
||||
where the health of a member is determined by the member reporting an
|
||||
operating_status of ERROR beyond an initial grace period after creation
|
||||
(120 seconds by default).
|
||||
"""
|
||||
|
||||
alarm_type = "loadbalancer_member_health"
|
||||
|
||||
support_status = support.SupportStatus(version='13.0.0')
|
||||
|
||||
PROPERTIES = (
|
||||
POOL, STACK, AUTOSCALING_GROUP_ID
|
||||
) = (
|
||||
"pool", "stack", "autoscaling_group_id"
|
||||
)
|
||||
|
||||
RULE_PROPERTIES = (
|
||||
POOL_ID, STACK_ID
|
||||
) = (
|
||||
"pool_id", "stack_id"
|
||||
)
|
||||
|
||||
properties_schema = {
|
||||
POOL: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("Name or ID of the loadbalancer pool for which the health of "
|
||||
"each member will be evaluated."),
|
||||
update_allowed=True,
|
||||
required=True,
|
||||
),
|
||||
STACK: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("Name or ID of the root / top level Heat stack containing the "
|
||||
"loadbalancer pool and members. An update will be triggered "
|
||||
"on the root Stack if an unhealthy member is detected in the "
|
||||
"loadbalancer pool."),
|
||||
update_allowed=False,
|
||||
required=True,
|
||||
),
|
||||
AUTOSCALING_GROUP_ID: properties.Schema(
|
||||
properties.Schema.STRING,
|
||||
_("ID of the Heat autoscaling group that contains the "
|
||||
"loadbalancer members. Unhealthy members will be marked "
|
||||
"as such before an update is triggered on the root stack."),
|
||||
update_allowed=True,
|
||||
required=True,
|
||||
),
|
||||
}
|
||||
|
||||
properties_schema.update(alarm_base.common_properties_schema)
|
||||
|
||||
def get_alarm_props(self, props):
|
||||
"""Apply all relevant compatibility xforms."""
|
||||
kwargs = self.actions_to_urls(props)
|
||||
kwargs['type'] = self.alarm_type
|
||||
|
||||
for prop in (self.POOL, self.STACK, self.AUTOSCALING_GROUP_ID):
|
||||
if prop in kwargs:
|
||||
del kwargs[prop]
|
||||
|
||||
rule = {
|
||||
self.POOL_ID: props[self.POOL],
|
||||
self.STACK_ID: props[self.STACK],
|
||||
self.AUTOSCALING_GROUP_ID: props[self.AUTOSCALING_GROUP_ID]
|
||||
}
|
||||
|
||||
kwargs["loadbalancer_member_health_rule"] = rule
|
||||
return kwargs
|
||||
|
||||
def translation_rules(self, properties):
|
||||
translation_rules = [
|
||||
translation.TranslationRule(
|
||||
properties,
|
||||
translation.TranslationRule.RESOLVE,
|
||||
[self.POOL],
|
||||
client_plugin=self.client_plugin('octavia'),
|
||||
finder='get_pool'
|
||||
),
|
||||
]
|
||||
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))
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
'OS::Aodh::Alarm': AodhAlarm,
|
||||
'OS::Aodh::CombinationAlarm': CombinationAlarm,
|
||||
'OS::Aodh::EventAlarm': EventAlarm,
|
||||
'OS::Aodh::LBMemberHealthAlarm': LBMemberHealthAlarm,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
@ -20,6 +21,7 @@ import six
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine.clients.os import aodh
|
||||
from heat.engine.clients.os import octavia
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources.openstack.aodh import alarm
|
||||
from heat.engine import rsrc_defn
|
||||
@ -142,6 +144,29 @@ event_alarm_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
lbmemberhealth_alarm_template = '''
|
||||
{
|
||||
"heat_template_version" : "newton",
|
||||
"description" : "Loadbalancer member health alarm test",
|
||||
"parameters" : {},
|
||||
"resources" : {
|
||||
"test_loadbalancer_member_health_alarm": {
|
||||
"type": "OS::Aodh::LBMemberHealthAlarm",
|
||||
"properties": {
|
||||
"description": "Something something dark side",
|
||||
"alarm_actions": ["trust+heat://"],
|
||||
"repeat_actions": false,
|
||||
"pool": "12345",
|
||||
"stack": "13579",
|
||||
"autoscaling_group_id": "02468"
|
||||
}
|
||||
},
|
||||
"signal_handler" : {
|
||||
"type" : "SignalResourceType"
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
FakeAodhAlarm = {'other_attrs': 'val',
|
||||
'alarm_id': 'foo'}
|
||||
@ -709,3 +734,101 @@ class EventAlarmTest(common.HeatTestCase):
|
||||
res.client().alarm.get.return_value = FakeAodhAlarm
|
||||
scheduler.TaskRunner(res.create)()
|
||||
self.assertEqual(FakeAodhAlarm, res.FnGetAtt('show'))
|
||||
|
||||
|
||||
class LBMemberHealthAlarmTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LBMemberHealthAlarmTest, self).setUp()
|
||||
self.fa = mock.Mock()
|
||||
self.patchobject(
|
||||
octavia.OctaviaClientPlugin, 'get_pool').return_value = "9999"
|
||||
|
||||
def create_stack(self, template=None):
|
||||
|
||||
if template is None:
|
||||
template = lbmemberhealth_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(lbmemberhealth_alarm_template)
|
||||
self.stack = utils.parse_stack(snippet)
|
||||
res = self.stack['test_loadbalancer_member_health_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_loadbalancer_member_health_alarm']
|
||||
|
||||
self.patchobject(aodh.AodhClientPlugin, 'client',
|
||||
return_value=self.fa)
|
||||
self.patchobject(self.fa.alarm, 'delete')
|
||||
rsrc.resource_id = '12345'
|
||||
|
||||
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_loadbalancer_member_health_alarm']
|
||||
|
||||
update_props = copy.deepcopy(rsrc.properties.data)
|
||||
update_props.update({
|
||||
"enabled": True,
|
||||
"description": "",
|
||||
"insufficient_data_actions": [],
|
||||
"alarm_actions": [],
|
||||
"ok_actions": ["signal_handler"],
|
||||
"pool": "0000",
|
||||
"autoscaling_group_id": "2222"
|
||||
})
|
||||
|
||||
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,4 @@
|
||||
---
|
||||
features:
|
||||
- OS::Aodh::LBMemberHealthAlarm resource plugin is added to manage
|
||||
Aodh loadbalancer_member_health alarm.
|
Loading…
Reference in New Issue
Block a user