from heat.openstack.common import log as logging
from heat.openstack.common import timeutils
+from heat.engine.properties import Properties
logger = logging.getLogger(__name__)
'Schema': tags_schema}}
}
+ # template keys and properties supported for handle_update,
+ # note trailing comma is required for a single item to get a tuple
+ update_allowed_keys = ('Properties',)
+ update_allowed_properties = ('MaxSize', 'MinSize',
+ 'Cooldown', 'DesiredCapacity',)
+
def __init__(self, name, json_snippet, stack):
super(AutoScalingGroup, self).__init__(name, json_snippet, stack)
# resource_id is a list of resources
raise_on_error=True)
def handle_update(self, json_snippet):
- return self.UPDATE_REPLACE
+ try:
+ tmpl_diff = self.update_template_diff(json_snippet)
+ except NotImplementedError:
+ logger.error("Could not update %s, invalid key" % self.name)
+ return self.UPDATE_REPLACE
+
+ try:
+ prop_diff = self.update_template_diff_properties(json_snippet)
+ except NotImplementedError:
+ logger.error("Could not update %s, invalid Property" % self.name)
+ return self.UPDATE_REPLACE
+
+ # If Properties has changed, update self.properties, so we
+ # get the new values during any subsequent adjustment
+ if prop_diff:
+ self.properties = Properties(self.properties_schema,
+ json_snippet.get('Properties', {}),
+ self.stack.resolve_runtime_data,
+ self.name)
+
+ # Get the current capacity, we may need to adjust if
+ # MinSize or MaxSize has changed
+ inst_list = []
+ if self.resource_id is not None:
+ inst_list = sorted(self.resource_id.split(','))
+
+ capacity = len(inst_list)
+
+ # Figure out if an adjustment is required
+ new_capacity = None
+ if 'MinSize' in prop_diff:
+ if capacity < int(self.properties['MinSize']):
+ new_capacity = int(self.properties['MinSize'])
+ if 'MaxSize' in prop_diff:
+ if capacity > int(self.properties['MinSize']):
+ new_capacity = int(self.properties['MinSize'])
+ if 'DesiredCapacity' in prop_diff:
+ if self.properties['DesiredCapacity']:
+ new_capacity = int(self.properties['DesiredCapacity'])
+
+ if new_capacity is not None:
+ self.adjust(new_capacity, adjustment_type='ExactCapacity',
+ raise_on_error=True)
+
+ return self.UPDATE_COMPLETE
def adjust(self, adjustment, adjustment_type='ChangeInCapacity',
raise_on_error=False):
import os
import datetime
+import copy
import unittest
import mox
resource.delete()
self.m.VerifyAll()
+ def test_scaling_group_update_ok_maxsize(self):
+ t = self.load_template()
+ properties = t['Resources']['WebServerGroup']['Properties']
+ properties['MinSize'] = '1'
+ properties['MaxSize'] = '3'
+ stack = self.parse_stack(t)
+
+ self._stub_lb_reload(['WebServerGroup-0'])
+ now = timeutils.utcnow()
+ self._stub_meta_expected(now, 'ExactCapacity : 1')
+ self._stub_create(1)
+ self.m.ReplayAll()
+ resource = self.create_scaling_group(t, stack, 'WebServerGroup')
+ self.assertEqual('WebServerGroup-0', resource.resource_id)
+
+ # Reduce the max size to 2, should complete without adjusting
+ update_snippet = copy.deepcopy(resource.parsed_template())
+ update_snippet['Properties']['MaxSize'] = '2'
+ self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
+ resource.handle_update(update_snippet))
+ self.assertEqual('WebServerGroup-0', resource.resource_id)
+
+ resource.delete()
+ self.m.VerifyAll()
+
+ def test_scaling_group_update_ok_minsize(self):
+ t = self.load_template()
+ properties = t['Resources']['WebServerGroup']['Properties']
+ properties['MinSize'] = '1'
+ properties['MaxSize'] = '3'
+ stack = self.parse_stack(t)
+
+ self._stub_lb_reload(['WebServerGroup-0'])
+ now = timeutils.utcnow()
+ self._stub_meta_expected(now, 'ExactCapacity : 1')
+ self._stub_create(1)
+ self.m.ReplayAll()
+ resource = self.create_scaling_group(t, stack, 'WebServerGroup')
+ self.assertEqual('WebServerGroup-0', resource.resource_id)
+
+ # Increase min size to 2, should trigger an ExactCapacity adjust
+ self._stub_lb_reload(['WebServerGroup-0', 'WebServerGroup-1'])
+ self._stub_meta_expected(now, 'ExactCapacity : 2')
+ self._stub_create(1)
+ self.m.ReplayAll()
+
+ update_snippet = copy.deepcopy(resource.parsed_template())
+ update_snippet['Properties']['MinSize'] = '2'
+ self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
+ resource.handle_update(update_snippet))
+ self.assertEqual('WebServerGroup-0,WebServerGroup-1',
+ resource.resource_id)
+
+ resource.delete()
+ self.m.VerifyAll()
+
+ def test_scaling_group_update_ok_desired(self):
+ t = self.load_template()
+ properties = t['Resources']['WebServerGroup']['Properties']
+ properties['MinSize'] = '1'
+ properties['MaxSize'] = '3'
+ stack = self.parse_stack(t)
+
+ self._stub_lb_reload(['WebServerGroup-0'])
+ now = timeutils.utcnow()
+ self._stub_meta_expected(now, 'ExactCapacity : 1')
+ self._stub_create(1)
+ self.m.ReplayAll()
+ resource = self.create_scaling_group(t, stack, 'WebServerGroup')
+ self.assertEqual('WebServerGroup-0', resource.resource_id)
+
+ # Increase min size to 2 via DesiredCapacity, should adjust
+ self._stub_lb_reload(['WebServerGroup-0', 'WebServerGroup-1'])
+ self._stub_meta_expected(now, 'ExactCapacity : 2')
+ self._stub_create(1)
+ self.m.ReplayAll()
+
+ update_snippet = copy.deepcopy(resource.parsed_template())
+ update_snippet['Properties']['DesiredCapacity'] = '2'
+ self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
+ resource.handle_update(update_snippet))
+ self.assertEqual('WebServerGroup-0,WebServerGroup-1',
+ resource.resource_id)
+
+ resource.delete()
+ self.m.VerifyAll()
+
+ def test_scaling_group_update_ok_desired_remove(self):
+ t = self.load_template()
+ properties = t['Resources']['WebServerGroup']['Properties']
+ properties['DesiredCapacity'] = '2'
+ stack = self.parse_stack(t)
+
+ self._stub_lb_reload(['WebServerGroup-0', 'WebServerGroup-1'])
+ now = timeutils.utcnow()
+ self._stub_meta_expected(now, 'ExactCapacity : 2')
+ self._stub_create(2)
+ self.m.ReplayAll()
+ resource = self.create_scaling_group(t, stack, 'WebServerGroup')
+ self.assertEqual('WebServerGroup-0,WebServerGroup-1',
+ resource.resource_id)
+
+ # Remove DesiredCapacity from the updated template, which should
+ # have no effect, it's an optional parameter
+ update_snippet = copy.deepcopy(resource.parsed_template())
+ del(update_snippet['Properties']['DesiredCapacity'])
+ self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
+ resource.handle_update(update_snippet))
+ self.assertEqual('WebServerGroup-0,WebServerGroup-1',
+ resource.resource_id)
+
+ resource.delete()
+ self.m.VerifyAll()
+
+ def test_scaling_group_update_ok_cooldown(self):
+ t = self.load_template()
+ properties = t['Resources']['WebServerGroup']['Properties']
+ properties['Cooldown'] = '60'
+ stack = self.parse_stack(t)
+
+ self._stub_lb_reload(['WebServerGroup-0'])
+ now = timeutils.utcnow()
+ self._stub_meta_expected(now, 'ExactCapacity : 1')
+ self._stub_create(1)
+ self.m.ReplayAll()
+ resource = self.create_scaling_group(t, stack, 'WebServerGroup')
+
+ self.assertEqual('WebServerGroup', resource.FnGetRefId())
+ self.assertEqual('WebServerGroup-0', resource.resource_id)
+ update_snippet = copy.deepcopy(resource.parsed_template())
+ old_cd = update_snippet['Properties']['Cooldown']
+ update_snippet['Properties']['Cooldown'] = '61'
+ self.assertEqual(asc.AutoScalingGroup.UPDATE_COMPLETE,
+ resource.handle_update(update_snippet))
+
+ resource.delete()
+ self.m.VerifyAll()
+
def test_scaling_group_adjust(self):
t = self.load_template()
stack = self.parse_stack(t)