Migrate to aodh for OS::Ceilometer::Alarm

This changes:
1. use aodhclient to manage OS::Ceilometer::Alarm
resource, including create, update, delete, check, suspend,
resume and show.
2. rename OS::Ceilometer::Alarm to OS::Aodh::Alarm
3. considering to compatible with old templates with resource
OS::Ceilometer::Alarm, set resource_registry to map Ceilometer alarm
to Aodh alarm

Blueprint migrate-to-use-aodh-for-alarms

Change-Id: I6e2d14f15a345b927b53adc237cf2bf4010842f0
This commit is contained in:
huangtianhua 2016-06-08 15:50:39 +08:00
parent 47262c079d
commit 4a79f7ca53
11 changed files with 344 additions and 376 deletions

View File

@ -5,5 +5,6 @@ resource_registry:
# Choose your implementation of AWS::CloudWatch::Alarm # Choose your implementation of AWS::CloudWatch::Alarm
"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml" "AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
#"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm" #"AWS::CloudWatch::Alarm": "OS::Heat::CWLiteAlarm"
"OS::Metering::Alarm": "OS::Ceilometer::Alarm" "OS::Metering::Alarm": "OS::Aodh::Alarm"
"AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml" "AWS::RDS::DBInstance": "file:///etc/heat/templates/AWS_RDS_DBInstance.yaml"
"OS::Ceilometer::Alarm": "OS::Aodh::Alarm"

View File

@ -60,7 +60,7 @@ Mappings:
Resources: Resources:
__alarm__: __alarm__:
Type: OS::Ceilometer::Alarm Type: OS::Aodh::Alarm
Properties: Properties:
description: description:
Ref: AlarmDescription Ref: AlarmDescription

View File

@ -0,0 +1,211 @@
#
# 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
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from heat.common.i18n import _
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine import support
COMMON_PROPERTIES = (
ALARM_ACTIONS, OK_ACTIONS, REPEAT_ACTIONS,
INSUFFICIENT_DATA_ACTIONS, DESCRIPTION, ENABLED, TIME_CONSTRAINTS,
SEVERITY,
) = (
'alarm_actions', 'ok_actions', 'repeat_actions',
'insufficient_data_actions', 'description', 'enabled', 'time_constraints',
'severity',
)
_TIME_CONSTRAINT_KEYS = (
NAME, START, DURATION, TIMEZONE, TIME_CONSTRAINT_DESCRIPTION,
) = (
'name', 'start', 'duration', 'timezone', 'description',
)
common_properties_schema = {
DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_('Description for the alarm.'),
update_allowed=True
),
ENABLED: properties.Schema(
properties.Schema.BOOLEAN,
_('True if alarm evaluation/actioning is enabled.'),
default='true',
update_allowed=True
),
ALARM_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'alarm.'),
update_allowed=True
),
OK_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'ok.'),
update_allowed=True
),
INSUFFICIENT_DATA_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'insufficient-data.'),
update_allowed=True
),
REPEAT_ACTIONS: properties.Schema(
properties.Schema.BOOLEAN,
_("False to trigger actions when the threshold is reached AND "
"the alarm's state has changed. By default, actions are called "
"each time the threshold is reached."),
default='true',
update_allowed=True
),
SEVERITY: properties.Schema(
properties.Schema.STRING,
_('Severity of the alarm.'),
default='low',
constraints=[
constraints.AllowedValues(['low', 'moderate', 'critical'])
],
update_allowed=True,
support_status=support.SupportStatus(version='5.0.0'),
),
TIME_CONSTRAINTS: properties.Schema(
properties.Schema.LIST,
_('Describe time constraints for the alarm. '
'Only evaluate the alarm if the time at evaluation '
'is within this time constraint. Start point(s) of '
'the constraint are specified with a cron expression, '
'whereas its duration is given in seconds.'
),
schema=properties.Schema(
properties.Schema.MAP,
schema={
NAME: properties.Schema(
properties.Schema.STRING,
_("Name for the time constraint."),
required=True
),
START: properties.Schema(
properties.Schema.STRING,
_("Start time for the time constraint. "
"A CRON expression property."),
constraints=[
constraints.CustomConstraint(
'cron_expression')
],
required=True
),
TIME_CONSTRAINT_DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_("Description for the time constraint."),
),
DURATION: properties.Schema(
properties.Schema.INTEGER,
_("Duration for the time constraint."),
constraints=[
constraints.Range(min=0)
],
required=True
),
TIMEZONE: properties.Schema(
properties.Schema.STRING,
_("Timezone for the time constraint "
"(eg. 'Taiwan/Taipei', 'Europe/Amsterdam')."),
constraints=[
constraints.CustomConstraint('timezone')
],
)
}
),
support_status=support.SupportStatus(version='5.0.0'),
default=[],
)
}
NOVA_METERS = ['instance', 'memory', 'memory.usage',
'cpu', 'cpu_util', 'vcpus',
'disk.read.requests', 'disk.read.requests.rate',
'disk.write.requests', 'disk.write.requests.rate',
'disk.read.bytes', 'disk.read.bytes.rate',
'disk.write.bytes', 'disk.write.bytes.rate',
'disk.device.read.requests', 'disk.device.read.requests.rate',
'disk.device.write.requests', 'disk.device.write.requests.rate',
'disk.device.read.bytes', 'disk.device.read.bytes.rate',
'disk.device.write.bytes', 'disk.device.write.bytes.rate',
'disk.root.size', 'disk.ephemeral.size',
'network.incoming.bytes', 'network.incoming.bytes.rate',
'network.outgoing.bytes', 'network.outgoing.bytes.rate',
'network.incoming.packets', 'network.incoming.packets.rate',
'network.outgoing.packets', 'network.outgoing.packets.rate']
class BaseAlarm(resource.Resource):
"""Base Alarm Manager."""
default_client_name = 'aodh'
entity = 'alarm'
alarm_type = 'threshold'
def actions_to_urls(self, props):
kwargs = {}
for k, v in iter(props.items()):
if k in [ALARM_ACTIONS, OK_ACTIONS,
INSUFFICIENT_DATA_ACTIONS] and v is not None:
kwargs[k] = []
for act in v:
# if the action is a resource name
# we ask the destination resource for an alarm url.
# the template writer should really do this in the
# template if possible with:
# {Fn::GetAtt: ['MyAction', 'AlarmUrl']}
if act in self.stack:
url = self.stack[act].FnGetAtt('AlarmUrl')
kwargs[k].append(url)
else:
if act:
kwargs[k].append(act)
else:
kwargs[k] = v
return kwargs
def _reformat_properties(self, props):
rule = {}
for name in self.PROPERTIES:
value = props.pop(name, None)
if value:
rule[name] = value
if rule:
props['%s_rule' % self.alarm_type] = rule
return props
def handle_suspend(self):
if self.resource_id is not None:
alarm_update = {'enabled': False}
self.client().alarm.update(self.resource_id,
alarm_update)
def handle_resume(self):
if self.resource_id is not None:
alarm_update = {'enabled': True}
self.client().alarm.update(self.resource_id,
alarm_update)
def handle_check(self):
self.client().alarm.get(self.resource_id)

