Merge "Native ScalingPolicy resource"

This commit is contained in:
Jenkins 2014-03-05 18:58:36 +00:00 committed by Gerrit Code Review
commit 2478007285
2 changed files with 151 additions and 2 deletions

View File

@ -888,6 +888,9 @@ class ScalingPolicy(signal_responder.SignalResponder, CooldownMixin):
'Cooldown', 'Cooldown',
) )
EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = (
'ExactCapacity', 'ChangeInCapacity', 'PercentChangeInCapacity')
properties_schema = { properties_schema = {
AUTO_SCALING_GROUP_NAME: properties.Schema( AUTO_SCALING_GROUP_NAME: properties.Schema(
properties.Schema.STRING, properties.Schema.STRING,
@ -941,6 +944,9 @@ class ScalingPolicy(signal_responder.SignalResponder, CooldownMixin):
self.name, self.name,
self.context) self.context)
def _get_adjustement_type(self):
return self.properties[self.ADJUSTMENT_TYPE]
def handle_signal(self, details=None): def handle_signal(self, details=None):
# ceilometer sends details like this: # ceilometer sends details like this:
# {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm', # {u'alarm_id': ID, u'previous': u'ok', u'current': u'alarm',
@ -981,8 +987,8 @@ class ScalingPolicy(signal_responder.SignalResponder, CooldownMixin):
'name': self.name, 'group': group.name, 'name': self.name, 'group': group.name,
'asgn_id': asgn_id, 'asgn_id': asgn_id,
'filter': self.properties[self.SCALING_ADJUSTMENT]}) 'filter': self.properties[self.SCALING_ADJUSTMENT]})
group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type = self._get_adjustement_type()
self.properties[self.ADJUSTMENT_TYPE]) group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type)
self._cooldown_timestamp("%s : %s" % self._cooldown_timestamp("%s : %s" %
(self.properties[self.ADJUSTMENT_TYPE], (self.properties[self.ADJUSTMENT_TYPE],
@ -1003,6 +1009,72 @@ class ScalingPolicy(signal_responder.SignalResponder, CooldownMixin):
return unicode(self.name) 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')
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
),
}
update_allowed_keys = ('Properties',)
attributes_schema = {
"alarm_url": _("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 == '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(): def resource_mapping():
return { return {
'AWS::AutoScaling::LaunchConfiguration': LaunchConfiguration, 'AWS::AutoScaling::LaunchConfiguration': LaunchConfiguration,
@ -1010,4 +1082,5 @@ def resource_mapping():
'AWS::AutoScaling::ScalingPolicy': ScalingPolicy, 'AWS::AutoScaling::ScalingPolicy': ScalingPolicy,
'OS::Heat::InstanceGroup': InstanceGroup, 'OS::Heat::InstanceGroup': InstanceGroup,
'OS::Heat::AutoScalingGroup': AutoScalingResourceGroup, 'OS::Heat::AutoScalingGroup': AutoScalingResourceGroup,
'OS::Heat::ScalingPolicy': AutoScalingPolicy,
} }

View File

@ -11,6 +11,7 @@
# under the License. # under the License.
import copy import copy
import datetime
from oslo.config import cfg from oslo.config import cfg
@ -21,6 +22,8 @@ from heat.engine import clients
from heat.engine import resource from heat.engine import resource
from heat.engine import scheduler from heat.engine import scheduler
from heat.openstack.common import timeutils
from heat.tests import fakes from heat.tests import fakes
from heat.tests import utils from heat.tests import utils
from heat.tests import generic_resource from heat.tests import generic_resource
@ -298,3 +301,76 @@ class HeatScalingGroupWithCFNScalingPolicyTest(HeatTestCase):
self.assertEqual(1, len(group.get_instances())) self.assertEqual(1, len(group.get_instances()))
scale_up.signal() scale_up.signal()
self.assertEqual(2, len(group.get_instances())) self.assertEqual(2, len(group.get_instances()))
class ScalingPolicyTest(HeatTestCase):
as_template = '''
heat_template_version: 2013-05-23
resources:
my-policy:
type: OS::Heat::ScalingPolicy
properties:
auto_scaling_group_id: {get_resource: my-group}
adjustment_type: change_in_capacity
scaling_adjustment: 1
my-group:
type: OS::Heat::AutoScalingGroup
properties:
max_size: 5
min_size: 1
resource:
type: ResourceWithProps
properties:
Foo: hello
'''
def setUp(self):
super(ScalingPolicyTest, self).setUp()
utils.setup_dummy_db()
resource._register_class('ResourceWithProps',
generic_resource.ResourceWithProps)
self.fc = fakes.FakeKeystoneClient()
client = self.patchobject(clients.OpenStackClients, "keystone")
client.return_value = self.fc
self.parsed = template_format.parse(self.as_template)
def test_alarm_attribute(self):
stack = utils.parse_stack(self.parsed)
stack.create()
policy = stack['my-policy']
self.assertIn("my-policy", policy.FnGetAtt('alarm_url'))
def test_signal(self):
stack = utils.parse_stack(self.parsed)
stack.create()
self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state)
policy = stack['my-policy']
group = stack['my-group']
self.assertEqual("1234", policy.FnGetRefId())
self.assertEqual(1, len(group.get_instance_names()))
policy.signal()
self.assertEqual(2, len(group.get_instance_names()))
def test_signal_with_cooldown(self):
self.parsed['resources']['my-policy']['properties']['cooldown'] = 60
stack = utils.parse_stack(self.parsed)
stack.create()
policy = stack['my-policy']
group = stack['my-group']
self.assertEqual(1, len(group.get_instance_names()))
policy.signal()
self.assertEqual(2, len(group.get_instance_names()))
policy.signal()
# The second signal shouldn't have changed it because of cooldown
self.assertEqual(2, len(group.get_instance_names()))
past = timeutils.strtime(timeutils.utcnow() -
datetime.timedelta(seconds=65))
policy.metadata = {past: 'ChangeInCapacity : 1'}
policy.signal()
self.assertEqual(3, len(group.get_instance_names()))