Correct group's updates from a failed state

This patch modifies autoscaling/instance group to
handle updates from a failed state.

Change-Id: I235b3e97276361022e708266bf06bca6556bcf76
Closes-Bug: #1383618
This commit is contained in:
huangtianhua 2014-11-19 15:12:51 +08:00
parent c16f539c53
commit 1eabdaf45f
5 changed files with 56 additions and 30 deletions

View File

@ -14,14 +14,15 @@
import six
def get_size(group):
def get_size(group, include_failed=False):
"""Get number of member resources managed by the specified group.
The list of members are not sorted or returned.
The size exclude failed members default, set include_failed=True
to get total size.
"""
if group.nested():
resources = [r for r in six.itervalues(group.nested())
if r.status != r.FAILED]
if include_failed or r.status != r.FAILED]
return len(resources)
else:
return 0

View File

@ -230,16 +230,13 @@ class AutoScalingGroup(instgrp.InstanceGroup, cooldown.CooldownMixin):
self.context)
self.update_policy = up
if prop_diff:
self.properties = json_snippet.properties(self.properties_schema,
self.context)
if prop_diff:
# Replace instances first if launch configuration has changed
self._try_rolling_update(prop_diff)
if (self.DESIRED_CAPACITY in prop_diff and
self.properties[self.DESIRED_CAPACITY] is not None):
if self.properties[self.DESIRED_CAPACITY] is not None:
self.adjust(self.properties[self.DESIRED_CAPACITY],
adjustment_type=EXACT_CAPACITY)
else:
@ -263,8 +260,9 @@ class AutoScalingGroup(instgrp.InstanceGroup, cooldown.CooldownMixin):
new_capacity = _calculate_new_capacity(capacity, adjustment,
adjustment_type, lower, upper)
if new_capacity == capacity:
total = grouputils.get_size(self, include_failed=True)
# if there are failed resources in nested_stack, has to change
if new_capacity == total:
LOG.debug('no change in capacity %d' % capacity)
return

View File

@ -205,19 +205,19 @@ class InstanceGroup(stack_resource.StackResource):
self.context)
self.update_policy = up
if prop_diff:
self.properties = json_snippet.properties(self.properties_schema,
self.context)
if prop_diff:
# Replace instances first if launch configuration has changed
self._try_rolling_update(prop_diff)
# Get the current capacity, we may need to adjust if
# Size has changed
if self.SIZE in prop_diff:
curr_size = grouputils.get_size(self)
if curr_size != self.properties[self.SIZE]:
if self.properties[self.SIZE] is not None:
self.resize(self.properties[self.SIZE])
else:
curr_size = grouputils.get_size(self)
self.resize(curr_size)
def _tags(self):
"""

View File

@ -14,6 +14,7 @@
import copy
import datetime
import mock
import mox
from oslo.config import cfg
from oslo.utils import timeutils
@ -26,6 +27,7 @@ from heat.common import template_format
from heat.engine.notification import autoscaling as notification
from heat.engine import parser
from heat.engine import resource
from heat.engine.resources.aws import autoscaling_group as asg
from heat.engine.resources import instance
from heat.engine.resources import loadbalancer
from heat.engine.resources.neutron import loadbalancer as neutron_lb
@ -71,6 +73,16 @@ class AutoScalingTest(common.HeatTestCase):
cfg.CONF.set_default('heat_waitcondition_server_url',
'http://server.test:8000/v1/waitcondition')
self.stub_keystoneclient()
t = template_format.parse(as_template)
stack = utils.parse_stack(t, params=self.params)
self.defn = rsrc_defn.ResourceDefinition(
'asg', 'AWS::AutoScaling::AutoScalingGroup',
{'AvailabilityZones': ['nova'],
'LaunchConfigurationName': 'config',
'MaxSize': 5,
'MinSize': 1,
'DesiredCapacity': 2})
self.asg = asg.AutoScalingGroup('asg', self.defn, stack)
def create_scaling_group(self, t, stack, resource_name):
# create the launch configuration resource
@ -490,6 +502,15 @@ class AutoScalingTest(common.HeatTestCase):
self.m.VerifyAll()
def test_update_in_failed(self):
self.asg.state_set('CREATE', 'FAILED')
# to update the failed asg
self.asg.adjust = mock.Mock(return_value=None)
self.asg.handle_update(self.defn, None, None)
self.asg.adjust.assert_called_once_with(
2, adjustment_type='ExactCapacity')
def test_lb_reload_static_resolve(self):
t = template_format.parse(as_template)
properties = t['Resources']['ElasticLoadBalancer']['Properties']
@ -710,7 +731,7 @@ class AutoScalingTest(common.HeatTestCase):
up_policy.metadata_get().AndReturn(previous_meta)
rsrc.metadata_get().AndReturn(previous_meta)
#stub for the metadata accesses while creating the two instances
# stub for the metadata accesses while creating the two instances
resource.Resource.metadata_get()
resource.Resource.metadata_get()

View File

@ -33,12 +33,12 @@ class TestInstanceGroup(common.HeatTestCase):
super(TestInstanceGroup, self).setUp()
t = template_format.parse(inline_templates.as_template)
stack = utils.parse_stack(t, params=inline_templates.as_params)
defn = rsrc_defn.ResourceDefinition(
self.defn = rsrc_defn.ResourceDefinition(
'asg', 'OS::Heat::InstanceGroup',
{'Size': 2, 'AvailabilityZones': ['zoneb'],
'LaunchConfigurationName': 'config'})
self.instance_group = instgrp.InstanceGroup('asg',
defn, stack)
self.defn, stack)
def test_child_template(self):
self.instance_group._create_template = mock.Mock(return_value='tpl')
@ -104,6 +104,14 @@ class TestInstanceGroup(common.HeatTestCase):
self.instance_group.create_with_template.assert_called_once_with(
'{}', expect_env)
def test_update_in_failed(self):
self.instance_group.state_set('CREATE', 'FAILED')
# to update the failed instance_group
self.instance_group.resize = mock.Mock(return_value=None)
self.instance_group.handle_update(self.defn, None, None)
self.instance_group.resize.assert_called_once_with(2)
def test_handle_delete(self):
self.instance_group.delete_nested = mock.Mock(return_value=None)
self.instance_group.handle_delete()
@ -112,8 +120,6 @@ class TestInstanceGroup(common.HeatTestCase):
def test_handle_update_size(self):
self.instance_group._try_rolling_update = mock.Mock(return_value=None)
self.instance_group.resize = mock.Mock(return_value=None)
get_size = self.patchobject(grouputils, 'get_size')
get_size.return_value = 2
props = {'Size': 5}
defn = rsrc_defn.ResourceDefinition(