diff --git a/heat/engine/resources/autoscaling.py b/heat/engine/resources/autoscaling.py index b2f1b53c3c..d0f5a0d9e8 100644 --- a/heat/engine/resources/autoscaling.py +++ b/heat/engine/resources/autoscaling.py @@ -24,10 +24,8 @@ from heat.engine import environment from heat.engine import function from heat.engine.notification import autoscaling as notification from heat.engine import properties -from heat.engine import resource from heat.engine import rsrc_defn from heat.engine import scheduler -from heat.engine import signal_responder from heat.engine import stack_resource from heat.openstack.common import excutils from heat.openstack.common import log as logging @@ -717,93 +715,6 @@ class AutoScalingGroup(InstanceGroup, cooldown.CooldownMixin): return self._create_template(num_instances) -class LaunchConfiguration(resource.Resource): - - PROPERTIES = ( - IMAGE_ID, INSTANCE_TYPE, KEY_NAME, USER_DATA, SECURITY_GROUPS, - KERNEL_ID, RAM_DISK_ID, BLOCK_DEVICE_MAPPINGS, NOVA_SCHEDULER_HINTS, - ) = ( - 'ImageId', 'InstanceType', 'KeyName', 'UserData', 'SecurityGroups', - 'KernelId', 'RamDiskId', 'BlockDeviceMappings', 'NovaSchedulerHints', - ) - - _NOVA_SCHEDULER_HINT_KEYS = ( - NOVA_SCHEDULER_HINT_KEY, NOVA_SCHEDULER_HINT_VALUE, - ) = ( - 'Key', 'Value', - ) - - properties_schema = { - IMAGE_ID: properties.Schema( - properties.Schema.STRING, - _('Glance image ID or name.'), - required=True, - constraints=[ - constraints.CustomConstraint('glance.image') - ] - ), - INSTANCE_TYPE: properties.Schema( - properties.Schema.STRING, - _('Nova instance type (flavor).'), - required=True - ), - KEY_NAME: properties.Schema( - properties.Schema.STRING, - _('Optional Nova keypair name.'), - constraints=[ - constraints.CustomConstraint("nova.keypair") - ] - ), - USER_DATA: properties.Schema( - properties.Schema.STRING, - _('User data to pass to instance.') - ), - SECURITY_GROUPS: properties.Schema( - properties.Schema.LIST, - _('Security group names to assign.') - ), - KERNEL_ID: properties.Schema( - properties.Schema.STRING, - _('Not Implemented.'), - implemented=False - ), - RAM_DISK_ID: properties.Schema( - properties.Schema.STRING, - _('Not Implemented.'), - implemented=False - ), - BLOCK_DEVICE_MAPPINGS: properties.Schema( - properties.Schema.STRING, - _('Not Implemented.'), - implemented=False - ), - NOVA_SCHEDULER_HINTS: properties.Schema( - properties.Schema.LIST, - _('Scheduler hints to pass to Nova (Heat extension).'), - schema=properties.Schema( - properties.Schema.MAP, - schema={ - NOVA_SCHEDULER_HINT_KEY: properties.Schema( - properties.Schema.STRING, - required=True - ), - NOVA_SCHEDULER_HINT_VALUE: properties.Schema( - properties.Schema.STRING, - required=True - ), - }, - ) - ), - } - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - if 'Metadata' in tmpl_diff: - raise resource.UpdateReplace(self.name) - - def FnGetRefId(self): - return self.physical_resource_name_or_FnGetRefId() - - class AutoScalingResourceGroup(AutoScalingGroup): """An autoscaling group that can scale arbitrary resources.""" @@ -940,219 +851,9 @@ class AutoScalingResourceGroup(AutoScalingGroup): key=key) -class ScalingPolicy(signal_responder.SignalResponder, cooldown.CooldownMixin): - PROPERTIES = ( - AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE, - COOLDOWN, - ) = ( - 'AutoScalingGroupName', 'ScalingAdjustment', 'AdjustmentType', - 'Cooldown', - ) - - EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = ( - 'ExactCapacity', 'ChangeInCapacity', 'PercentChangeInCapacity') - - ATTRIBUTES = ( - ALARM_URL, - ) = ( - 'AlarmUrl', - ) - - properties_schema = { - AUTO_SCALING_GROUP_NAME: properties.Schema( - properties.Schema.STRING, - _('AutoScaling group name to apply policy to.'), - required=True - ), - SCALING_ADJUSTMENT: properties.Schema( - properties.Schema.NUMBER, - _('Size of adjustment.'), - required=True, - update_allowed=True - ), - ADJUSTMENT_TYPE: properties.Schema( - properties.Schema.STRING, - _('Type of adjustment (absolute or percentage).'), - required=True, - constraints=[ - constraints.AllowedValues([CHANGE_IN_CAPACITY, - EXACT_CAPACITY, - PERCENT_CHANGE_IN_CAPACITY]), - ], - update_allowed=True - ), - COOLDOWN: properties.Schema( - properties.Schema.NUMBER, - _('Cooldown period, in seconds.'), - update_allowed=True - ), - } - - attributes_schema = { - ALARM_URL: attributes.Schema( - _("A signed url to handle the alarm. (Heat extension).") - ), - } - - def handle_create(self): - super(ScalingPolicy, self).handle_create() - self.resource_id_set(self._get_user_id()) - - def handle_update(self, json_snippet, tmpl_diff, prop_diff): - """ - If Properties has changed, update self.properties, so we get the new - values during any subsequent adjustment. - """ - if prop_diff: - self.properties = json_snippet.properties(self.properties_schema, - self.context) - - def _get_adjustement_type(self): - return self.properties[self.ADJUSTMENT_TYPE] - - def handle_signal(self, details=None): - if self.action in (self.SUSPEND, self.DELETE): - msg = _('Cannot signal resource during %s') % self.action - raise Exception(msg) - - # ceilometer sends details like this: - # {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm', - # u'reason': u'...'}) - # in this policy we currently assume that this gets called - # only when there is an alarm. But the template writer can - # put the policy in all the alarm notifiers (nodata, and ok). - # - # our watchrule has upper case states so lower() them all. - if details is None: - alarm_state = 'alarm' - else: - alarm_state = details.get('current', - details.get('state', 'alarm')).lower() - - LOG.info(_('%(name)s Alarm, new state %(state)s') - % {'name': self.name, 'state': alarm_state}) - - if alarm_state != 'alarm': - return - if self._cooldown_inprogress(): - LOG.info(_("%(name)s NOT performing scaling action, " - "cooldown %(cooldown)s") - % {'name': self.name, - 'cooldown': self.properties[self.COOLDOWN]}) - return - - asgn_id = self.properties[self.AUTO_SCALING_GROUP_NAME] - group = self.stack.resource_by_refid(asgn_id) - if group is None: - raise exception.NotFound(_('Alarm %(alarm)s could not find ' - 'scaling group named "%(group)s"') % { - 'alarm': self.name, - 'group': asgn_id}) - - LOG.info(_('%(name)s Alarm, adjusting Group %(group)s with id ' - '%(asgn_id)s by %(filter)s') - % {'name': self.name, 'group': group.name, 'asgn_id': asgn_id, - 'filter': self.properties[self.SCALING_ADJUSTMENT]}) - adjustment_type = self._get_adjustement_type() - group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type) - - self._cooldown_timestamp("%s : %s" % - (self.properties[self.ADJUSTMENT_TYPE], - self.properties[self.SCALING_ADJUSTMENT])) - - def _resolve_attribute(self, name): - ''' - heat extension: "AlarmUrl" returns the url to post to the policy - when there is an alarm. - ''' - if name == self.ALARM_URL and self.resource_id is not None: - return unicode(self._get_signed_url()) - - def FnGetRefId(self): - if self.resource_id is not None: - return unicode(self._get_signed_url()) - else: - return unicode(self.name) - - -class AutoScalingPolicy(ScalingPolicy): - """A resource to manage scaling of `OS::Heat::AutoScalingGroup`. - - **Note** while it may incidentally support - `AWS::AutoScaling::AutoScalingGroup` for now, please don't use it for that - purpose and use `AWS::AutoScaling::ScalingPolicy` instead. - """ - PROPERTIES = ( - AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE, - COOLDOWN, - ) = ( - 'auto_scaling_group_id', 'scaling_adjustment', 'adjustment_type', - 'cooldown', - ) - - EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = ( - 'exact_capacity', 'change_in_capacity', 'percent_change_in_capacity') - - ATTRIBUTES = ( - ALARM_URL, - ) = ( - 'alarm_url', - ) - - properties_schema = { - AUTO_SCALING_GROUP_NAME: properties.Schema( - properties.Schema.STRING, - _('AutoScaling group ID to apply policy to.'), - required=True - ), - SCALING_ADJUSTMENT: properties.Schema( - properties.Schema.NUMBER, - _('Size of adjustment.'), - required=True, - update_allowed=True - ), - ADJUSTMENT_TYPE: properties.Schema( - properties.Schema.STRING, - _('Type of adjustment (absolute or percentage).'), - required=True, - constraints=[ - constraints.AllowedValues([CHANGE_IN_CAPACITY, - EXACT_CAPACITY, - PERCENT_CHANGE_IN_CAPACITY]), - ], - update_allowed=True - ), - COOLDOWN: properties.Schema( - properties.Schema.NUMBER, - _('Cooldown period, in seconds.'), - update_allowed=True - ), - } - - attributes_schema = { - ALARM_URL: attributes.Schema( - _("A signed url to handle the alarm.") - ), - } - - def _get_adjustement_type(self): - adjustment_type = self.properties[self.ADJUSTMENT_TYPE] - return ''.join([t.capitalize() for t in adjustment_type.split('_')]) - - def _resolve_attribute(self, name): - if name == self.ALARM_URL and self.resource_id is not None: - return unicode(self._get_signed_url()) - - def FnGetRefId(self): - return resource.Resource.FnGetRefId(self) - - def resource_mapping(): return { - 'AWS::AutoScaling::LaunchConfiguration': LaunchConfiguration, 'AWS::AutoScaling::AutoScalingGroup': AutoScalingGroup, - 'AWS::AutoScaling::ScalingPolicy': ScalingPolicy, 'OS::Heat::InstanceGroup': InstanceGroup, 'OS::Heat::AutoScalingGroup': AutoScalingResourceGroup, - 'OS::Heat::ScalingPolicy': AutoScalingPolicy, } diff --git a/heat/engine/resources/aws/__init__.py b/heat/engine/resources/aws/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/heat/engine/resources/aws/launch_config.py b/heat/engine/resources/aws/launch_config.py new file mode 100644 index 0000000000..3a1bf0f140 --- /dev/null +++ b/heat/engine/resources/aws/launch_config.py @@ -0,0 +1,114 @@ +# +# 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.engine import constraints +from heat.engine import properties +from heat.engine import resource + +from heat.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +class LaunchConfiguration(resource.Resource): + + PROPERTIES = ( + IMAGE_ID, INSTANCE_TYPE, KEY_NAME, USER_DATA, SECURITY_GROUPS, + KERNEL_ID, RAM_DISK_ID, BLOCK_DEVICE_MAPPINGS, NOVA_SCHEDULER_HINTS, + ) = ( + 'ImageId', 'InstanceType', 'KeyName', 'UserData', 'SecurityGroups', + 'KernelId', 'RamDiskId', 'BlockDeviceMappings', 'NovaSchedulerHints', + ) + + _NOVA_SCHEDULER_HINT_KEYS = ( + NOVA_SCHEDULER_HINT_KEY, NOVA_SCHEDULER_HINT_VALUE, + ) = ( + 'Key', 'Value', + ) + + properties_schema = { + IMAGE_ID: properties.Schema( + properties.Schema.STRING, + _('Glance image ID or name.'), + required=True, + constraints=[ + constraints.CustomConstraint('glance.image') + ] + ), + INSTANCE_TYPE: properties.Schema( + properties.Schema.STRING, + _('Nova instance type (flavor).'), + required=True + ), + KEY_NAME: properties.Schema( + properties.Schema.STRING, + _('Optional Nova keypair name.'), + constraints=[ + constraints.CustomConstraint("nova.keypair") + ] + ), + USER_DATA: properties.Schema( + properties.Schema.STRING, + _('User data to pass to instance.') + ), + SECURITY_GROUPS: properties.Schema( + properties.Schema.LIST, + _('Security group names to assign.') + ), + KERNEL_ID: properties.Schema( + properties.Schema.STRING, + _('Not Implemented.'), + implemented=False + ), + RAM_DISK_ID: properties.Schema( + properties.Schema.STRING, + _('Not Implemented.'), + implemented=False + ), + BLOCK_DEVICE_MAPPINGS: properties.Schema( + properties.Schema.STRING, + _('Not Implemented.'), + implemented=False + ), + NOVA_SCHEDULER_HINTS: properties.Schema( + properties.Schema.LIST, + _('Scheduler hints to pass to Nova (Heat extension).'), + schema=properties.Schema( + properties.Schema.MAP, + schema={ + NOVA_SCHEDULER_HINT_KEY: properties.Schema( + properties.Schema.STRING, + required=True + ), + NOVA_SCHEDULER_HINT_VALUE: properties.Schema( + properties.Schema.STRING, + required=True + ), + }, + ) + ), + } + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if 'Metadata' in tmpl_diff: + raise resource.UpdateReplace(self.name) + + def FnGetRefId(self): + return self.physical_resource_name_or_FnGetRefId() + + +def resource_mapping(): + return { + 'AWS::AutoScaling::LaunchConfiguration': LaunchConfiguration, + } diff --git a/heat/engine/resources/aws/scaling_policy.py b/heat/engine/resources/aws/scaling_policy.py new file mode 100644 index 0000000000..40986bb899 --- /dev/null +++ b/heat/engine/resources/aws/scaling_policy.py @@ -0,0 +1,165 @@ +# +# 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 import exception + +from heat.engine import attributes +from heat.engine import constraints +from heat.engine import properties +from heat.engine import signal_responder +from heat.openstack.common import log as logging +from heat.scaling import cooldown + +LOG = logging.getLogger(__name__) + + +class AWSScalingPolicy(signal_responder.SignalResponder, + cooldown.CooldownMixin): + PROPERTIES = ( + AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE, + COOLDOWN, + ) = ( + 'AutoScalingGroupName', 'ScalingAdjustment', 'AdjustmentType', + 'Cooldown', + ) + + EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = ( + 'ExactCapacity', 'ChangeInCapacity', 'PercentChangeInCapacity') + + ATTRIBUTES = ( + ALARM_URL, + ) = ( + 'AlarmUrl', + ) + + properties_schema = { + AUTO_SCALING_GROUP_NAME: properties.Schema( + properties.Schema.STRING, + _('AutoScaling group name to apply policy to.'), + required=True + ), + SCALING_ADJUSTMENT: properties.Schema( + properties.Schema.NUMBER, + _('Size of adjustment.'), + required=True, + update_allowed=True + ), + ADJUSTMENT_TYPE: properties.Schema( + properties.Schema.STRING, + _('Type of adjustment (absolute or percentage).'), + required=True, + constraints=[ + constraints.AllowedValues([CHANGE_IN_CAPACITY, + EXACT_CAPACITY, + PERCENT_CHANGE_IN_CAPACITY]), + ], + update_allowed=True + ), + COOLDOWN: properties.Schema( + properties.Schema.NUMBER, + _('Cooldown period, in seconds.'), + update_allowed=True + ), + } + + attributes_schema = { + ALARM_URL: attributes.Schema( + _("A signed url to handle the alarm. (Heat extension).") + ), + } + + def handle_create(self): + super(AWSScalingPolicy, self).handle_create() + self.resource_id_set(self._get_user_id()) + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + """ + If Properties has changed, update self.properties, so we get the new + values during any subsequent adjustment. + """ + if prop_diff: + self.properties = json_snippet.properties(self.properties_schema, + self.context) + + def _get_adjustement_type(self): + return self.properties[self.ADJUSTMENT_TYPE] + + def handle_signal(self, details=None): + if self.action in (self.SUSPEND, self.DELETE): + msg = _('Cannot signal resource during %s') % self.action + raise Exception(msg) + + # ceilometer sends details like this: + # {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm', + # u'reason': u'...'}) + # in this policy we currently assume that this gets called + # only when there is an alarm. But the template writer can + # put the policy in all the alarm notifiers (nodata, and ok). + # + # our watchrule has upper case states so lower() them all. + if details is None: + alarm_state = 'alarm' + else: + alarm_state = details.get('current', + details.get('state', 'alarm')).lower() + + LOG.info(_('%(name)s Alarm, new state %(state)s') + % {'name': self.name, 'state': alarm_state}) + + if alarm_state != 'alarm': + return + if self._cooldown_inprogress(): + LOG.info(_("%(name)s NOT performing scaling action, " + "cooldown %(cooldown)s") + % {'name': self.name, + 'cooldown': self.properties[self.COOLDOWN]}) + return + + asgn_id = self.properties[self.AUTO_SCALING_GROUP_NAME] + group = self.stack.resource_by_refid(asgn_id) + if group is None: + raise exception.NotFound(_('Alarm %(alarm)s could not find ' + 'scaling group named "%(group)s"') % { + 'alarm': self.name, + 'group': asgn_id}) + + LOG.info(_('%(name)s Alarm, adjusting Group %(group)s with id ' + '%(asgn_id)s by %(filter)s') + % {'name': self.name, 'group': group.name, 'asgn_id': asgn_id, + 'filter': self.properties[self.SCALING_ADJUSTMENT]}) + adjustment_type = self._get_adjustement_type() + group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type) + + self._cooldown_timestamp("%s : %s" % + (self.properties[self.ADJUSTMENT_TYPE], + self.properties[self.SCALING_ADJUSTMENT])) + + def _resolve_attribute(self, name): + ''' + heat extension: "AlarmUrl" returns the url to post to the policy + when there is an alarm. + ''' + if name == self.ALARM_URL and self.resource_id is not None: + return unicode(self._get_signed_url()) + + def FnGetRefId(self): + if self.resource_id is not None: + return unicode(self._get_signed_url()) + else: + return unicode(self.name) + + +def resource_mapping(): + return { + 'AWS::AutoScaling::ScalingPolicy': AWSScalingPolicy, + } diff --git a/heat/engine/resources/openstack/__init__.py b/heat/engine/resources/openstack/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/heat/engine/resources/openstack/scaling_policy.py b/heat/engine/resources/openstack/scaling_policy.py new file mode 100644 index 0000000000..0d1fed3251 --- /dev/null +++ b/heat/engine/resources/openstack/scaling_policy.py @@ -0,0 +1,167 @@ +# +# 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 import exception +from heat.engine import attributes +from heat.engine import constraints +from heat.engine import properties +from heat.engine import resource +from heat.engine import signal_responder +from heat.scaling import cooldown + +from heat.openstack.common import log as logging + +LOG = logging.getLogger(__name__) + + +class AutoScalingPolicy(signal_responder.SignalResponder, + cooldown.CooldownMixin): + """A resource to manage scaling of `OS::Heat::AutoScalingGroup`. + + **Note** while it may incidentally support + `AWS::AutoScaling::AutoScalingGroup` for now, please don't use it for that + purpose and use `AWS::AutoScaling::ScalingPolicy` instead. + """ + PROPERTIES = ( + AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE, + COOLDOWN, + ) = ( + 'auto_scaling_group_id', 'scaling_adjustment', 'adjustment_type', + 'cooldown', + ) + + EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = ( + 'exact_capacity', 'change_in_capacity', 'percent_change_in_capacity') + + ATTRIBUTES = ( + ALARM_URL, + ) = ( + 'alarm_url', + ) + + properties_schema = { + # TODO(Qiming): property name should be AUTO_SCALING_GROUP_ID + AUTO_SCALING_GROUP_NAME: properties.Schema( + properties.Schema.STRING, + _('AutoScaling group ID to apply policy to.'), + required=True + ), + SCALING_ADJUSTMENT: properties.Schema( + properties.Schema.NUMBER, + _('Size of adjustment.'), + required=True, + update_allowed=True + ), + ADJUSTMENT_TYPE: properties.Schema( + properties.Schema.STRING, + _('Type of adjustment (absolute or percentage).'), + required=True, + constraints=[ + constraints.AllowedValues([CHANGE_IN_CAPACITY, + EXACT_CAPACITY, + PERCENT_CHANGE_IN_CAPACITY]), + ], + update_allowed=True + ), + COOLDOWN: properties.Schema( + properties.Schema.NUMBER, + _('Cooldown period, in seconds.'), + update_allowed=True + ), + } + + attributes_schema = { + ALARM_URL: attributes.Schema( + _("A signed url to handle the alarm.") + ), + } + + def handle_create(self): + super(AutoScalingPolicy, self).handle_create() + self.resource_id_set(self._get_user_id()) + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + """ + If Properties has changed, update self.properties, so we get the new + values during any subsequent adjustment. + """ + if prop_diff: + self.properties = json_snippet.properties(self.properties_schema, + self.context) + + def _get_adjustement_type(self): + adjustment_type = self.properties[self.ADJUSTMENT_TYPE] + return ''.join([t.capitalize() for t in adjustment_type.split('_')]) + + def handle_signal(self, details=None): + if self.action in (self.SUSPEND, self.DELETE): + msg = _('Cannot signal resource during %s') % self.action + raise Exception(msg) + + # ceilometer sends details like this: + # {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm', + # u'reason': u'...'}) + # in this policy we currently assume that this gets called + # only when there is an alarm. But the template writer can + # put the policy in all the alarm notifiers (nodata, and ok). + # + # our watchrule has upper case states so lower() them all. + if details is None: + alarm_state = 'alarm' + else: + alarm_state = details.get('current', + details.get('state', 'alarm')).lower() + + LOG.info(_('Alarm %(name)s, new state %(state)s') + % {'name': self.name, 'state': alarm_state}) + + if alarm_state != 'alarm': + return + if self._cooldown_inprogress(): + LOG.info(_("%(name)s NOT performing scaling action, " + "cooldown %(cooldown)s") + % {'name': self.name, + 'cooldown': self.properties[self.COOLDOWN]}) + return + + asgn_id = self.properties[self.AUTO_SCALING_GROUP_NAME] + group = self.stack.resource_by_refid(asgn_id) + if group is None: + raise exception.NotFound(_('Alarm %(alarm)s could not find ' + 'scaling group named "%(group)s"') % { + 'alarm': self.name, + 'group': asgn_id}) + + LOG.info(_('%(name)s Alarm, adjusting Group %(group)s with id ' + '%(asgn_id)s by %(filter)s') + % {'name': self.name, 'group': group.name, 'asgn_id': asgn_id, + 'filter': self.properties[self.SCALING_ADJUSTMENT]}) + adjustment_type = self._get_adjustement_type() + group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type) + + self._cooldown_timestamp("%s : %s" % + (self.properties[self.ADJUSTMENT_TYPE], + self.properties[self.SCALING_ADJUSTMENT])) + + def _resolve_attribute(self, name): + if name == self.ALARM_URL and self.resource_id is not None: + return unicode(self._get_signed_url()) + + def FnGetRefId(self): + return resource.Resource.FnGetRefId(self) + + +def resource_mapping(): + return { + 'OS::Heat::ScalingPolicy': AutoScalingPolicy, + } diff --git a/heat/tests/test_heat_autoscaling_group.py b/heat/tests/test_heat_autoscaling_group.py index 0799c92051..eb3c12f9df 100644 --- a/heat/tests/test_heat_autoscaling_group.py +++ b/heat/tests/test_heat_autoscaling_group.py @@ -323,7 +323,7 @@ class HeatScalingGroupWithCFNScalingPolicyTest(HeatTestCase): class ScalingPolicyTest(HeatTestCase): - + # TODO(Qiming): Add more tests to the scaling policy as_template = ''' heat_template_version: 2013-05-23 resources: