Merge "Native ScalingPolicy resource"
This commit is contained in:
commit
2478007285
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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()))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user