Allow scale-down of ASG as part of update
When user wants to do manual scale-up of ASG by increasing the
desired_capacity and also update some member properties as
part of a stack update, if it fails due to insufficient
resources when resizing, trying to manually scale-down by
decreasing the desired_capacity won't work, as it would
always expect to update the group with the earlier size
before scaling down.
This patch uses the target_size when building the batches.
Task: 39867
Change-Id: Id851530b424f68b5e0e967fe34431483bfffd852
(cherry picked from commit 26d8f64fc9
)
This commit is contained in:
parent
33972cce57
commit
a0ff3081e2
|
@ -186,6 +186,10 @@ class AutoScalingGroup(cooldown.CooldownMixin, instgrp.InstanceGroup):
|
||||||
schema=rolling_update_schema)
|
schema=rolling_update_schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_size(self):
|
||||||
|
"""Get desired capacity."""
|
||||||
|
return self.properties[self.DESIRED_CAPACITY]
|
||||||
|
|
||||||
def handle_create(self):
|
def handle_create(self):
|
||||||
return self.create_with_template(self.child_template())
|
return self.create_with_template(self.child_template())
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,10 @@ class InstanceGroup(stack_resource.StackResource):
|
||||||
schema=rolling_update_schema)
|
schema=rolling_update_schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_size(self):
|
||||||
|
"""Get desired size."""
|
||||||
|
return self.properties[self.SIZE]
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
"""Add validation for update_policy."""
|
"""Add validation for update_policy."""
|
||||||
self.validate_launchconfig()
|
self.validate_launchconfig()
|
||||||
|
@ -335,7 +339,12 @@ class InstanceGroup(stack_resource.StackResource):
|
||||||
old_template = group_data.template()
|
old_template = group_data.template()
|
||||||
|
|
||||||
capacity = group_data.size(include_failed=True)
|
capacity = group_data.size(include_failed=True)
|
||||||
batches = list(self._get_batches(capacity, batch_size, min_in_service))
|
|
||||||
|
target_capacity = min(self.get_size() or capacity, capacity)
|
||||||
|
|
||||||
|
batches = list(self._get_batches(target_capacity,
|
||||||
|
capacity, batch_size,
|
||||||
|
min_in_service))
|
||||||
|
|
||||||
update_timeout = self._update_timeout(len(batches), pause_sec)
|
update_timeout = self._update_timeout(len(batches), pause_sec)
|
||||||
|
|
||||||
|
@ -359,7 +368,7 @@ class InstanceGroup(stack_resource.StackResource):
|
||||||
self._lb_reload()
|
self._lb_reload()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_batches(capacity, batch_size, min_in_service):
|
def _get_batches(target_capacity, capacity, batch_size, min_in_service):
|
||||||
"""Return an iterator over the batches in a batched update.
|
"""Return an iterator over the batches in a batched update.
|
||||||
|
|
||||||
Each batch is a tuple comprising the total size of the group after
|
Each batch is a tuple comprising the total size of the group after
|
||||||
|
@ -368,15 +377,14 @@ class InstanceGroup(stack_resource.StackResource):
|
||||||
updating an existing one).
|
updating an existing one).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
efft_capacity = capacity
|
|
||||||
updated = 0
|
updated = 0
|
||||||
|
|
||||||
while rolling_update.needs_update(capacity, efft_capacity, updated):
|
while rolling_update.needs_update(target_capacity, capacity, updated):
|
||||||
batch = rolling_update.next_batch(capacity, efft_capacity,
|
batch = rolling_update.next_batch(target_capacity, capacity,
|
||||||
updated, batch_size,
|
updated, batch_size,
|
||||||
min_in_service)
|
min_in_service)
|
||||||
yield batch
|
yield batch
|
||||||
efft_capacity, num_updates = batch
|
capacity, num_updates = batch
|
||||||
updated += num_updates
|
updated += num_updates
|
||||||
|
|
||||||
def _check_for_completion(self, updater):
|
def _check_for_completion(self, updater):
|
||||||
|
|
|
@ -482,42 +482,45 @@ class ResizeWithFailedInstancesTest(InstanceGroupWithNestedStack):
|
||||||
class TestGetBatches(common.HeatTestCase):
|
class TestGetBatches(common.HeatTestCase):
|
||||||
|
|
||||||
scenarios = [
|
scenarios = [
|
||||||
('4_1_0', dict(curr_cap=4, bat_size=1, min_serv=0,
|
('4_4_1_0', dict(tgt_cap=4, curr_cap=4, bat_size=1, min_serv=0,
|
||||||
batches=[(4, 1)] * 4)),
|
batches=[(4, 1)] * 4)),
|
||||||
('4_1_4', dict(curr_cap=4, bat_size=1, min_serv=4,
|
('3_4_1_0', dict(tgt_cap=3, curr_cap=4, bat_size=1, min_serv=0,
|
||||||
batches=([(5, 1)] * 4) + [(4, 0)])),
|
batches=[(3, 1)] * 3)),
|
||||||
('4_1_5', dict(curr_cap=4, bat_size=1, min_serv=5,
|
('4_4_1_4', dict(tgt_cap=4, curr_cap=4, bat_size=1, min_serv=4,
|
||||||
batches=([(5, 1)] * 4) + [(4, 0)])),
|
batches=([(5, 1)] * 4) + [(4, 0)])),
|
||||||
('4_2_0', dict(curr_cap=4, bat_size=2, min_serv=0,
|
('4_4_1_5', dict(tgt_cap=4, curr_cap=4, bat_size=1, min_serv=5,
|
||||||
batches=[(4, 2)] * 2)),
|
batches=([(5, 1)] * 4) + [(4, 0)])),
|
||||||
('4_2_4', dict(curr_cap=4, bat_size=2, min_serv=4,
|
('4_4_2_0', dict(tgt_cap=4, curr_cap=4, bat_size=2, min_serv=0,
|
||||||
batches=([(6, 2)] * 2) + [(4, 0)])),
|
batches=[(4, 2)] * 2)),
|
||||||
('5_2_0', dict(curr_cap=5, bat_size=2, min_serv=0,
|
('4_4_2_4', dict(tgt_cap=4, curr_cap=4, bat_size=2, min_serv=4,
|
||||||
batches=([(5, 2)] * 2) + [(5, 1)])),
|
batches=([(6, 2)] * 2) + [(4, 0)])),
|
||||||
('5_2_4', dict(curr_cap=5, bat_size=2, min_serv=4,
|
('5_5_2_0', dict(tgt_cap=5, curr_cap=5, bat_size=2, min_serv=0,
|
||||||
batches=([(6, 2)] * 2) + [(5, 1)])),
|
batches=([(5, 2)] * 2) + [(5, 1)])),
|
||||||
('3_2_0', dict(curr_cap=3, bat_size=2, min_serv=0,
|
('5_5_2_4', dict(tgt_cap=5, curr_cap=5, bat_size=2, min_serv=4,
|
||||||
batches=[(3, 2), (3, 1)])),
|
batches=([(6, 2)] * 2) + [(5, 1)])),
|
||||||
('3_2_4', dict(curr_cap=3, bat_size=2, min_serv=4,
|
('3_3_2_0', dict(tgt_cap=3, curr_cap=3, bat_size=2, min_serv=0,
|
||||||
batches=[(5, 2), (4, 1), (3, 0)])),
|
batches=[(3, 2), (3, 1)])),
|
||||||
('4_4_0', dict(curr_cap=4, bat_size=4, min_serv=0,
|
('3_3_2_4', dict(tgt_cap=3, curr_cap=3, bat_size=2, min_serv=4,
|
||||||
batches=[(4, 4)])),
|
batches=[(5, 2), (4, 1), (3, 0)])),
|
||||||
('4_5_0', dict(curr_cap=4, bat_size=5, min_serv=0,
|
('4_4_4_0', dict(tgt_cap=4, curr_cap=4, bat_size=4, min_serv=0,
|
||||||
batches=[(4, 4)])),
|
batches=[(4, 4)])),
|
||||||
('4_4_1', dict(curr_cap=4, bat_size=4, min_serv=1,
|
('4_4_5_0', dict(tgt_cap=4, curr_cap=4, bat_size=5, min_serv=0,
|
||||||
batches=[(5, 4), (4, 0)])),
|
batches=[(4, 4)])),
|
||||||
('4_6_1', dict(curr_cap=4, bat_size=6, min_serv=1,
|
('4_4_4_1', dict(tgt_cap=4, curr_cap=4, bat_size=4, min_serv=1,
|
||||||
batches=[(5, 4), (4, 0)])),
|
batches=[(5, 4), (4, 0)])),
|
||||||
('4_4_2', dict(curr_cap=4, bat_size=4, min_serv=2,
|
('4_4_6_1', dict(tgt_cap=4, curr_cap=4, bat_size=6, min_serv=1,
|
||||||
batches=[(6, 4), (4, 0)])),
|
batches=[(5, 4), (4, 0)])),
|
||||||
('4_4_4', dict(curr_cap=4, bat_size=4, min_serv=4,
|
('4_4_4_2', dict(tgt_cap=4, curr_cap=4, bat_size=4, min_serv=2,
|
||||||
batches=[(8, 4), (4, 0)])),
|
batches=[(6, 4), (4, 0)])),
|
||||||
('4_5_6', dict(curr_cap=4, bat_size=5, min_serv=6,
|
('4_4_4_4', dict(tgt_cap=4, curr_cap=4, bat_size=4, min_serv=4,
|
||||||
batches=[(8, 4), (4, 0)])),
|
batches=[(8, 4), (4, 0)])),
|
||||||
|
('4_4_5_6', dict(tgt_cap=4, curr_cap=4, bat_size=5, min_serv=6,
|
||||||
|
batches=[(8, 4), (4, 0)])),
|
||||||
]
|
]
|
||||||
|
|
||||||
def test_get_batches(self):
|
def test_get_batches(self):
|
||||||
batches = list(instgrp.InstanceGroup._get_batches(self.curr_cap,
|
batches = list(instgrp.InstanceGroup._get_batches(self.tgt_cap,
|
||||||
|
self.curr_cap,
|
||||||
self.bat_size,
|
self.bat_size,
|
||||||
self.min_serv))
|
self.min_serv))
|
||||||
self.assertEqual(self.batches, batches)
|
self.assertEqual(self.batches, batches)
|
||||||
|
|
|
@ -298,5 +298,6 @@ class InstanceGroupReplaceTest(common.HeatTestCase):
|
||||||
|
|
||||||
group = instgrp.InstanceGroup('asg', defn, stack)
|
group = instgrp.InstanceGroup('asg', defn, stack)
|
||||||
group._group_data().size = mock.Mock(return_value=12)
|
group._group_data().size = mock.Mock(return_value=12)
|
||||||
|
group.get_size = mock.Mock(return_value=12)
|
||||||
self.assertRaises(ValueError,
|
self.assertRaises(ValueError,
|
||||||
group._replace, 10, 1, 14 * 60)
|
group._replace, 10, 1, 14 * 60)
|
||||||
|
|
Loading…
Reference in New Issue