View File

@ -17,172 +17,13 @@ from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import constraints from heat.engine import constraints
from heat.engine import properties from heat.engine import properties
from heat.engine import resource from heat.engine.resources import alarm_base
from heat.engine import support from heat.engine import support
from heat.engine import watchrule from heat.engine import watchrule
COMMON_PROPERTIES = ( class AodhAlarm(alarm_base.BaseAlarm):
ALARM_ACTIONS, OK_ACTIONS, REPEAT_ACTIONS, """A resource that implements alarming service of Aodh.
INSUFFICIENT_DATA_ACTIONS, DESCRIPTION, ENABLED, TIME_CONSTRAINTS,
SEVERITY,
) = (
'alarm_actions', 'ok_actions', 'repeat_actions',
'insufficient_data_actions', 'description', 'enabled', 'time_constraints',
'severity',
)
_TIME_CONSTRAINT_KEYS = (
NAME, START, DURATION, TIMEZONE, TIME_CONSTRAINT_DESCRIPTION,
) = (
'name', 'start', 'duration', 'timezone', 'description',
)
common_properties_schema = {
DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_('Description for the alarm.'),
update_allowed=True
),
ENABLED: properties.Schema(
properties.Schema.BOOLEAN,
_('True if alarm evaluation/actioning is enabled.'),
default='true',
update_allowed=True
),
ALARM_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'alarm.'),
update_allowed=True
),
OK_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'ok.'),
update_allowed=True
),
INSUFFICIENT_DATA_ACTIONS: properties.Schema(
properties.Schema.LIST,
_('A list of URLs (webhooks) to invoke when state transitions to '
'insufficient-data.'),
update_allowed=True
),
REPEAT_ACTIONS: properties.Schema(
properties.Schema.BOOLEAN,
_("False to trigger actions when the threshold is reached AND "
"the alarm's state has changed. By default, actions are called "
"each time the threshold is reached."),
default='true',
update_allowed=True
),
SEVERITY: properties.Schema(
properties.Schema.STRING,
_('Severity of the alarm.'),
default='low',
constraints=[
constraints.AllowedValues(['low', 'moderate', 'critical'])
],
update_allowed=True,
support_status=support.SupportStatus(version='5.0.0'),
),
TIME_CONSTRAINTS: properties.Schema(
properties.Schema.LIST,
_('Describe time constraints for the alarm. '
'Only evaluate the alarm if the time at evaluation '
'is within this time constraint. Start point(s) of '
'the constraint are specified with a cron expression, '
'whereas its duration is given in seconds.'
),
schema=properties.Schema(
properties.Schema.MAP,
schema={
NAME: properties.Schema(
properties.Schema.STRING,
_("Name for the time constraint."),
required=True
),
START: properties.Schema(
properties.Schema.STRING,
_("Start time for the time constraint. "
"A CRON expression property."),
constraints=[
constraints.CustomConstraint(
'cron_expression')
],
required=True
),
TIME_CONSTRAINT_DESCRIPTION: properties.Schema(
properties.Schema.STRING,
_("Description for the time constraint."),
),
DURATION: properties.Schema(
properties.Schema.INTEGER,
_("Duration for the time constraint."),
constraints=[
constraints.Range(min=0)
],
required=True
),
TIMEZONE: properties.Schema(
properties.Schema.STRING,
_("Timezone for the time constraint "
"(eg. 'Taiwan/Taipei', 'Europe/Amsterdam')."),
constraints=[
constraints.CustomConstraint('timezone')
],
)
}
),
support_status=support.SupportStatus(version='5.0.0'),
default=[],
)
}
NOVA_METERS = ['instance', 'memory', 'memory.usage',
'cpu', 'cpu_util', 'vcpus',
'disk.read.requests', 'disk.read.requests.rate',
'disk.write.requests', 'disk.write.requests.rate',
'disk.read.bytes', 'disk.read.bytes.rate',
'disk.write.bytes', 'disk.write.bytes.rate',
'disk.device.read.requests', 'disk.device.read.requests.rate',
'disk.device.write.requests', 'disk.device.write.requests.rate',
'disk.device.read.bytes', 'disk.device.read.bytes.rate',
'disk.device.write.bytes', 'disk.device.write.bytes.rate',
'disk.root.size', 'disk.ephemeral.size',
'network.incoming.bytes', 'network.incoming.bytes.rate',
'network.outgoing.bytes', 'network.outgoing.bytes.rate',
'network.incoming.packets', 'network.incoming.packets.rate',
'network.outgoing.packets', 'network.outgoing.packets.rate']
def actions_to_urls(stack, properties):
kwargs = {}
for k, v in iter(properties.items()):
if k in [ALARM_ACTIONS, OK_ACTIONS,
INSUFFICIENT_DATA_ACTIONS] and v is not None:
kwargs[k] = []
for act in v:
# if the action is a resource name
# we ask the destination resource for an alarm url.
# the template writer should really do this in the
# template if possible with:
# {Fn::GetAtt: ['MyAction', 'AlarmUrl']}
if act in stack:
url = stack[act].FnGetAtt('AlarmUrl')
kwargs[k].append(url)
else:
if act:
kwargs[k].append(act)
else:
kwargs[k] = v
return kwargs
class CeilometerAlarm(resource.Resource):
"""A resource that implements alarming service of Ceilometer.
A resource that allows for the setting alarms based on threshold evaluation A resource that allows for the setting alarms based on threshold evaluation
for a collection of samples. Also, you can define actions to take if state for a collection of samples. Also, you can define actions to take if state
@ -290,18 +131,15 @@ class CeilometerAlarm(resource.Resource):
) )
) )
} }
properties_schema.update(common_properties_schema)
default_client_name = 'ceilometer' properties_schema.update(alarm_base.common_properties_schema)
entity = 'alarms' def get_alarm_props(self, props):
def cfn_to_ceilometer(self, stack, properties):
"""Apply all relevant compatibility xforms.""" """Apply all relevant compatibility xforms."""
kwargs = actions_to_urls(stack, properties) kwargs = self.actions_to_urls(props)
kwargs['type'] = 'threshold' kwargs['type'] = self.alarm_type
if kwargs.get(self.METER_NAME) in NOVA_METERS: if kwargs.get(self.METER_NAME) in alarm_base.NOVA_METERS:
prefix = 'user_metadata.' prefix = 'user_metadata.'
else: else:
prefix = 'metering.' prefix = 'metering.'
@ -312,8 +150,8 @@ class CeilometerAlarm(resource.Resource):
if field in kwargs: if field in kwargs:
rule[field] = kwargs[field] rule[field] = kwargs[field]
del kwargs[field] del kwargs[field]
mmd = properties.get(self.MATCHING_METADATA) or {} mmd = props.get(self.MATCHING_METADATA) or {}
query = properties.get(self.QUERY) or [] query = props.get(self.QUERY) or []
# make sure the matching_metadata appears in the query like this: # make sure the matching_metadata appears in the query like this:
# {field: metadata.$prefix.x, ...} # {field: metadata.$prefix.x, ...}
@ -339,11 +177,10 @@ class CeilometerAlarm(resource.Resource):
return kwargs return kwargs
def handle_create(self): def handle_create(self):
props = self.cfn_to_ceilometer(self.stack, props = self.get_alarm_props(self.properties)
self.properties)
props['name'] = self.physical_resource_name() props['name'] = self.physical_resource_name()
alarm = self.client().alarms.create(**props) alarm = self.client().alarm.create(props)
self.resource_id_set(alarm.alarm_id) self.resource_id_set(alarm['alarm_id'])
# the watchrule below is for backwards compatibility. # the watchrule below is for backwards compatibility.
# 1) so we don't create watch tasks unnecessarily # 1) so we don't create watch tasks unnecessarily
@ -358,21 +195,11 @@ class CeilometerAlarm(resource.Resource):
def handle_update(self, json_snippet, tmpl_diff, prop_diff): def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if prop_diff: if prop_diff:
kwargs = {'alarm_id': self.resource_id} kwargs = {}
kwargs.update(self.properties) kwargs.update(self.properties)
kwargs.update(prop_diff) kwargs.update(prop_diff)
alarms_client = self.client().alarms self.client().alarm.update(self.resource_id,
alarms_client.update(**self.cfn_to_ceilometer(self.stack, kwargs)) self.get_alarm_props(kwargs))
def handle_suspend(self):
if self.resource_id is not None:
self.client().alarms.update(alarm_id=self.resource_id,
enabled=False)
def handle_resume(self):
if self.resource_id is not None:
self.client().alarms.update(alarm_id=self.resource_id,
enabled=True)
def handle_delete(self): def handle_delete(self):
try: try:
@ -382,45 +209,37 @@ class CeilometerAlarm(resource.Resource):
except exception.EntityNotFound: except exception.EntityNotFound:
pass pass
return super(CeilometerAlarm, self).handle_delete() return super(AodhAlarm, self).handle_delete()
def handle_check(self): def handle_check(self):
watch_name = self.physical_resource_name() watch_name = self.physical_resource_name()
watchrule.WatchRule.load(self.context, watch_name=watch_name) watchrule.WatchRule.load(self.context, watch_name=watch_name)
self.client().alarms.get(self.resource_id) self.client().alarm.get(self.resource_id)
def _show_resource(self):
return self.client().alarm.get(self.resource_id)
class BaseCeilometerAlarm(resource.Resource): class BaseCeilometerAlarm(alarm_base.BaseAlarm):
default_client_name = 'ceilometer' default_client_name = 'ceilometer'
entity = 'alarms' entity = 'alarms'
def handle_create(self): def handle_create(self):
properties = actions_to_urls(self.stack, props = self.actions_to_urls(self.properties)
self.properties) props['name'] = self.physical_resource_name()
properties['name'] = self.physical_resource_name() props['type'] = self.alarm_type
properties['type'] = self.ceilometer_alarm_type
alarm = self.client().alarms.create( alarm = self.client().alarms.create(
**self._reformat_properties(properties)) **self._reformat_properties(props))
self.resource_id_set(alarm.alarm_id) self.resource_id_set(alarm.alarm_id)
def _reformat_properties(self, properties):
rule = {}
for name in self.PROPERTIES:
value = properties.pop(name, None)
if value:
rule[name] = value
if rule:
properties['%s_rule' % self.ceilometer_alarm_type] = rule
return properties
def handle_update(self, json_snippet, tmpl_diff, prop_diff): def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if prop_diff: if prop_diff:
kwargs = {'alarm_id': self.resource_id} kwargs = {'alarm_id': self.resource_id}
kwargs.update(prop_diff) kwargs.update(prop_diff)
alarms_client = self.client().alarms alarms_client = self.client().alarms
alarms_client.update(**self._reformat_properties( alarms_client.update(**self._reformat_properties(
actions_to_urls(self.stack, kwargs))) self.actions_to_urls(kwargs)))
def handle_suspend(self): def handle_suspend(self):
self.client().alarms.update( self.client().alarms.update(
@ -463,13 +282,13 @@ class CombinationAlarm(BaseCeilometerAlarm):
constraints=[constraints.AllowedValues(['and', 'or'])], constraints=[constraints.AllowedValues(['and', 'or'])],
update_allowed=True) update_allowed=True)
} }
properties_schema.update(common_properties_schema) properties_schema.update(alarm_base.common_properties_schema)
ceilometer_alarm_type = 'combination' alarm_type = 'combination'
def resource_mapping(): def resource_mapping():
return { return {
'OS::Ceilometer::Alarm': CeilometerAlarm, 'OS::Aodh::Alarm': AodhAlarm,
'OS::Ceilometer::CombinationAlarm': CombinationAlarm, 'OS::Ceilometer::CombinationAlarm': CombinationAlarm,
} }

