def adjust(self, adjustment, adjustment_type=sc_util.CFN_CHANGE_IN_CAPACITY, min_adjustment_step=None, signal=False): """Adjust the size of the scaling group if the cooldown permits.""" if not self._is_scaling_allowed(): LOG.info(_LI("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s"), {'name': self.name, 'cooldown': self.properties[self.COOLDOWN]}) if signal: raise exception.NoActionRequired() else: return capacity = grouputils.get_size(self) lower = self.properties[self.MIN_SIZE] upper = self.properties[self.MAX_SIZE] new_capacity = sc_util.calculate_new_capacity(capacity, adjustment, adjustment_type, min_adjustment_step, lower, upper) changed_size = new_capacity != capacity # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId()}, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({'suffix': 'error', 'message': six.text_type(resize_ex), 'capacity': grouputils.get_size(self), }) notification.send(**notif) except Exception: LOG.exception(_LE('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname']}, }) notification.send(**notif) finally: self._finished_scaling("%s : %s" % (adjustment_type, adjustment), changed_size=changed_size) return changed_size
def test_scale_up(self): stack = self.create_stack(self.parsed) scale_up = stack['scale-up'] group = stack['my-group'] self.assertEqual(1, grouputils.get_size(group)) scale_up.signal() self.assertEqual(2, grouputils.get_size(group))
def test_scaling_group_scale_up_failure(self): stack = self.create_stack(self.parsed) mock_create = self.patchobject(generic_resource.ResourceWithProps, 'handle_create') rsrc = stack['my-group'] self.assertEqual(1, grouputils.get_size(rsrc)) mock_create.side_effect = exception.Error('Bang') self.assertRaises(exception.Error, rsrc.adjust, 1) self.assertEqual(1, grouputils.get_size(rsrc))
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, grouputils.get_size(group)) policy.signal() self.assertEqual(2, grouputils.get_size(group))
def test_scaling_group_truncate_adjustment(self): # Create initial group, 2 instances properties = self.parsed['resources']['my-group']['properties'] properties['desired_capacity'] = 2 rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(2, grouputils.get_size(rsrc)) rsrc.adjust(4) self.assertEqual(5, grouputils.get_size(rsrc)) rsrc.adjust(-5) self.assertEqual(1, grouputils.get_size(rsrc)) rsrc.adjust(0) self.assertEqual(1, grouputils.get_size(rsrc))
def _do_test_scaling_group_percent(self, decrease, lowest, increase, create, highest): # Create initial group, 2 instances properties = self.parsed['resources']['my-group']['properties'] properties['desired_capacity'] = 2 rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(2, grouputils.get_size(rsrc)) # reduce by decrease % rsrc.adjust(decrease, 'percentage_change_in_capacity') self.assertEqual(lowest, grouputils.get_size(rsrc)) # raise by increase % rsrc.adjust(increase, 'percentage_change_in_capacity') self.assertEqual(highest, grouputils.get_size(rsrc))
def test_scaling_group_update_ok_desired(self): properties = self.parsed['resources']['my-group']['properties'] properties['min_size'] = 1 properties['max_size'] = 3 rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(1, grouputils.get_size(rsrc)) props = copy.copy(rsrc.properties.data) props['desired_capacity'] = 2 update_snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props) scheduler.TaskRunner(rsrc.update, update_snippet)() self.assertEqual(2, grouputils.get_size(rsrc)) self.assertEqual(2, rsrc.properties['desired_capacity'])
def test_normal_group(self): group = mock.Mock() t = template_format.parse(nested_stack) stack = utils.parse_stack(t) # group size self.patchobject(group, 'nested', return_value=stack) self.assertEqual(2, grouputils.get_size(group)) # member list (sorted) members = [r for r in six.itervalues(stack)] expected = sorted(members, key=lambda r: (r.created_time, r.name)) actual = grouputils.get_members(group) self.assertEqual(expected, actual) # refids actual_ids = grouputils.get_member_refids(group) self.assertEqual(['ID-r0', 'ID-r1'], actual_ids) partial_ids = grouputils.get_member_refids(group, exclude=['ID-r1']) self.assertEqual(['ID-r0'], partial_ids) # names names = grouputils.get_member_names(group) self.assertEqual(['r0', 'r1'], names) # defn snippets as list expected = rsrc_defn.ResourceDefinition( None, "OverwrittenFnGetRefIdType") member_defs = grouputils.get_member_definitions(group) self.assertEqual([(x, expected) for x in names], member_defs)
def check_create_complete(self, task): """Invoke the cooldown after creation succeeds.""" done = super(AutoScalingGroup, self).check_create_complete(task) if done: self._cooldown_timestamp( "%s : %s" % (EXACT_CAPACITY, grouputils.get_size(self))) return done
def handle_update(self, json_snippet, tmpl_diff, prop_diff): """Updates self.properties, if Properties has changed. If Properties has changed, update self.properties, so we get the new values during any subsequent adjustment. """ if tmpl_diff: # parse update policy if tmpl_diff.update_policy_changed(): up = json_snippet.update_policy(self.update_policy_schema, self.context) self.update_policy = up 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) # Update will happen irrespective of whether auto-scaling # is in progress or not. capacity = grouputils.get_size(self) desired_capacity = self.properties[self.DESIRED_CAPACITY] or capacity new_capacity = self._get_new_capacity(capacity, desired_capacity) self.resize(new_capacity)
def test_scaling_delete_empty(self): properties = self.parsed['resources']['my-group']['properties'] properties['min_size'] = 0 properties['max_size'] = 0 rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(0, grouputils.get_size(rsrc)) rsrc.delete()
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 tmpl_diff: # parse update policy if 'UpdatePolicy' in tmpl_diff: up = json_snippet.update_policy(self.update_policy_schema, self.context) self.update_policy = up if prop_diff: self.properties = json_snippet.properties(self.properties_schema, self.context) # 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): self.adjust(self.properties[self.DESIRED_CAPACITY], adjustment_type=EXACT_CAPACITY) else: current_capacity = grouputils.get_size(self) self.adjust(current_capacity, adjustment_type=EXACT_CAPACITY)
def handle_update(self, json_snippet, tmpl_diff, prop_diff): """Updates self.properties, if Properties has changed. If Properties has changed, update self.properties, so we get the new values during any subsequent adjustment. """ if tmpl_diff: # parse update policy if rsrc_defn.UPDATE_POLICY in tmpl_diff: up = json_snippet.update_policy(self.update_policy_schema, self.context) self.update_policy = up 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.properties[self.SIZE] is not None: self.resize(self.properties[self.SIZE]) else: curr_size = grouputils.get_size(self) self.resize(curr_size)
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 tmpl_diff: # parse update policy if 'UpdatePolicy' in tmpl_diff: up = json_snippet.update_policy(self.update_policy_schema, self.context) self.update_policy = up if prop_diff: self.properties = json_snippet.properties(self.properties_schema, self.context) # 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]: self.resize(self.properties[self.SIZE])
def check_update_complete(self, cookie): """Update the cooldown timestamp after update succeeds.""" done = super(AutoScalingGroup, self).check_update_complete(cookie) if done: self._finished_scaling( "%s : %s" % (sc_util.CFN_EXACT_CAPACITY, grouputils.get_size(self))) return done
def test_non_nested_resource(self): group = mock.Mock() self.patchobject(group, 'nested', return_value=None) self.assertEqual(0, grouputils.get_size(group)) self.assertEqual([], grouputils.get_members(group)) self.assertEqual([], grouputils.get_member_refids(group)) self.assertEqual([], grouputils.get_member_names(group))
def check_create_complete(self, task): """Update cooldown timestamp after create succeeds.""" done = super(AutoScalingGroup, self).check_create_complete(task) cooldown = self.properties[self.COOLDOWN] if done: self._finished_scaling( cooldown, "%s : %s" % (sc_util.CFN_EXACT_CAPACITY, grouputils.get_size(self))) return done
def check_create_complete(self, task): """Update cooldown timestamp after create succeeds.""" done = super(AutoScalingGroup, self).check_create_complete(task) cooldown = self.properties[self.COOLDOWN] if done: self._finished_scaling(cooldown, "%s : %s" % (sc_util.CFN_EXACT_CAPACITY, grouputils.get_size(self))) return done
def test_non_nested_resource(self): group = mock.Mock() group.nested_identifier.return_value = None group.nested.return_value = None self.assertEqual(0, grouputils.get_size(group)) self.assertEqual([], grouputils.get_members(group)) self.assertEqual([], grouputils.get_member_refids(group)) self.assertEqual([], grouputils.get_member_names(group))
def test_scaling_group_resume(self): rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(1, grouputils.get_size(rsrc)) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) rsrc.state_set(rsrc.SUSPEND, rsrc.COMPLETE) for i in rsrc.nested().values(): i.state_set(rsrc.SUSPEND, rsrc.COMPLETE) scheduler.TaskRunner(rsrc.resume)() self.assertEqual((rsrc.RESUME, rsrc.COMPLETE), rsrc.state)
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY): """ Adjust the size of the scaling group if the cooldown permits. """ if self._cooldown_inprogress(): LOG.info(_LI("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s"), {'name': self.name, 'cooldown': self.properties[self.COOLDOWN]}) return capacity = grouputils.get_size(self) lower = self.properties[self.MIN_SIZE] upper = self.properties[self.MAX_SIZE] new_capacity = _calculate_new_capacity(capacity, adjustment, adjustment_type, lower, upper) if new_capacity == capacity: LOG.debug('no change in capacity %d' % capacity) return # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId()}, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({'suffix': 'error', 'message': six.text_type(resize_ex), }) notification.send(**notif) except Exception: LOG.exception(_LE('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname']}, }) notification.send(**notif) self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
def test_scale_success(self, mock_notify, mock_send): self.mock_stack_except_for_group() group = self.create_autoscaling_stack_and_get_group() expected = self.expected_notifs_calls(group, adjust=1, start_capacity=1, end_capacity=2, ) group.adjust(1) self.assertEqual(2, grouputils.get_size(group)) mock_notify.assert_has_calls(expected) expected = self.expected_notifs_calls(group, adjust=-1, start_capacity=2, end_capacity=1, ) group.adjust(-1) self.assertEqual(1, grouputils.get_size(group)) mock_notify.assert_has_calls(expected)
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, grouputils.get_size(group)) policy.signal() self.assertEqual(2, grouputils.get_size(group)) policy.signal() # The second signal shouldn't have changed it because of cooldown self.assertEqual(2, grouputils.get_size(group)) past = timeutils.strtime(timeutils.utcnow() - datetime.timedelta(seconds=65)) policy.metadata_set({past: 'ChangeInCapacity : 1'}) policy.signal() self.assertEqual(3, grouputils.get_size(group))
def test_scaling_group_create_error(self): mock_create = self.patchobject(generic_resource.ResourceWithProps, 'handle_create') mock_create.side_effect = Exception("Creation failed!") rsrc = utils.parse_stack(self.parsed)['my-group'] self.assertRaises(exception.ResourceFailure, scheduler.TaskRunner(rsrc.create)) self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) self.assertEqual(0, grouputils.get_size(rsrc))
def FnGetAtt(self, key, *path): if key == self.CURRENT_SIZE: return grouputils.get_size(self) if path: members = grouputils.get_members(self) attrs = ((rsrc.name, rsrc.FnGetAtt(*path)) for rsrc in members) if key == self.OUTPUTS: return dict(attrs) if key == self.OUTPUTS_LIST: return [value for name, value in attrs] raise exception.InvalidTemplateAttribute(resource=self.name, key=key)
def test_scale_success(self, mock_notify, mock_send): self.mock_stack_except_for_group() group = self.create_autoscaling_stack_and_get_group() expected = self.expected_notifs_calls( group, adjust=1, start_capacity=1, end_capacity=2, ) group.adjust(1) self.assertEqual(2, grouputils.get_size(group)) mock_notify.assert_has_calls(expected) expected = self.expected_notifs_calls( group, adjust=-1, start_capacity=2, end_capacity=1, ) group.adjust(-1) self.assertEqual(1, grouputils.get_size(group)) mock_notify.assert_has_calls(expected)
def adjust(self, adjustment, adjustment_type=CHANGE_IN_CAPACITY): """ Adjust the size of the scaling group if the cooldown permits. """ if self._cooldown_inprogress(): LOG.info(_LI("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s"), {'name': self.name, 'cooldown': self.properties[self.COOLDOWN]}) return capacity = grouputils.get_size(self) lower = self.properties[self.MIN_SIZE] upper = self.properties[self.MAX_SIZE] new_capacity = _calculate_new_capacity(capacity, adjustment, adjustment_type, lower, upper) # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId()}, 'suffix': 'start', } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({'suffix': 'error', 'message': six.text_type(resize_ex), }) notification.send(**notif) except Exception: LOG.exception(_LE('Failed sending error notification')) else: notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname']}, }) notification.send(**notif) self._cooldown_timestamp("%s : %s" % (adjustment_type, adjustment))
def test_group_with_failed_members(self): group = mock.Mock() t = template_format.parse(nested_stack) stack = utils.parse_stack(t) self.patchobject(group, 'nested', return_value=stack) # Just failed for whatever reason rsrc_err = stack.resources['r0'] rsrc_err.status = rsrc_err.FAILED rsrc_ok = stack.resources['r1'] self.assertEqual(1, grouputils.get_size(group)) self.assertEqual([rsrc_ok], grouputils.get_members(group)) self.assertEqual(['ID-r1'], grouputils.get_member_refids(group)) self.assertEqual(['r1'], grouputils.get_member_names(group))
def test_scaleup_failure(self, mock_error, mock_info, mock_send): self.mock_stack_except_for_group() group = self.create_autoscaling_stack_and_get_group() err_message = 'Boooom' m_as = self.patchobject(aws_asg.AutoScalingGroup, 'resize') m_as.side_effect = exception.Error(err_message) info, error = self.expected_notifs_calls(group, adjust=2, start_capacity=1, with_error=err_message) self.assertRaises(exception.Error, group.adjust, 2) self.assertEqual(1, grouputils.get_size(group)) mock_error.assert_has_calls([error]) mock_info.assert_has_calls([info])
def adjust(self, adjustment, adjustment_type=sc_util.CFN_CHANGE_IN_CAPACITY, min_adjustment_step=None): """Adjust the size of the scaling group if the cooldown permits.""" if not self._is_scaling_allowed(): LOG.info( _LI("%(name)s NOT performing scaling adjustment, " "cooldown %(cooldown)s"), {"name": self.name, "cooldown": self.properties[self.COOLDOWN]}, ) raise exception.NoActionRequired() capacity = grouputils.get_size(self) new_capacity = self._get_new_capacity(capacity, adjustment, adjustment_type, min_adjustment_step) changed_size = new_capacity != capacity # send a notification before, on-error and on-success. notif = { "stack": self.stack, "adjustment": adjustment, "adjustment_type": adjustment_type, "capacity": capacity, "groupname": self.FnGetRefId(), "message": _("Start resizing the group %(group)s") % {"group": self.FnGetRefId()}, "suffix": "start", } notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update( {"suffix": "error", "message": six.text_type(resize_ex), "capacity": grouputils.get_size(self)} ) notification.send(**notif) except Exception: LOG.exception(_LE("Failed sending error notification")) else: notif.update( { "suffix": "end", "capacity": new_capacity, "message": _("End resizing the group %(group)s") % {"group": notif["groupname"]}, } ) notification.send(**notif) finally: self._finished_scaling("%s : %s" % (adjustment_type, adjustment), changed_size=changed_size) return changed_size
def test_scaling_adjust_down_empty(self): properties = self.parsed['resources']['my-group']['properties'] properties['min_size'] = 1 properties['max_size'] = 1 rsrc = self.create_stack(self.parsed)['my-group'] resources = grouputils.get_members(rsrc) self.assertEqual(1, len(resources)) # Reduce the min size to 0, should complete without adjusting props = copy.copy(rsrc.properties.data) props['min_size'] = 0 update_snippet = rsrc_defn.ResourceDefinition(rsrc.name, rsrc.type(), props) scheduler.TaskRunner(rsrc.update, update_snippet)() self.assertEqual(resources, grouputils.get_members(rsrc)) # trigger adjustment to reduce to 0, there should be no more instances rsrc.adjust(-1) self.assertEqual(0, grouputils.get_size(rsrc))
def get_attribute(self, key, *path): if key == self.CURRENT_SIZE: return grouputils.get_size(self) if key == self.REFS: refs = grouputils.get_member_refids(self) return refs if path: members = grouputils.get_members(self) attrs = ((rsrc.name, rsrc.FnGetAtt(*path)) for rsrc in members) if key == self.OUTPUTS: return dict(attrs) if key == self.OUTPUTS_LIST: return [value for name, value in attrs] if key.startswith("resource."): return grouputils.get_nested_attrs(self, key, True, *path) raise exception.InvalidTemplateAttribute(resource=self.name, key=key)
def get_attribute(self, key, *path): if key == self.CURRENT_SIZE: return grouputils.get_size(self) if key == self.REFS: refs = grouputils.get_member_refids(self) return refs if key == self.REFS_MAP: members = grouputils.get_members(self) refs_map = {m.name: m.resource_id for m in members} return refs_map if path: members = grouputils.get_members(self) attrs = ((rsrc.name, rsrc.FnGetAtt(*path)) for rsrc in members) if key == self.OUTPUTS: return dict(attrs) if key == self.OUTPUTS_LIST: return [value for name, value in attrs] if key.startswith("resource."): return grouputils.get_nested_attrs(self, key, True, *path) raise exception.InvalidTemplateAttribute(resource=self.name, key=key)
def test_normal_group(self): group = mock.Mock() t = template_format.parse(nested_stack) stack = utils.parse_stack(t) # group size self.patchobject(group, 'nested', return_value=stack) self.assertEqual(2, grouputils.get_size(group)) # member list (sorted) members = [r for r in six.itervalues(stack)] expected = sorted(members, key=lambda r: (r.created_time, r.name)) actual = grouputils.get_members(group) self.assertEqual(expected, actual) # refids actual_ids = grouputils.get_member_refids(group) self.assertEqual(['ID-r0', 'ID-r1'], actual_ids) partial_ids = grouputils.get_member_refids(group, exclude=['ID-r1']) self.assertEqual(['ID-r0'], partial_ids) # names self.assertEqual(['r0', 'r1'], grouputils.get_member_names(group))
def test_normal_group(self): group = mock.Mock() t = template_format.parse(nested_stack) stack = utils.parse_stack(t) # group size self.patchobject(group, 'nested', return_value=stack) self.assertEqual(2, grouputils.get_size(group)) # member list (sorted) members = [r for r in stack.itervalues()] expected = sorted(members, key=lambda r: (r.created_time, r.name)) actual = grouputils.get_members(group) self.assertEqual(expected, actual) # refids actual_ids = grouputils.get_member_refids(group) self.assertEqual(['ID-r0', 'ID-r1'], actual_ids) partial_ids = grouputils.get_member_refids(group, exclude=['ID-r1']) self.assertEqual(['ID-r0'], partial_ids) # names self.assertEqual(['r0', 'r1'], grouputils.get_member_names(group))
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 tmpl_diff: # parse update policy if 'UpdatePolicy' in tmpl_diff: up = json_snippet.update_policy(self.update_policy_schema, self.context) self.update_policy = up 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.properties[self.DESIRED_CAPACITY] is not None: self.adjust(self.properties[self.DESIRED_CAPACITY], adjustment_type=EXACT_CAPACITY) else: current_capacity = grouputils.get_size(self) self.adjust(current_capacity, adjustment_type=EXACT_CAPACITY)
def test_scaling_group_suspend(self): rsrc = self.create_stack(self.parsed)['my-group'] self.assertEqual(1, grouputils.get_size(rsrc)) self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) scheduler.TaskRunner(rsrc.suspend)() self.assertEqual((rsrc.SUSPEND, rsrc.COMPLETE), rsrc.state)
def adjust(self, adjustment, adjustment_type=sc_util.CFN_CHANGE_IN_CAPACITY, min_adjustment_step=None): """Adjust the size of the scaling group if the cooldown permits.""" if self.status != self.COMPLETE: LOG.info("%s NOT performing scaling adjustment, " "when status is not COMPLETE", self.name) raise resource.NoActionRequired capacity = grouputils.get_size(self) new_capacity = self._get_new_capacity(capacity, adjustment, adjustment_type, min_adjustment_step) if new_capacity == capacity: LOG.info("%s NOT performing scaling adjustment, " "as there is no change in capacity.", self.name) raise resource.NoActionRequired self._check_scaling_allowed() # send a notification before, on-error and on-success. notif = { 'stack': self.stack, 'adjustment': adjustment, 'adjustment_type': adjustment_type, 'capacity': capacity, 'groupname': self.FnGetRefId(), 'message': _("Start resizing the group %(group)s") % { 'group': self.FnGetRefId()}, 'suffix': 'start', } size_changed = False try: notification.send(**notif) try: self.resize(new_capacity) except Exception as resize_ex: with excutils.save_and_reraise_exception(): try: notif.update({'suffix': 'error', 'message': six.text_type(resize_ex), 'capacity': grouputils.get_size(self), }) notification.send(**notif) except Exception: LOG.exception('Failed sending error notification') else: size_changed = True notif.update({ 'suffix': 'end', 'capacity': new_capacity, 'message': _("End resizing the group %(group)s") % { 'group': notif['groupname']}, }) notification.send(**notif) except Exception: LOG.error("Error in performing scaling adjustment for " "group %s.", self.name) raise finally: self._finished_scaling("%s : %s" % (adjustment_type, adjustment), size_changed=size_changed)
def get_attribute(self, key, *path): # noqa: C901 if key == self.CURRENT_SIZE: return grouputils.get_size(self) op_key = key op_path = path keycomponents = None if key == self.OUTPUTS_LIST: op_key = self.OUTPUTS elif key == self.REFS: op_key = self.REFS_MAP elif key.startswith("resource."): keycomponents = key.split('.', 2) if len(keycomponents) > 2: op_path = (keycomponents[2], ) + path op_key = self.OUTPUTS if op_path else self.REFS_MAP try: output = self.get_output( self._attribute_output_name(op_key, *op_path)) except (exception.NotFound, exception.TemplateOutputError) as op_err: LOG.debug('Falling back to grouputils due to %s', op_err) if key == self.REFS: return grouputils.get_member_refids(self) if key == self.REFS_MAP: members = grouputils.get_members(self) return {m.name: m.resource_id for m in members} if path and key in {self.OUTPUTS, self.OUTPUTS_LIST}: members = grouputils.get_members(self) attrs = ((rsrc.name, rsrc.FnGetAtt(*path)) for rsrc in members) if key == self.OUTPUTS: return dict(attrs) if key == self.OUTPUTS_LIST: return [value for name, value in attrs] if keycomponents is not None: return grouputils.get_nested_attrs(self, key, True, *path) else: if key in {self.REFS, self.REFS_MAP}: names = self._group_data().member_names(False) if key == self.REFS: return [output[n] for n in names if n in output] else: return {n: output[n] for n in names if n in output} if path and key in {self.OUTPUTS_LIST, self.OUTPUTS}: names = self._group_data().member_names(False) if key == self.OUTPUTS_LIST: return [output[n] for n in names if n in output] else: return {n: output[n] for n in names if n in output} if keycomponents is not None: names = list(self._group_data().member_names(False)) index = keycomponents[1] try: resource_name = names[int(index)] return output[resource_name] except (IndexError, KeyError): raise exception.NotFound( _("Member '%(mem)s' not found " "in group resource '%(grp)s'.") % { 'mem': index, 'grp': self.name }) raise exception.InvalidTemplateAttribute(resource=self.name, key=key)