Merge "Add min_adjustment_step property to ScalingPolicy"
This commit is contained in:
commit
729aa08f4a
|
@ -381,6 +381,11 @@ class ResourcePropertyDependency(HeatException):
|
|||
msg_fmt = _('%(prop1)s cannot be specified without %(prop2)s.')
|
||||
|
||||
|
||||
class ResourcePropertyValueDependency(HeatException):
|
||||
msg_fmt = _('%(prop1)s property should only be specified '
|
||||
'for %(prop2)s with value %(value)s.')
|
||||
|
||||
|
||||
class PropertyUnspecifiedError(HeatException):
|
||||
msg_fmt = _('At least one of the following properties '
|
||||
'must be specified: %(props)s')
|
||||
|
|
|
@ -41,12 +41,18 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
|
||||
def _calculate_new_capacity(current, adjustment, adjustment_type,
|
||||
minimum, maximum):
|
||||
min_adjustment_step, minimum, maximum):
|
||||
"""
|
||||
Given the current capacity, calculates the new capacity which results
|
||||
from applying the given adjustment of the given adjustment-type. The
|
||||
new capacity will be kept within the maximum and minimum bounds.
|
||||
"""
|
||||
def _get_minimum_adjustment(adjustment, min_adjustment_step):
|
||||
if min_adjustment_step and min_adjustment_step > abs(adjustment):
|
||||
adjustment = (min_adjustment_step if adjustment > 0
|
||||
else -min_adjustment_step)
|
||||
return adjustment
|
||||
|
||||
if adjustment_type == CHANGE_IN_CAPACITY:
|
||||
new_capacity = current + adjustment
|
||||
elif adjustment_type == EXACT_CAPACITY:
|
||||
|
@ -60,7 +66,8 @@ def _calculate_new_capacity(current, adjustment, adjustment_type,
|
|||
else:
|
||||
rounded = int(math.floor(delta) if delta > 0.0
|
||||
else math.ceil(delta))
|
||||
new_capacity = current + rounded
|
||||
adjustment = _get_minimum_adjustment(rounded, min_adjustment_step)
|
||||
new_capacity = current + adjustment
|
||||
|
||||
if new_capacity > maximum:
|
||||
LOG.debug('truncating growth to %s' % maximum)
|
||||
|
@ -293,7 +300,8 @@ class AutoScalingGroup(instgrp.InstanceGroup, cooldown.CooldownMixin):
|
|||
current_capacity = grouputils.get_size(self)
|
||||
self.adjust(current_capacity, adjustment_type=EXACT_CAPACITY)
|
||||
|
||||
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY):
|
||||
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None):
|
||||
"""
|
||||
Adjust the size of the scaling group if the cooldown permits.
|
||||
"""
|
||||
|
@ -309,7 +317,9 @@ class AutoScalingGroup(instgrp.InstanceGroup, cooldown.CooldownMixin):
|
|||
upper = self.properties[self.MAX_SIZE]
|
||||
|
||||
new_capacity = _calculate_new_capacity(capacity, adjustment,
|
||||
adjustment_type, lower, upper)
|
||||
adjustment_type,
|
||||
min_adjustment_step,
|
||||
lower, upper)
|
||||
|
||||
# send a notification before, on-error and on-success.
|
||||
notif = {
|
||||
|
|
|
@ -14,26 +14,22 @@
|
|||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.common.i18n import _LI
|
||||
from heat.engine import attributes
|
||||
from heat.engine import constraints
|
||||
from heat.engine import properties
|
||||
from heat.engine.resources import signal_responder
|
||||
from heat.scaling import cooldown
|
||||
from heat.engine.resources.openstack.heat import scaling_policy as heat_sp
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AWSScalingPolicy(signal_responder.SignalResponder,
|
||||
cooldown.CooldownMixin):
|
||||
class AWSScalingPolicy(heat_sp.AutoScalingPolicy):
|
||||
PROPERTIES = (
|
||||
AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE,
|
||||
COOLDOWN,
|
||||
COOLDOWN, MIN_ADJUSTMENT_STEP,
|
||||
) = (
|
||||
'AutoScalingGroupName', 'ScalingAdjustment', 'AdjustmentType',
|
||||
'Cooldown',
|
||||
'Cooldown', 'MinAdjustmentStep',
|
||||
)
|
||||
|
||||
EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = (
|
||||
|
@ -73,6 +69,20 @@ class AWSScalingPolicy(signal_responder.SignalResponder,
|
|||
_('Cooldown period, in seconds.'),
|
||||
update_allowed=True
|
||||
),
|
||||
MIN_ADJUSTMENT_STEP: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
_('Minimum number of resources that are added or removed '
|
||||
'when the AutoScaling group scales up or down. This can '
|
||||
'be used only when specifying PercentChangeInCapacity '
|
||||
'for the AdjustmentType property.'),
|
||||
constraints=[
|
||||
constraints.Range(
|
||||
min=0,
|
||||
),
|
||||
],
|
||||
update_allowed=True
|
||||
),
|
||||
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
|
@ -81,76 +91,9 @@ class AWSScalingPolicy(signal_responder.SignalResponder,
|
|||
),
|
||||
}
|
||||
|
||||
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):
|
||||
# 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(_LI('%(name)s Alarm, new state %(state)s'),
|
||||
{'name': self.name, 'state': alarm_state})
|
||||
|
||||
if alarm_state != 'alarm':
|
||||
return
|
||||
if self._cooldown_inprogress():
|
||||
LOG.info(_LI("%(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(_LI('%(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 six.text_type(self._get_signed_url())
|
||||
|
||||
def FnGetRefId(self):
|
||||
if self.resource_id is not None:
|
||||
return six.text_type(self._get_signed_url())
|
||||
|
|
|
@ -37,10 +37,10 @@ class AutoScalingPolicy(signal_responder.SignalResponder,
|
|||
"""
|
||||
PROPERTIES = (
|
||||
AUTO_SCALING_GROUP_NAME, SCALING_ADJUSTMENT, ADJUSTMENT_TYPE,
|
||||
COOLDOWN,
|
||||
COOLDOWN, MIN_ADJUSTMENT_STEP
|
||||
) = (
|
||||
'auto_scaling_group_id', 'scaling_adjustment', 'adjustment_type',
|
||||
'cooldown',
|
||||
'cooldown', 'min_adjustment_step',
|
||||
)
|
||||
|
||||
EXACT_CAPACITY, CHANGE_IN_CAPACITY, PERCENT_CHANGE_IN_CAPACITY = (
|
||||
|
@ -81,6 +81,20 @@ class AutoScalingPolicy(signal_responder.SignalResponder,
|
|||
_('Cooldown period, in seconds.'),
|
||||
update_allowed=True
|
||||
),
|
||||
MIN_ADJUSTMENT_STEP: properties.Schema(
|
||||
properties.Schema.INTEGER,
|
||||
_('Minimum number of resources that are added or removed '
|
||||
'when the AutoScaling group scales up or down. This can '
|
||||
'be used only when specifying percent_change_in_capacity '
|
||||
'for the adjustment_type property.'),
|
||||
constraints=[
|
||||
constraints.Range(
|
||||
min=0,
|
||||
),
|
||||
],
|
||||
update_allowed=True
|
||||
),
|
||||
|
||||
}
|
||||
|
||||
attributes_schema = {
|
||||
|
@ -89,6 +103,20 @@ class AutoScalingPolicy(signal_responder.SignalResponder,
|
|||
),
|
||||
}
|
||||
|
||||
def validate(self):
|
||||
"""
|
||||
Add validation for min_adjustment_step
|
||||
"""
|
||||
super(AutoScalingPolicy, self).validate()
|
||||
adjustment_type = self.properties.get(self.ADJUSTMENT_TYPE)
|
||||
adjustment_step = self.properties.get(self.MIN_ADJUSTMENT_STEP)
|
||||
if (adjustment_type != self.PERCENT_CHANGE_IN_CAPACITY
|
||||
and adjustment_step is not None):
|
||||
raise exception.ResourcePropertyValueDependency(
|
||||
prop1=self.MIN_ADJUSTMENT_STEP,
|
||||
prop2=self.ADJUSTMENT_TYPE,
|
||||
value=self.PERCENT_CHANGE_IN_CAPACITY)
|
||||
|
||||
def handle_create(self):
|
||||
super(AutoScalingPolicy, self).handle_create()
|
||||
self.resource_id_set(self._get_user_id())
|
||||
|
@ -146,7 +174,8 @@ class AutoScalingPolicy(signal_responder.SignalResponder,
|
|||
{'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)
|
||||
group.adjust(self.properties[self.SCALING_ADJUSTMENT], adjustment_type,
|
||||
self.properties[self.MIN_ADJUSTMENT_STEP])
|
||||
|
||||
self._cooldown_timestamp("%s : %s" %
|
||||
(self.properties[self.ADJUSTMENT_TYPE],
|
||||
|
|
|
@ -164,6 +164,66 @@ class TestGroupAdjust(common.HeatTestCase):
|
|||
resize.assert_called_once_with(3)
|
||||
cd_stamp.assert_called_once_with('ExactCapacity : 3')
|
||||
|
||||
def test_scale_up_min_adjustment(self):
|
||||
self.patchobject(grouputils, 'get_size', return_value=1)
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
cd_stamp = self.patchobject(self.group, '_cooldown_timestamp')
|
||||
notify = self.patch('heat.engine.notification.autoscaling.send')
|
||||
self.patchobject(self.group, '_cooldown_inprogress',
|
||||
return_value=False)
|
||||
self.group.adjust(33, adjustment_type='PercentChangeInCapacity',
|
||||
min_adjustment_step=2)
|
||||
|
||||
expected_notifies = [
|
||||
mock.call(
|
||||
capacity=1, suffix='start',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'my-group',
|
||||
message=u'Start resizing the group my-group',
|
||||
adjustment=33,
|
||||
stack=self.group.stack),
|
||||
mock.call(
|
||||
capacity=3, suffix='end',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'my-group',
|
||||
message=u'End resizing the group my-group',
|
||||
adjustment=33,
|
||||
stack=self.group.stack)]
|
||||
|
||||
self.assertEqual(expected_notifies, notify.call_args_list)
|
||||
resize.assert_called_once_with(3)
|
||||
cd_stamp.assert_called_once_with('PercentChangeInCapacity : 33')
|
||||
|
||||
def test_scale_down_min_adjustment(self):
|
||||
self.patchobject(grouputils, 'get_size', return_value=3)
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
cd_stamp = self.patchobject(self.group, '_cooldown_timestamp')
|
||||
notify = self.patch('heat.engine.notification.autoscaling.send')
|
||||
self.patchobject(self.group, '_cooldown_inprogress',
|
||||
return_value=False)
|
||||
self.group.adjust(-33, adjustment_type='PercentChangeInCapacity',
|
||||
min_adjustment_step=2)
|
||||
|
||||
expected_notifies = [
|
||||
mock.call(
|
||||
capacity=3, suffix='start',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'my-group',
|
||||
message=u'Start resizing the group my-group',
|
||||
adjustment=-33,
|
||||
stack=self.group.stack),
|
||||
mock.call(
|
||||
capacity=1, suffix='end',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'my-group',
|
||||
message=u'End resizing the group my-group',
|
||||
adjustment=-33,
|
||||
stack=self.group.stack)]
|
||||
|
||||
self.assertEqual(expected_notifies, notify.call_args_list)
|
||||
resize.assert_called_once_with(1)
|
||||
cd_stamp.assert_called_once_with('PercentChangeInCapacity : -33')
|
||||
|
||||
def test_scaling_policy_cooldown_ok(self):
|
||||
self.patchobject(grouputils, 'get_members', return_value=[])
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
|
|
|
@ -20,9 +20,11 @@ import six
|
|||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.tests.autoscaling import inline_templates
|
||||
from heat.tests import common
|
||||
from heat.tests import generic_resource
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
|
@ -33,6 +35,8 @@ as_params = inline_templates.as_params
|
|||
class TestAutoScalingPolicy(common.HeatTestCase):
|
||||
def setUp(self):
|
||||
super(TestAutoScalingPolicy, self).setUp()
|
||||
resource._register_class('ResourceWithPropsAndAttrs',
|
||||
generic_resource.ResourceWithPropsAndAttrs)
|
||||
cfg.CONF.set_default('heat_waitcondition_server_url',
|
||||
'http://server.test:8000/v1/waitcondition')
|
||||
|
||||
|
@ -43,6 +47,32 @@ class TestAutoScalingPolicy(common.HeatTestCase):
|
|||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
return rsrc
|
||||
|
||||
def test_validate_scaling_policy_ok(self):
|
||||
t = template_format.parse(as_template)
|
||||
t['resources']['my-policy']['properties'][
|
||||
'scaling_adjustment'] = 33
|
||||
t['resources']['my-policy']['properties'][
|
||||
'adjustment_type'] = 'percent_change_in_capacity'
|
||||
t['resources']['my-policy']['properties'][
|
||||
'min_adjustment_step'] = 2
|
||||
stack = utils.parse_stack(t)
|
||||
self.assertIsNone(stack.validate())
|
||||
|
||||
def test_validate_scaling_policy_error(self):
|
||||
t = template_format.parse(as_template)
|
||||
t['resources']['my-policy']['properties'][
|
||||
'scaling_adjustment'] = 1
|
||||
t['resources']['my-policy']['properties'][
|
||||
'adjustment_type'] = 'change_in_capacity'
|
||||
t['resources']['my-policy']['properties'][
|
||||
'min_adjustment_step'] = 2
|
||||
stack = utils.parse_stack(t)
|
||||
ex = self.assertRaises(exception.ResourcePropertyValueDependency,
|
||||
stack.validate)
|
||||
self.assertIn('min_adjustment_step property should only '
|
||||
'be specified for adjustment_type with '
|
||||
'value percent_change_in_capacity.', six.text_type(ex))
|
||||
|
||||
def test_scaling_policy_bad_group(self):
|
||||
t = template_format.parse(inline_templates.as_heat_template_bad_group)
|
||||
stack = utils.parse_stack(t)
|
||||
|
@ -92,7 +122,7 @@ class TestAutoScalingPolicy(common.HeatTestCase):
|
|||
return_value=False) as mock_cip:
|
||||
pol.handle_signal(details=test)
|
||||
mock_cip.assert_called_once_with()
|
||||
group.adjust.assert_called_once_with(1, 'ChangeInCapacity')
|
||||
group.adjust.assert_called_once_with(1, 'ChangeInCapacity', None)
|
||||
|
||||
|
||||
class TestCooldownMixin(common.HeatTestCase):
|
||||
|
|
|
@ -22,52 +22,86 @@ class TestCapacityChanges(common.HeatTestCase):
|
|||
# r rounded (+up, -down)
|
||||
# e EXACT_CAPACITY
|
||||
# p PERCENT_CHANGE_IN_CAPACITY
|
||||
# s MIN_ADJUSTMENT_STEP
|
||||
scenarios = [
|
||||
('+n', dict(current=2, adjustment=3,
|
||||
adjustment_type=asc.CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=10, expected=5)),
|
||||
('-n', dict(current=6, adjustment=-2,
|
||||
adjustment_type=asc.CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=5, expected=4)),
|
||||
('+nb', dict(current=2, adjustment=8,
|
||||
adjustment_type=asc.CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=5, expected=5)),
|
||||
('-nb', dict(current=2, adjustment=-10,
|
||||
adjustment_type=asc.CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=1, maximum=5, expected=1)),
|
||||
('e', dict(current=2, adjustment=4,
|
||||
adjustment_type=asc.EXACT_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=5, expected=4)),
|
||||
('+eb', dict(current=2, adjustment=11,
|
||||
adjustment_type=asc.EXACT_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=5, expected=5)),
|
||||
('-eb', dict(current=4, adjustment=1,
|
||||
adjustment_type=asc.EXACT_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=3, maximum=5, expected=3)),
|
||||
('+p', dict(current=4, adjustment=50,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=1, maximum=10, expected=6)),
|
||||
('-p', dict(current=4, adjustment=-25,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=1, maximum=10, expected=3)),
|
||||
('+pb', dict(current=4, adjustment=100,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=1, maximum=6, expected=6)),
|
||||
('-pb', dict(current=6, adjustment=-50,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=4, maximum=10, expected=4)),
|
||||
('-p+r', dict(current=2, adjustment=-33,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=10, expected=1)),
|
||||
('+p+r', dict(current=1, adjustment=33,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=10, expected=2)),
|
||||
('-p-r', dict(current=2, adjustment=-66,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=10, expected=1)),
|
||||
('+p-r', dict(current=1, adjustment=225,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=None,
|
||||
minimum=0, maximum=10, expected=3)),
|
||||
('+ps', dict(current=1, adjustment=100,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=3,
|
||||
minimum=0, maximum=10, expected=4)),
|
||||
('+p+rs', dict(current=1, adjustment=33,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=2,
|
||||
minimum=0, maximum=10, expected=3)),
|
||||
('+p-rs', dict(current=1, adjustment=325,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=2,
|
||||
minimum=0, maximum=10, expected=4)),
|
||||
('-p-rs', dict(current=3, adjustment=-25,
|
||||
adjustment_type=asc.PERCENT_CHANGE_IN_CAPACITY,
|
||||
min_adjustment_step=2,
|
||||
minimum=0, maximum=10, expected=1)),
|
||||
|
||||
|
||||
]
|
||||
|
||||
def test_calc(self):
|
||||
|
@ -75,4 +109,5 @@ class TestCapacityChanges(common.HeatTestCase):
|
|||
asc._calculate_new_capacity(
|
||||
self.current, self.adjustment,
|
||||
self.adjustment_type,
|
||||
self.min_adjustment_step,
|
||||
self.minimum, self.maximum))
|
||||
|
|
|
@ -345,6 +345,66 @@ class TestGroupAdjust(common.HeatTestCase):
|
|||
resize.assert_called_once_with(3)
|
||||
cd_stamp.assert_called_once_with('ExactCapacity : 3')
|
||||
|
||||
def test_scale_up_min_adjustment(self):
|
||||
self.patchobject(grouputils, 'get_size', return_value=1)
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
cd_stamp = self.patchobject(self.group, '_cooldown_timestamp')
|
||||
notify = self.patch('heat.engine.notification.autoscaling.send')
|
||||
self.patchobject(self.group, '_cooldown_inprogress',
|
||||
return_value=False)
|
||||
self.group.adjust(33, adjustment_type='PercentChangeInCapacity',
|
||||
min_adjustment_step=2)
|
||||
|
||||
expected_notifies = [
|
||||
mock.call(
|
||||
capacity=1, suffix='start',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'WebServerGroup',
|
||||
message=u'Start resizing the group WebServerGroup',
|
||||
adjustment=33,
|
||||
stack=self.group.stack),
|
||||
mock.call(
|
||||
capacity=3, suffix='end',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'WebServerGroup',
|
||||
message=u'End resizing the group WebServerGroup',
|
||||
adjustment=33,
|
||||
stack=self.group.stack)]
|
||||
|
||||
self.assertEqual(expected_notifies, notify.call_args_list)
|
||||
resize.assert_called_once_with(3)
|
||||
cd_stamp.assert_called_once_with('PercentChangeInCapacity : 33')
|
||||
|
||||
def test_scale_down_min_adjustment(self):
|
||||
self.patchobject(grouputils, 'get_size', return_value=5)
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
cd_stamp = self.patchobject(self.group, '_cooldown_timestamp')
|
||||
notify = self.patch('heat.engine.notification.autoscaling.send')
|
||||
self.patchobject(self.group, '_cooldown_inprogress',
|
||||
return_value=False)
|
||||
self.group.adjust(-33, adjustment_type='PercentChangeInCapacity',
|
||||
min_adjustment_step=2)
|
||||
|
||||
expected_notifies = [
|
||||
mock.call(
|
||||
capacity=5, suffix='start',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'WebServerGroup',
|
||||
message=u'Start resizing the group WebServerGroup',
|
||||
adjustment=-33,
|
||||
stack=self.group.stack),
|
||||
mock.call(
|
||||
capacity=3, suffix='end',
|
||||
adjustment_type='PercentChangeInCapacity',
|
||||
groupname=u'WebServerGroup',
|
||||
message=u'End resizing the group WebServerGroup',
|
||||
adjustment=-33,
|
||||
stack=self.group.stack)]
|
||||
|
||||
self.assertEqual(expected_notifies, notify.call_args_list)
|
||||
resize.assert_called_once_with(3)
|
||||
cd_stamp.assert_called_once_with('PercentChangeInCapacity : -33')
|
||||
|
||||
def test_scaling_policy_cooldown_ok(self):
|
||||
self.patchobject(grouputils, 'get_members', return_value=[])
|
||||
resize = self.patchobject(self.group, 'resize')
|
||||
|
|
|
@ -43,6 +43,34 @@ class TestAutoScalingPolicy(common.HeatTestCase):
|
|||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
return rsrc
|
||||
|
||||
def test_validate_scaling_policy_ok(self):
|
||||
t = template_format.parse(inline_templates.as_template)
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'ScalingAdjustment'] = 33
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'AdjustmentType'] = 'PercentChangeInCapacity'
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'MinAdjustmentStep'] = 2
|
||||
stack = utils.parse_stack(t, params=as_params)
|
||||
self.policy = stack['WebServerScaleUpPolicy']
|
||||
self.assertIsNone(self.policy.validate())
|
||||
|
||||
def test_validate_scaling_policy_error(self):
|
||||
t = template_format.parse(inline_templates.as_template)
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'ScalingAdjustment'] = 1
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'AdjustmentType'] = 'ChangeInCapacity'
|
||||
t['Resources']['WebServerScaleUpPolicy']['Properties'][
|
||||
'MinAdjustmentStep'] = 2
|
||||
stack = utils.parse_stack(t, params=as_params)
|
||||
self.policy = stack['WebServerScaleUpPolicy']
|
||||
ex = self.assertRaises(exception.ResourcePropertyValueDependency,
|
||||
self.policy.validate)
|
||||
self.assertIn('MinAdjustmentStep property should only '
|
||||
'be specified for AdjustmentType with '
|
||||
'value PercentChangeInCapacity.', six.text_type(ex))
|
||||
|
||||
def test_scaling_policy_bad_group(self):
|
||||
t = template_format.parse(inline_templates.as_template_bad_group)
|
||||
stack = utils.parse_stack(t, params=as_params)
|
||||
|
@ -93,7 +121,7 @@ class TestAutoScalingPolicy(common.HeatTestCase):
|
|||
return_value=False) as mock_cip:
|
||||
pol.handle_signal(details=test)
|
||||
mock_cip.assert_called_once_with()
|
||||
group.adjust.assert_called_once_with(1, 'ChangeInCapacity')
|
||||
group.adjust.assert_called_once_with(1, 'ChangeInCapacity', None)
|
||||
|
||||
|
||||
class TestCooldownMixin(common.HeatTestCase):
|
||||
|
|
Loading…
Reference in New Issue