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:
parent
47262c079d
commit
4a79f7ca53
@ -5,5 +5,6 @@ resource_registry:
|
||||
# Choose your implementation of AWS::CloudWatch::Alarm
|
||||
"AWS::CloudWatch::Alarm": "file:///etc/heat/templates/AWS_CloudWatch_Alarm.yaml"
|
||||
#"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"
|
||||
"OS::Ceilometer::Alarm": "OS::Aodh::Alarm"
|
||||
|
@ -60,7 +60,7 @@ Mappings:
|
||||
|
||||
Resources:
|
||||
__alarm__:
|
||||
Type: OS::Ceilometer::Alarm
|
||||
Type: OS::Aodh::Alarm
|
||||
Properties:
|
||||
description:
|
||||
Ref: AlarmDescription
|
||||
|
211
heat/engine/resources/alarm_base.py
Normal file
211
heat/engine/resources/alarm_base.py
Normal 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)
|
@ -17,172 +17,13 @@ from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import constraints
|
||||
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 watchrule
|
||||
|
||||
|
||||
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']
|
||||
|
||||
|
||||
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.
|
||||
class AodhAlarm(alarm_base.BaseAlarm):
|
||||
"""A resource that implements alarming service of Aodh.
|
||||
|
||||
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
|
||||
@ -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 cfn_to_ceilometer(self, stack, properties):
|
||||
def get_alarm_props(self, props):
|
||||
"""Apply all relevant compatibility xforms."""
|
||||
|
||||
kwargs = actions_to_urls(stack, properties)
|
||||
kwargs['type'] = 'threshold'
|
||||
if kwargs.get(self.METER_NAME) in NOVA_METERS:
|
||||
kwargs = self.actions_to_urls(props)
|
||||
kwargs['type'] = self.alarm_type
|
||||
if kwargs.get(self.METER_NAME) in alarm_base.NOVA_METERS:
|
||||
prefix = 'user_metadata.'
|
||||
else:
|
||||
prefix = 'metering.'
|
||||
@ -312,8 +150,8 @@ class CeilometerAlarm(resource.Resource):
|
||||
if field in kwargs:
|
||||
rule[field] = kwargs[field]
|
||||
del kwargs[field]
|
||||
mmd = properties.get(self.MATCHING_METADATA) or {}
|
||||
query = properties.get(self.QUERY) or []
|
||||
mmd = props.get(self.MATCHING_METADATA) or {}
|
||||
query = props.get(self.QUERY) or []
|
||||
|
||||
# make sure the matching_metadata appears in the query like this:
|
||||
# {field: metadata.$prefix.x, ...}
|
||||
@ -339,11 +177,10 @@ class CeilometerAlarm(resource.Resource):
|
||||
return kwargs
|
||||
|
||||
def handle_create(self):
|
||||
props = self.cfn_to_ceilometer(self.stack,
|
||||
self.properties)
|
||||
props = self.get_alarm_props(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
alarm = self.client().alarms.create(**props)
|
||||
self.resource_id_set(alarm.alarm_id)
|
||||
alarm = self.client().alarm.create(props)
|
||||
self.resource_id_set(alarm['alarm_id'])
|
||||
|
||||
# the watchrule below is for backwards compatibility.
|
||||
# 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):
|
||||
if prop_diff:
|
||||
kwargs = {'alarm_id': self.resource_id}
|
||||
kwargs = {}
|
||||
kwargs.update(self.properties)
|
||||
kwargs.update(prop_diff)
|
||||
alarms_client = self.client().alarms
|
||||
alarms_client.update(**self.cfn_to_ceilometer(self.stack, 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)
|
||||
self.client().alarm.update(self.resource_id,
|
||||
self.get_alarm_props(kwargs))
|
||||
|
||||
def handle_delete(self):
|
||||
try:
|
||||
@ -382,45 +209,37 @@ class CeilometerAlarm(resource.Resource):
|
||||
except exception.EntityNotFound:
|
||||
pass
|
||||
|
||||
return super(CeilometerAlarm, self).handle_delete()
|
||||
return super(AodhAlarm, self).handle_delete()
|
||||
|
||||
def handle_check(self):
|
||||
watch_name = self.physical_resource_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'
|
||||
|
||||
entity = 'alarms'
|
||||
|
||||
def handle_create(self):
|
||||
properties = actions_to_urls(self.stack,
|
||||
self.properties)
|
||||
properties['name'] = self.physical_resource_name()
|
||||
properties['type'] = self.ceilometer_alarm_type
|
||||
props = self.actions_to_urls(self.properties)
|
||||
props['name'] = self.physical_resource_name()
|
||||
props['type'] = self.alarm_type
|
||||
alarm = self.client().alarms.create(
|
||||
**self._reformat_properties(properties))
|
||||
**self._reformat_properties(props))
|
||||
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):
|
||||
if prop_diff:
|
||||
kwargs = {'alarm_id': self.resource_id}
|
||||
kwargs.update(prop_diff)
|
||||
alarms_client = self.client().alarms
|
||||
alarms_client.update(**self._reformat_properties(
|
||||
actions_to_urls(self.stack, kwargs)))
|
||||
self.actions_to_urls(kwargs)))
|
||||
|
||||
def handle_suspend(self):
|
||||
self.client().alarms.update(
|
||||
@ -463,13 +282,13 @@ class CombinationAlarm(BaseCeilometerAlarm):
|
||||
constraints=[constraints.AllowedValues(['and', 'or'])],
|
||||
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():
|
||||
return {
|
||||
'OS::Ceilometer::Alarm': CeilometerAlarm,
|
||||
'OS::Aodh::Alarm': AodhAlarm,
|
||||
'OS::Ceilometer::CombinationAlarm': CombinationAlarm,
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
from heat.common.i18n import _
|
||||
from heat.engine import constraints
|
||||
from heat.engine import properties
|
||||
from heat.engine.resources import alarm_base
|
||||
from heat.engine.resources.openstack.ceilometer import alarm
|
||||
from heat.engine import support
|
||||
|
||||
@ -103,9 +104,9 @@ class CeilometerGnocchiResourcesAlarm(alarm.BaseCeilometerAlarm):
|
||||
),
|
||||
}
|
||||
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(
|
||||
@ -130,9 +131,9 @@ class CeilometerGnocchiAggregationByMetricsAlarm(
|
||||
),
|
||||
}
|
||||
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(
|
||||
@ -175,9 +176,9 @@ class CeilometerGnocchiAggregationByResourcesAlarm(
|
||||
}
|
||||
|
||||
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():
|
||||
|
@ -143,7 +143,7 @@ class CloudWatchAlarm(resource.Resource):
|
||||
support_status = support.SupportStatus(
|
||||
status=support.HIDDEN,
|
||||
message=_('OS::Heat::CWLiteAlarm is deprecated, '
|
||||
'use OS::Ceilometer::Alarm instead.'),
|
||||
'use OS::Aodh::Alarm instead.'),
|
||||
version='5.0.0',
|
||||
previous_status=support.SupportStatus(
|
||||
status=support.DEPRECATED,
|
||||
|
@ -20,8 +20,8 @@ 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 ceilometer
|
||||
from heat.engine import properties as props
|
||||
from heat.engine.resources.openstack.ceilometer import alarm
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
@ -39,7 +39,7 @@ alarm_template = '''
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"MEMAlarmHigh": {
|
||||
"Type": "OS::Ceilometer::Alarm",
|
||||
"Type": "OS::Aodh::Alarm",
|
||||
"Properties": {
|
||||
"description": "Scale-up if MEM > 50% for 1 minute",
|
||||
"meter_name": "MemoryUtilization",
|
||||
@ -66,7 +66,7 @@ alarm_template_with_time_constraints = '''
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"MEMAlarmHigh": {
|
||||
"Type": "OS::Ceilometer::Alarm",
|
||||
"Type": "OS::Aodh::Alarm",
|
||||
"Properties": {
|
||||
"description": "Scale-up if MEM > 50% for 1 minute",
|
||||
"meter_name": "MemoryUtilization",
|
||||
@ -100,7 +100,7 @@ not_string_alarm_template = '''
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"MEMAlarmHigh": {
|
||||
"Type": "OS::Ceilometer::Alarm",
|
||||
"Type": "OS::Aodh::Alarm",
|
||||
"Properties": {
|
||||
"description": "Scale-up if MEM > 50% for 1 minute",
|
||||
"meter_name": "MemoryUtilization",
|
||||
@ -146,9 +146,13 @@ class FakeCeilometerAlarm(object):
|
||||
self.to_dict = lambda: {'attr': 'val'}
|
||||
|
||||
|
||||
class CeilometerAlarmTest(common.HeatTestCase):
|
||||
FakeAodhAlarm = {'other_attrs': 'val',
|
||||
'alarm_id': 'foo'}
|
||||
|
||||
|
||||
class AodhAlarmTest(common.HeatTestCase):
|
||||
def setUp(self):
|
||||
super(CeilometerAlarmTest, self).setUp()
|
||||
super(AodhAlarmTest, self).setUp()
|
||||
self.fa = mock.Mock()
|
||||
|
||||
def create_stack(self, template=None, time_constraints=None):
|
||||
@ -162,46 +166,14 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
disable_rollback=True)
|
||||
stack.store()
|
||||
|
||||
self.m.StubOutWithMock(ceilometer.CeilometerClientPlugin, '_create')
|
||||
ceilometer.CeilometerClientPlugin._create().AndReturn(self.fa)
|
||||
self.patchobject(aodh.AodhClientPlugin,
|
||||
'_create').return_value = self.fa
|
||||
|
||||
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['severity'] = 'low'
|
||||
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:
|
||||
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())
|
||||
|
||||
self.patchobject(self.fa.alarm, 'create').return_value = FakeAodhAlarm
|
||||
|
||||
return stack
|
||||
|
||||
def test_mem_alarm_high_update_no_replace(self):
|
||||
@ -214,37 +186,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
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 = 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)
|
||||
test_stack = self.create_stack(template=json.dumps(t))
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
update_mock = self.patchobject(self.fa.alarm, 'update')
|
||||
|
||||
properties = copy.copy(rsrc.properties.data)
|
||||
properties.update({
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
update_props = copy.deepcopy(rsrc.properties.data)
|
||||
update_props.update({
|
||||
'comparison_operator': 'lt',
|
||||
'description': 'fruity',
|
||||
'evaluation_periods': '2',
|
||||
@ -259,13 +209,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
'matching_metadata': {'x': 'y'},
|
||||
'query': [dict(field='c', op='ne', value='z')]
|
||||
})
|
||||
|
||||
snippet = rsrc_defn.ResourceDefinition(rsrc.name,
|
||||
rsrc.type(),
|
||||
properties)
|
||||
update_props)
|
||||
|
||||
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):
|
||||
"""Tests resource replacing when changing non-updatable properties."""
|
||||
@ -275,11 +227,10 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
properties['alarm_actions'] = ['signal_handler']
|
||||
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()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
properties = copy.copy(rsrc.properties.data)
|
||||
properties['meter_name'] = 'temp'
|
||||
@ -290,40 +241,34 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
updater = scheduler.TaskRunner(rsrc.update, snippet)
|
||||
self.assertRaises(exception.UpdateReplace, updater)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_mem_alarm_suspend_resume(self):
|
||||
"""Tests suspending and resuming of the alarm.
|
||||
|
||||
Make sure that the Alarm resource gets disabled on suspend
|
||||
and re-enabled on resume.
|
||||
"""
|
||||
self.stack = self.create_stack()
|
||||
test_stack = self.create_stack()
|
||||
|
||||
self.m.StubOutWithMock(self.fa.alarms, 'update')
|
||||
al_suspend = {'alarm_id': mox.IgnoreArg(),
|
||||
'enabled': False}
|
||||
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()
|
||||
update_mock = self.patchobject(self.fa.alarm, 'update')
|
||||
al_suspend = {'enabled': False}
|
||||
al_resume = {'enabled': True}
|
||||
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
scheduler.TaskRunner(rsrc.suspend)()
|
||||
self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state)
|
||||
scheduler.TaskRunner(rsrc.resume)()
|
||||
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):
|
||||
self.stack = self.create_stack(not_string_alarm_template)
|
||||
test_stack = self.create_stack(not_string_alarm_template)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertIsNone(rsrc.validate())
|
||||
|
||||
@ -331,20 +276,18 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
self.assertIsInstance(rsrc.properties['period'], int)
|
||||
self.assertIsInstance(rsrc.properties['threshold'], int)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_alarm_metadata_prefix(self):
|
||||
t = template_format.parse(alarm_template)
|
||||
properties = t['Resources']['MEMAlarmHigh']['Properties']
|
||||
# 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':
|
||||
'foo'}
|
||||
|
||||
self.stack = self.create_stack(template=json.dumps(t))
|
||||
test_stack = self.create_stack(template=json.dumps(t))
|
||||
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
rsrc.properties.data = rsrc.cfn_to_ceilometer(self.stack, properties)
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
rsrc.properties.data = rsrc.get_alarm_props(properties)
|
||||
self.assertIsNone(rsrc.properties.data.get('matching_metadata'))
|
||||
query = rsrc.properties.data['threshold_rule']['query']
|
||||
expected_query = [{'field': u'metadata.user_metadata.groupname',
|
||||
@ -355,13 +298,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
t = template_format.parse(alarm_template)
|
||||
properties = t['Resources']['MEMAlarmHigh']['Properties']
|
||||
# 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':
|
||||
'foo'}
|
||||
self.stack = self.create_stack(template=json.dumps(t))
|
||||
|
||||
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'))
|
||||
query = rsrc.properties.data['threshold_rule']['query']
|
||||
expected_query = [{'field': u'metadata.metering.groupname',
|
||||
@ -377,19 +320,16 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
'pro': '{"Mem": {"Ala": {"Hig"}}}',
|
||||
'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()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
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'))
|
||||
for key in rsrc.properties.data['threshold_rule']['query']:
|
||||
self.assertIsInstance(key['value'], six.text_type)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_no_matching_metadata(self):
|
||||
"""Make sure that we can pass in an empty matching_metadata."""
|
||||
|
||||
@ -398,16 +338,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
properties['alarm_actions'] = ['signal_handler']
|
||||
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()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
self.assertIsNone(rsrc.validate())
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_mem_alarm_high_not_correct_string_parameters(self):
|
||||
orig_snippet = template_format.parse(not_string_alarm_template)
|
||||
for p in ('period', 'evaluation_periods'):
|
||||
@ -416,7 +353,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
stack = utils.parse_stack(snippet)
|
||||
|
||||
resource_defns = stack.t.resource_definitions(stack)
|
||||
rsrc = alarm.CeilometerAlarm(
|
||||
rsrc = alarm.AodhAlarm(
|
||||
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
@ -432,7 +369,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
stack = utils.parse_stack(snippet)
|
||||
|
||||
resource_defns = stack.t.resource_definitions(stack)
|
||||
rsrc = alarm.CeilometerAlarm(
|
||||
rsrc = alarm.AodhAlarm(
|
||||
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
|
||||
# python 3.4.3 returns another error message
|
||||
# so try to handle this by regexp
|
||||
@ -448,7 +385,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
stack = utils.parse_stack(snippet)
|
||||
|
||||
resource_defns = stack.t.resource_definitions(stack)
|
||||
rsrc = alarm.CeilometerAlarm(
|
||||
rsrc = alarm.AodhAlarm(
|
||||
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
|
||||
error = self.assertRaises(exception.StackValidationFailed,
|
||||
rsrc.validate)
|
||||
@ -464,35 +401,35 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
stack = utils.parse_stack(snippet)
|
||||
|
||||
resource_defns = stack.t.resource_definitions(stack)
|
||||
rsrc = alarm.CeilometerAlarm(
|
||||
rsrc = alarm.AodhAlarm(
|
||||
'MEMAlarmHigh', resource_defns['MEMAlarmHigh'], stack)
|
||||
self.assertIsNone(rsrc.validate())
|
||||
|
||||
def test_delete_watchrule_destroy(self):
|
||||
t = template_format.parse(alarm_template)
|
||||
|
||||
self.stack = self.create_stack(template=json.dumps(t))
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack = self.create_stack(template=json.dumps(t))
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
wr = mock.MagicMock()
|
||||
self.patchobject(watchrule.WatchRule, 'load', return_value=wr)
|
||||
wr.destroy.return_value = None
|
||||
|
||||
self.patchobject(ceilometer.CeilometerClientPlugin, 'client',
|
||||
self.patchobject(aodh.AodhClientPlugin, 'client',
|
||||
return_value=self.fa)
|
||||
self.patchobject(self.fa.alarms, 'delete')
|
||||
self.patchobject(self.fa.alarm, 'delete')
|
||||
rsrc.resource_id = '12345'
|
||||
|
||||
self.assertEqual('12345', rsrc.handle_delete())
|
||||
self.assertEqual(1, wr.destroy.call_count)
|
||||
# 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):
|
||||
t = template_format.parse(alarm_template)
|
||||
|
||||
self.stack = self.create_stack(template=json.dumps(t))
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack = self.create_stack(template=json.dumps(t))
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
wr = mock.MagicMock()
|
||||
self.patchobject(watchrule.WatchRule, 'load',
|
||||
@ -500,15 +437,15 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
entity='Watch Rule', name='test')])
|
||||
wr.destroy.return_value = None
|
||||
|
||||
self.patchobject(ceilometer.CeilometerClientPlugin, 'client',
|
||||
self.patchobject(aodh.AodhClientPlugin, 'client',
|
||||
return_value=self.fa)
|
||||
self.patchobject(self.fa.alarms, 'delete')
|
||||
self.patchobject(self.fa.alarm, 'delete')
|
||||
rsrc.resource_id = '12345'
|
||||
|
||||
self.assertEqual('12345', rsrc.handle_delete())
|
||||
self.assertEqual(0, wr.destroy.call_count)
|
||||
# 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):
|
||||
snippet = template_format.parse(not_string_alarm_template)
|
||||
@ -516,7 +453,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
res = self.stack['MEMAlarmHigh']
|
||||
res.client = mock.Mock()
|
||||
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
|
||||
|
||||
@mock.patch.object(alarm.watchrule.WatchRule, 'load')
|
||||
@ -539,7 +476,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
@mock.patch.object(alarm.watchrule.WatchRule, 'load')
|
||||
def test_check_alarm_failure(self, mock_load):
|
||||
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,
|
||||
scheduler.TaskRunner(res.check))
|
||||
@ -548,11 +485,10 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
|
||||
def test_show_resource(self):
|
||||
res = self._prepare_check_resource()
|
||||
res.client().alarms.create.return_value = mock.MagicMock(
|
||||
alarm_id='2')
|
||||
res.client().alarms.get.return_value = FakeCeilometerAlarm()
|
||||
res.client().alarm.create.return_value = FakeAodhAlarm
|
||||
res.client().alarm.get.return_value = FakeAodhAlarm
|
||||
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):
|
||||
t = template_format.parse(alarm_template_with_time_constraints)
|
||||
@ -562,11 +498,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
"duration": 10800,
|
||||
"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)
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
self.assertEqual((test_stack.CREATE, test_stack.COMPLETE),
|
||||
test_stack.state)
|
||||
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
properties = copy.copy(rsrc.properties.data)
|
||||
start_time = '* * * * * 100'
|
||||
@ -605,8 +543,6 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
"[%s] is not acceptable, out of range" % (start_time, start_time),
|
||||
error.message)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_alarm_with_wrong_timezone(self):
|
||||
t = template_format.parse(alarm_template_with_time_constraints)
|
||||
time_constraints = [{"name": "tc1",
|
||||
@ -615,11 +551,13 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
"duration": 10800,
|
||||
"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)
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
rsrc = self.stack['MEMAlarmHigh']
|
||||
test_stack.create()
|
||||
self.assertEqual((test_stack.CREATE, test_stack.COMPLETE),
|
||||
test_stack.state)
|
||||
|
||||
rsrc = test_stack['MEMAlarmHigh']
|
||||
|
||||
properties = copy.copy(rsrc.properties.data)
|
||||
timezone = 'wrongtimezone'
|
||||
@ -658,8 +596,6 @@ class CeilometerAlarmTest(common.HeatTestCase):
|
||||
% (timezone, timezone),
|
||||
error.message)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class CombinationAlarmTest(common.HeatTestCase):
|
||||
|
||||
|
@ -121,7 +121,7 @@ IntegrationTestGroup = [
|
||||
cfg.ListOpt('skip_scenario_test_list',
|
||||
help="List of scenario test class or class.method "
|
||||
"names to skip ex. NeutronLoadBalancerTest, "
|
||||
"CeilometerAlarmTest.test_alarm"),
|
||||
"AodhAlarmTest.test_alarm"),
|
||||
cfg.ListOpt('skip_test_stack_action_list',
|
||||
help="List of stack actions in tests to skip "
|
||||
"ex. ABANDON, ADOPT, SUSPEND, RESUME"),
|
||||
|
@ -104,7 +104,7 @@
|
||||
#skip_functional_test_list = <None>
|
||||
|
||||
# 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>
|
||||
|
||||
# List of stack actions in tests to skip ex. ABANDON, ADOPT, SUSPEND, RESUME
|
||||
|
@ -15,7 +15,7 @@ resources:
|
||||
cooldown: 0
|
||||
scaling_adjustment: 1
|
||||
alarm:
|
||||
type: OS::Ceilometer::Alarm
|
||||
type: OS::Aodh::Alarm
|
||||
properties:
|
||||
description: Scale-up if the average CPU > 50% for 1 minute
|
||||
meter_name: test_meter
|
@ -18,12 +18,12 @@ from heat_integrationtests.scenario import scenario_base
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CeilometerAlarmTest(scenario_base.ScenarioTestsBase):
|
||||
"""Class is responsible for testing of ceilometer usage."""
|
||||
class AodhAlarmTest(scenario_base.ScenarioTestsBase):
|
||||
"""Class is responsible for testing of aodh usage."""
|
||||
def setUp(self):
|
||||
super(CeilometerAlarmTest, self).setUp()
|
||||
super(AodhAlarmTest, self).setUp()
|
||||
self.template = self._load_template(__file__,
|
||||
'test_ceilometer_alarm.yaml',
|
||||
'test_aodh_alarm.yaml',
|
||||
'templates')
|
||||
|
||||
def check_instance_count(self, stack_identifier, expected):
|
Loading…
Reference in New Issue
Block a user