View File

@ -15,6 +15,7 @@
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine import constraints from heat.engine import constraints
from heat.engine import properties from heat.engine import properties
from heat.engine.resources import alarm_base
from heat.engine.resources.openstack.ceilometer import alarm from heat.engine.resources.openstack.ceilometer import alarm
from heat.engine import support from heat.engine import support
@ -103,9 +104,9 @@ class CeilometerGnocchiResourcesAlarm(alarm.BaseCeilometerAlarm):
), ),
} }
properties_schema.update(common_gnocchi_properties_schema) properties_schema.update(common_gnocchi_properties_schema)
properties_schema.update(alarm.common_properties_schema) properties_schema.update(alarm_base.common_properties_schema)
ceilometer_alarm_type = 'gnocchi_resources_threshold' alarm_type = 'gnocchi_resources_threshold'
class CeilometerGnocchiAggregationByMetricsAlarm( class CeilometerGnocchiAggregationByMetricsAlarm(
@ -130,9 +131,9 @@ class CeilometerGnocchiAggregationByMetricsAlarm(
), ),
} }
properties_schema.update(common_gnocchi_properties_schema) properties_schema.update(common_gnocchi_properties_schema)
properties_schema.update(alarm.common_properties_schema) properties_schema.update(alarm_base.common_properties_schema)
ceilometer_alarm_type = 'gnocchi_aggregation_by_metrics_threshold' alarm_type = 'gnocchi_aggregation_by_metrics_threshold'
class CeilometerGnocchiAggregationByResourcesAlarm( class CeilometerGnocchiAggregationByResourcesAlarm(
@ -175,9 +176,9 @@ class CeilometerGnocchiAggregationByResourcesAlarm(
} }
properties_schema.update(common_gnocchi_properties_schema) properties_schema.update(common_gnocchi_properties_schema)
properties_schema.update(alarm.common_properties_schema) properties_schema.update(alarm_base.common_properties_schema)
ceilometer_alarm_type = 'gnocchi_aggregation_by_resources_threshold' alarm_type = 'gnocchi_aggregation_by_resources_threshold'
def resource_mapping(): def resource_mapping():

View File

@ -143,7 +143,7 @@ class CloudWatchAlarm(resource.Resource):
support_status = support.SupportStatus( support_status = support.SupportStatus(
status=support.HIDDEN, status=support.HIDDEN,
message=_('OS::Heat::CWLiteAlarm is deprecated, ' message=_('OS::Heat::CWLiteAlarm is deprecated, '
'use OS::Ceilometer::Alarm instead.'), 'use OS::Aodh::Alarm instead.'),
version='5.0.0', version='5.0.0',
previous_status=support.SupportStatus( previous_status=support.SupportStatus(
status=support.DEPRECATED, status=support.DEPRECATED,

View File

@ -20,8 +20,8 @@ import six
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
from heat.engine.clients.os import aodh
from heat.engine.clients.os import ceilometer from heat.engine.clients.os import ceilometer
from heat.engine import properties as props
from heat.engine.resources.openstack.ceilometer import alarm from heat.engine.resources.openstack.ceilometer import alarm
from heat.engine import rsrc_defn from heat.engine import rsrc_defn
from heat.engine import scheduler from heat.engine import scheduler
@ -39,7 +39,7 @@ alarm_template = '''
"Parameters" : {}, "Parameters" : {},
"Resources" : { "Resources" : {
"MEMAlarmHigh": { "MEMAlarmHigh": {
"Type": "OS::Ceilometer::Alarm", "Type": "OS::Aodh::Alarm",
"Properties": { "Properties": {
"description": "Scale-up if MEM > 50% for 1 minute", "description": "Scale-up if MEM > 50% for 1 minute",
"meter_name": "MemoryUtilization", "meter_name": "MemoryUtilization",
@ -66,7 +66,7 @@ alarm_template_with_time_constraints = '''
"Parameters" : {}, "Parameters" : {},
"Resources" : { "Resources" : {
"MEMAlarmHigh": { "MEMAlarmHigh": {
"Type": "OS::Ceilometer::Alarm", "Type": "OS::Aodh::Alarm",
"Properties": { "Properties": {
"description": "Scale-up if MEM > 50% for 1 minute", "description": "Scale-up if MEM > 50% for 1 minute",
"meter_name": "MemoryUtilization", "meter_name": "MemoryUtilization",
@ -100,7 +100,7 @@ not_string_alarm_template = '''
"Parameters" : {}, "Parameters" : {},
"Resources" : { "Resources" : {
"MEMAlarmHigh": { "MEMAlarmHigh": {
"Type": "OS::Ceilometer::Alarm", "Type": "OS::Aodh::Alarm",
"Properties": { "Properties": {
"description": "Scale-up if MEM > 50% for 1 minute", "description": "Scale-up if MEM > 50% for 1 minute",
"meter_name": "MemoryUtilization", "meter_name": "MemoryUtilization",
@ -146,9 +146,13 @@ class FakeCeilometerAlarm(object):
self.to_dict = lambda: {'attr': 'val'} self.to_dict = lambda: {'attr': 'val'}
class CeilometerAlarmTest(common.HeatTestCase): FakeAodhAlarm = {'other_attrs': 'val',
'alarm_id': 'foo'}
class AodhAlarmTest(common.HeatTestCase):
def setUp(self): def setUp(self):
super(CeilometerAlarmTest, self).setUp() super(AodhAlarmTest, self).setUp()
self.fa = mock.Mock() self.fa = mock.Mock()
def create_stack(self, template=None, time_constraints=None): def create_stack(self, template=None, time_constraints=None):
@ -162,46 +166,14 @@ class CeilometerAlarmTest(common.HeatTestCase):
disable_rollback=True) disable_rollback=True)
stack.store() stack.store()
self.m.StubOutWithMock(ceilometer.CeilometerClientPlugin, '_create') self.patchobject(aodh.AodhClientPlugin,
ceilometer.CeilometerClientPlugin._create().AndReturn(self.fa) '_create').return_value = self.fa
al = copy.deepcopy(temp['Resources']['MEMAlarmHigh']['Properties']) al = copy.deepcopy(temp['Resources']['MEMAlarmHigh']['Properties'])
al['description'] = mox.IgnoreArg()
al['name'] = mox.IgnoreArg()
al['alarm_actions'] = mox.IgnoreArg()
al['insufficient_data_actions'] = None
al['ok_actions'] = None
al['repeat_actions'] = True
al['enabled'] = True
al['time_constraints'] = time_constraints if time_constraints else [] al['time_constraints'] = time_constraints if time_constraints else []
al['severity'] = 'low'
rule = dict( self.patchobject(self.fa.alarm, 'create').return_value = FakeAodhAlarm
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:
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=six.text_type(v)))
if 'matching_metadata' in al:
del al['matching_metadata']
if query:
rule['query'] = mox.SameElementsAs(query)
al['threshold_rule'] = rule
al['type'] = 'threshold'
self.m.StubOutWithMock(self.fa.alarms, 'create')
self.fa.alarms.create(**al).AndReturn(FakeCeilometerAlarm())
return stack return stack
def test_mem_alarm_high_update_no_replace(self): def test_mem_alarm_high_update_no_replace(self):
@ -214,37 +186,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
properties['matching_metadata'] = {'a': 'v'} properties['matching_metadata'] = {'a': 'v'}
properties['query'] = [dict(field='b', op='eq', value='w')] properties['query'] = [dict(field='b', op='eq', value='w')]
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
self.m.StubOutWithMock(self.fa.alarms, 'update')
schema = props.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 and k not in exns)
al2['time_constraints'] = mox.IgnoreArg()
al2['alarm_id'] = mox.IgnoreArg()
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() update_mock = self.patchobject(self.fa.alarm, 'update')
self.stack.create()
rsrc = self.stack['MEMAlarmHigh']
properties = copy.copy(rsrc.properties.data) test_stack.create()
properties.update({ rsrc = test_stack['MEMAlarmHigh']
update_props = copy.deepcopy(rsrc.properties.data)
update_props.update({
'comparison_operator': 'lt', 'comparison_operator': 'lt',
'description': 'fruity', 'description': 'fruity',
'evaluation_periods': '2', 'evaluation_periods': '2',
@ -259,13 +209,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
'matching_metadata': {'x': 'y'}, 'matching_metadata': {'x': 'y'},
'query': [dict(field='c', op='ne', value='z')] 'query': [dict(field='c', op='ne', value='z')]
}) })
snippet = rsrc_defn.ResourceDefinition(rsrc.name, snippet = rsrc_defn.ResourceDefinition(rsrc.name,
rsrc.type(), rsrc.type(),
properties) update_props)
scheduler.TaskRunner(rsrc.update, snippet)() scheduler.TaskRunner(rsrc.update, snippet)()
self.m.VerifyAll() self.assertEqual((rsrc.UPDATE, rsrc.COMPLETE), rsrc.state)
self.assertEqual(1, update_mock.call_count)
def test_mem_alarm_high_update_replace(self): def test_mem_alarm_high_update_replace(self):
"""Tests resource replacing when changing non-updatable properties.""" """Tests resource replacing when changing non-updatable properties."""
@ -275,11 +227,10 @@ class CeilometerAlarmTest(common.HeatTestCase):
properties['alarm_actions'] = ['signal_handler'] properties['alarm_actions'] = ['signal_handler']
properties['matching_metadata'] = {'a': 'v'} properties['matching_metadata'] = {'a': 'v'}
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
self.m.ReplayAll() test_stack.create()
self.stack.create() rsrc = test_stack['MEMAlarmHigh']
rsrc = self.stack['MEMAlarmHigh']
properties = copy.copy(rsrc.properties.data) properties = copy.copy(rsrc.properties.data)
properties['meter_name'] = 'temp' properties['meter_name'] = 'temp'
@ -290,40 +241,34 @@ class CeilometerAlarmTest(common.HeatTestCase):
updater = scheduler.TaskRunner(rsrc.update, snippet) updater = scheduler.TaskRunner(rsrc.update, snippet)
self.assertRaises(exception.UpdateReplace, updater) self.assertRaises(exception.UpdateReplace, updater)
self.m.VerifyAll()
def test_mem_alarm_suspend_resume(self): def test_mem_alarm_suspend_resume(self):
"""Tests suspending and resuming of the alarm. """Tests suspending and resuming of the alarm.
Make sure that the Alarm resource gets disabled on suspend Make sure that the Alarm resource gets disabled on suspend
and re-enabled on resume. and re-enabled on resume.
""" """
self.stack = self.create_stack() test_stack = self.create_stack()
self.m.StubOutWithMock(self.fa.alarms, 'update') update_mock = self.patchobject(self.fa.alarm, 'update')
al_suspend = {'alarm_id': mox.IgnoreArg(), al_suspend = {'enabled': False}
'enabled': False} al_resume = {'enabled': True}
self.fa.alarms.update(**al_suspend).AndReturn(None)
al_resume = {'alarm_id': mox.IgnoreArg(),
'enabled': True}
self.fa.alarms.update(**al_resume).AndReturn(None)
self.m.ReplayAll()
self.stack.create() test_stack.create()
rsrc = self.stack['MEMAlarmHigh'] rsrc = test_stack['MEMAlarmHigh']
scheduler.TaskRunner(rsrc.suspend)() scheduler.TaskRunner(rsrc.suspend)()
self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state) self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state)
scheduler.TaskRunner(rsrc.resume)() scheduler.TaskRunner(rsrc.resume)()
self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state) self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state)
self.m.VerifyAll() update_mock.assert_has_calls((
mock.call('foo', al_suspend),
mock.call('foo', al_resume)))
def test_mem_alarm_high_correct_int_parameters(self): def test_mem_alarm_high_correct_int_parameters(self):
self.stack = self.create_stack(not_string_alarm_template) test_stack = self.create_stack(not_string_alarm_template)
self.m.ReplayAll() test_stack.create()
self.stack.create() rsrc = test_stack['MEMAlarmHigh']
rsrc = self.stack['MEMAlarmHigh']
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.assertIsNone(rsrc.validate()) self.assertIsNone(rsrc.validate())
@ -331,20 +276,18 @@ class CeilometerAlarmTest(common.HeatTestCase):
self.assertIsInstance(rsrc.properties['period'], int) self.assertIsInstance(rsrc.properties['period'], int)
self.assertIsInstance(rsrc.properties['threshold'], int) self.assertIsInstance(rsrc.properties['threshold'], int)
self.m.VerifyAll()
def test_alarm_metadata_prefix(self): def test_alarm_metadata_prefix(self):
t = template_format.parse(alarm_template) t = template_format.parse(alarm_template)
properties = t['Resources']['MEMAlarmHigh']['Properties'] properties = t['Resources']['MEMAlarmHigh']['Properties']
# Test for bug/1383521, where meter_name is in NOVA_METERS # Test for bug/1383521, where meter_name is in NOVA_METERS
properties[alarm.CeilometerAlarm.METER_NAME] = 'memory.usage' properties[alarm.AodhAlarm.METER_NAME] = 'memory.usage'
properties['matching_metadata'] = {'metadata.user_metadata.groupname': properties['matching_metadata'] = {'metadata.user_metadata.groupname':
'foo'} 'foo'}
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
rsrc = self.stack['MEMAlarmHigh'] rsrc = test_stack['MEMAlarmHigh']
rsrc.properties.data = rsrc.cfn_to_ceilometer(self.stack, properties) rsrc.properties.data = rsrc.get_alarm_props(properties)
self.assertIsNone(rsrc.properties.data.get('matching_metadata')) self.assertIsNone(rsrc.properties.data.get('matching_metadata'))
query = rsrc.properties.data['threshold_rule']['query'] query = rsrc.properties.data['threshold_rule']['query']
expected_query = [{'field': u'metadata.user_metadata.groupname', expected_query = [{'field': u'metadata.user_metadata.groupname',
@ -355,13 +298,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
t = template_format.parse(alarm_template) t = template_format.parse(alarm_template)
properties = t['Resources']['MEMAlarmHigh']['Properties'] properties = t['Resources']['MEMAlarmHigh']['Properties']
# Test that meter_name is not in NOVA_METERS # Test that meter_name is not in NOVA_METERS
properties[alarm.CeilometerAlarm.METER_NAME] = 'memory_util' properties[alarm.AodhAlarm.METER_NAME] = 'memory_util'
properties['matching_metadata'] = {'metadata.user_metadata.groupname': properties['matching_metadata'] = {'metadata.user_metadata.groupname':
'foo'} 'foo'}
self.stack = self.create_stack(template=json.dumps(t)) self.stack = self.create_stack(template=json.dumps(t))
rsrc = self.stack['MEMAlarmHigh'] rsrc = self.stack['MEMAlarmHigh']
rsrc.properties.data = rsrc.cfn_to_ceilometer(self.stack, properties) rsrc.properties.data = rsrc.get_alarm_props(properties)
self.assertIsNone(rsrc.properties.data.get('matching_metadata')) self.assertIsNone(rsrc.properties.data.get('matching_metadata'))
query = rsrc.properties.data['threshold_rule']['query'] query = rsrc.properties.data['threshold_rule']['query']
expected_query = [{'field': u'metadata.metering.groupname', expected_query = [{'field': u'metadata.metering.groupname',
@ -377,19 +320,16 @@ class CeilometerAlarmTest(common.HeatTestCase):
'pro': '{"Mem": {"Ala": {"Hig"}}}', 'pro': '{"Mem": {"Ala": {"Hig"}}}',
'tro': [1, 2, 3, 4]} 'tro': [1, 2, 3, 4]}
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
self.m.ReplayAll() test_stack.create()
self.stack.create() rsrc = test_stack['MEMAlarmHigh']
rsrc = self.stack['MEMAlarmHigh']
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
rsrc.properties.data = rsrc.cfn_to_ceilometer(self.stack, properties) rsrc.properties.data = rsrc.get_alarm_props(properties)
self.assertIsNone(rsrc.properties.data.get('matching_metadata')) self.assertIsNone(rsrc.properties.data.get('matching_metadata'))
for key in rsrc.properties.data['threshold_rule']['query']: for key in rsrc.properties.data['threshold_rule']['query']:
self.assertIsInstance(key['value'], six.text_type) self.assertIsInstance(key['value'], six.text_type)
self.m.VerifyAll()
def test_no_matching_metadata(self): def test_no_matching_metadata(self):
"""Make sure that we can pass in an empty matching_metadata.""" """Make sure that we can pass in an empty matching_metadata."""
@ -398,16 +338,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
properties['alarm_actions'] = ['signal_handler'] properties['alarm_actions'] = ['signal_handler']
del properties['matching_metadata'] del properties['matching_metadata']
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
self.m.ReplayAll() test_stack.create()
self.stack.create() rsrc = test_stack['MEMAlarmHigh']
rsrc = self.stack['MEMAlarmHigh']
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
self.assertIsNone(rsrc.validate()) self.assertIsNone(rsrc.validate())
self.m.VerifyAll()
def test_mem_alarm_high_not_correct_string_parameters(self): def test_mem_alarm_high_not_correct_string_parameters(self):
orig_snippet = template_format.parse(not_string_alarm_template) orig_snippet = template_format.parse(not_string_alarm_template)
for p in ('period', 'evaluation_periods'): for p in ('period', 'evaluation_periods'):
@ -416,7 +353,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)
resource_defns = stack.t.resource_definitions(stack) resource_defns = stack.t.resource_definitions(stack)
rsrc = alarm.CeilometerAlarm( rsrc = alarm.AodhAlarm(
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack) 'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
error = self.assertRaises(exception.StackValidationFailed, error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate) rsrc.validate)
@ -432,7 +369,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)
resource_defns = stack.t.resource_definitions(stack) resource_defns = stack.t.resource_definitions(stack)
rsrc = alarm.CeilometerAlarm( rsrc = alarm.AodhAlarm(
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack) 'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
# python 3.4.3 returns another error message # python 3.4.3 returns another error message
# so try to handle this by regexp # so try to handle this by regexp
@ -448,7 +385,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)
resource_defns = stack.t.resource_definitions(stack) resource_defns = stack.t.resource_definitions(stack)
rsrc = alarm.CeilometerAlarm( rsrc = alarm.AodhAlarm(
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack) 'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
error = self.assertRaises(exception.StackValidationFailed, error = self.assertRaises(exception.StackValidationFailed,
rsrc.validate) rsrc.validate)
@ -464,35 +401,35 @@ class CeilometerAlarmTest(common.HeatTestCase):
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)
resource_defns = stack.t.resource_definitions(stack) resource_defns = stack.t.resource_definitions(stack)
rsrc = alarm.CeilometerAlarm( rsrc = alarm.AodhAlarm(
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack) 'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
self.assertIsNone(rsrc.validate()) self.assertIsNone(rsrc.validate())
def test_delete_watchrule_destroy(self): def test_delete_watchrule_destroy(self):
t = template_format.parse(alarm_template) t = template_format.parse(alarm_template)
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
rsrc = self.stack['MEMAlarmHigh'] rsrc = test_stack['MEMAlarmHigh']
wr = mock.MagicMock() wr = mock.MagicMock()
self.patchobject(watchrule.WatchRule, 'load', return_value=wr) self.patchobject(watchrule.WatchRule, 'load', return_value=wr)
wr.destroy.return_value = None wr.destroy.return_value = None
self.patchobject(ceilometer.CeilometerClientPlugin, 'client', self.patchobject(aodh.AodhClientPlugin, 'client',
return_value=self.fa) return_value=self.fa)
self.patchobject(self.fa.alarms, 'delete') self.patchobject(self.fa.alarm, 'delete')
rsrc.resource_id = '12345' rsrc.resource_id = '12345'
self.assertEqual('12345', rsrc.handle_delete()) self.assertEqual('12345', rsrc.handle_delete())
self.assertEqual(1, wr.destroy.call_count) self.assertEqual(1, wr.destroy.call_count)
# check that super method has been called and execute deleting # check that super method has been called and execute deleting
self.assertEqual(1, self.fa.alarms.delete.call_count) self.assertEqual(1, self.fa.alarm.delete.call_count)
def test_delete_no_watchrule(self): def test_delete_no_watchrule(self):
t = template_format.parse(alarm_template) t = template_format.parse(alarm_template)
self.stack = self.create_stack(template=json.dumps(t)) test_stack = self.create_stack(template=json.dumps(t))
rsrc = self.stack['MEMAlarmHigh'] rsrc = test_stack['MEMAlarmHigh']
wr = mock.MagicMock() wr = mock.MagicMock()
self.patchobject(watchrule.WatchRule, 'load', self.patchobject(watchrule.WatchRule, 'load',
@ -500,15 +437,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
entity='Watch Rule', name='test')]) entity='Watch Rule', name='test')])
wr.destroy.return_value = None wr.destroy.return_value = None
self.patchobject(ceilometer.CeilometerClientPlugin, 'client', self.patchobject(aodh.AodhClientPlugin, 'client',
return_value=self.fa) return_value=self.fa)
self.patchobject(self.fa.alarms, 'delete') self.patchobject(self.fa.alarm, 'delete')
rsrc.resource_id = '12345' rsrc.resource_id = '12345'
self.assertEqual('12345', rsrc.handle_delete()) self.assertEqual('12345', rsrc.handle_delete())
self.assertEqual(0, wr.destroy.call_count) self.assertEqual(0, wr.destroy.call_count)
# check that super method has been called and execute deleting # check that super method has been called and execute deleting
self.assertEqual(1, self.fa.alarms.delete.call_count) self.assertEqual(1, self.fa.alarm.delete.call_count)
def _prepare_check_resource(self): def _prepare_check_resource(self):
snippet = template_format.parse(not_string_alarm_template) snippet = template_format.parse(not_string_alarm_template)
@ -516,7 +453,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
res = self.stack['MEMAlarmHigh'] res = self.stack['MEMAlarmHigh']
res.client = mock.Mock() res.client = mock.Mock()
mock_alarm = mock.Mock(enabled=True, state='ok') mock_alarm = mock.Mock(enabled=True, state='ok')
res.client().alarms.get.return_value = mock_alarm res.client().alarm.get.return_value = mock_alarm
return res return res
@mock.patch.object(alarm.watchrule.WatchRule, 'load') @mock.patch.object(alarm.watchrule.WatchRule, 'load')
@ -539,7 +476,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
@mock.patch.object(alarm.watchrule.WatchRule, 'load') @mock.patch.object(alarm.watchrule.WatchRule, 'load')
def test_check_alarm_failure(self, mock_load): def test_check_alarm_failure(self, mock_load):
res = self._prepare_check_resource() res = self._prepare_check_resource()
res.client().alarms.get.side_effect = Exception('Boom') res.client().alarm.get.side_effect = Exception('Boom')
self.assertRaises(exception.ResourceFailure, self.assertRaises(exception.ResourceFailure,
scheduler.TaskRunner(res.check)) scheduler.TaskRunner(res.check))
@ -548,11 +485,10 @@ class CeilometerAlarmTest(common.HeatTestCase):
def test_show_resource(self): def test_show_resource(self):
res = self._prepare_check_resource() res = self._prepare_check_resource()
res.client().alarms.create.return_value = mock.MagicMock( res.client().alarm.create.return_value = FakeAodhAlarm
alarm_id='2') res.client().alarm.get.return_value = FakeAodhAlarm
res.client().alarms.get.return_value = FakeCeilometerAlarm()
scheduler.TaskRunner(res.create)() scheduler.TaskRunner(res.create)()
self.assertEqual({'attr': 'val'}, res.FnGetAtt('show')) self.assertEqual(FakeAodhAlarm, res.FnGetAtt('show'))
def test_alarm_with_wrong_start_time(self): def test_alarm_with_wrong_start_time(self):
t = template_format.parse(alarm_template_with_time_constraints) t = template_format.parse(alarm_template_with_time_constraints)
@ -562,11 +498,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
"duration": 10800, "duration": 10800,
"description": "a description" "description": "a description"
}] }]
self.stack = self.create_stack(template=json.dumps(t), test_stack = self.create_stack(template=json.dumps(t),
time_constraints=time_constraints) time_constraints=time_constraints)
self.m.ReplayAll() test_stack.create()
self.stack.create() self.assertEqual((test_stack.CREATE, test_stack.COMPLETE),
rsrc = self.stack['MEMAlarmHigh'] test_stack.state)
rsrc = test_stack['MEMAlarmHigh']
properties = copy.copy(rsrc.properties.data) properties = copy.copy(rsrc.properties.data)
start_time = '* * * * * 100' start_time = '* * * * * 100'
@ -605,8 +543,6 @@ class CeilometerAlarmTest(common.HeatTestCase):
"[%s] is not acceptable, out of range" % (start_time, start_time), "[%s] is not acceptable, out of range" % (start_time, start_time),
error.message) error.message)
self.m.VerifyAll()
def test_alarm_with_wrong_timezone(self): def test_alarm_with_wrong_timezone(self):
t = template_format.parse(alarm_template_with_time_constraints) t = template_format.parse(alarm_template_with_time_constraints)
time_constraints = [{"name": "tc1", time_constraints = [{"name": "tc1",
@ -615,11 +551,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
"duration": 10800, "duration": 10800,
"description": "a description" "description": "a description"
}] }]
self.stack = self.create_stack(template=json.dumps(t), test_stack = self.create_stack(template=json.dumps(t),
time_constraints=time_constraints) time_constraints=time_constraints)
self.m.ReplayAll() test_stack.create()
self.stack.create() self.assertEqual((test_stack.CREATE, test_stack.COMPLETE),
rsrc = self.stack['MEMAlarmHigh'] test_stack.state)
rsrc = test_stack['MEMAlarmHigh']
properties = copy.copy(rsrc.properties.data) properties = copy.copy(rsrc.properties.data)
timezone = 'wrongtimezone' timezone = 'wrongtimezone'
@ -658,8 +596,6 @@ class CeilometerAlarmTest(common.HeatTestCase):
% (timezone, timezone), % (timezone, timezone),
error.message) error.message)
self.m.VerifyAll()
class CombinationAlarmTest(common.HeatTestCase): class CombinationAlarmTest(common.HeatTestCase):

View File

@ -121,7 +121,7 @@ IntegrationTestGroup = [
cfg.ListOpt('skip_scenario_test_list', cfg.ListOpt('skip_scenario_test_list',
help="List of scenario test class or class.method " help="List of scenario test class or class.method "
"names to skip ex. NeutronLoadBalancerTest, " "names to skip ex. NeutronLoadBalancerTest, "
"CeilometerAlarmTest.test_alarm"), "AodhAlarmTest.test_alarm"),
cfg.ListOpt('skip_test_stack_action_list', cfg.ListOpt('skip_test_stack_action_list',
help="List of stack actions in tests to skip " help="List of stack actions in tests to skip "
"ex. ABANDON, ADOPT, SUSPEND, RESUME"), "ex. ABANDON, ADOPT, SUSPEND, RESUME"),

View File

@ -104,7 +104,7 @@
#skip_functional_test_list = <None> #skip_functional_test_list = <None>
# List of scenario test class or class.method names to skip ex. # List of scenario test class or class.method names to skip ex.
# NeutronLoadBalancerTest, CeilometerAlarmTest.test_alarm (list value) # NeutronLoadBalancerTest, AodhAlarmTest.test_alarm (list value)
#skip_scenario_test_list = <None> #skip_scenario_test_list = <None>
# List of stack actions in tests to skip ex. ABANDON, ADOPT, SUSPEND, RESUME # List of stack actions in tests to skip ex. ABANDON, ADOPT, SUSPEND, RESUME

View File

@ -15,7 +15,7 @@ resources:
cooldown: 0 cooldown: 0
scaling_adjustment: 1 scaling_adjustment: 1
alarm: alarm:
type: OS::Ceilometer::Alarm type: OS::Aodh::Alarm
properties: properties:
description: Scale-up if the average CPU > 50% for 1 minute description: Scale-up if the average CPU > 50% for 1 minute
meter_name: test_meter meter_name: test_meter

View File

@ -18,12 +18,12 @@ from heat_integrationtests.scenario import scenario_base
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class CeilometerAlarmTest(scenario_base.ScenarioTestsBase): class AodhAlarmTest(scenario_base.ScenarioTestsBase):
"""Class is responsible for testing of ceilometer usage.""" """Class is responsible for testing of aodh usage."""
def setUp(self): def setUp(self):
super(CeilometerAlarmTest, self).setUp() super(AodhAlarmTest, self).setUp()
self.template = self._load_template(__file__, self.template = self._load_template(__file__,
'test_ceilometer_alarm.yaml', 'test_aodh_alarm.yaml',
'templates') 'templates')
def check_instance_count(self, stack_identifier, expected): def check_instance_count(self, stack_identifier, expected):