Example #1
0
    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'])
Example #8
0
    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)
Example #9
0
 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
Example #10
0
    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()
Example #12
0
    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)
Example #14
0
    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])
Example #15
0
 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
Example #16
0
 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
Example #17
0
    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))
Example #18
0
    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))
Example #19
0
 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
Example #20
0
 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
Example #21
0
    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))
Example #22
0
    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)
Example #24
0
    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))
Example #25
0
    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))
Example #28
0
    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)
Example #29
0
    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)
Example #30
0
    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)
Example #31
0
    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))
Example #32
0
    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))
Example #33
0
    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))
Example #34
0
    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])
Example #35
0
    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])
Example #36
0
    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
Example #37
0
    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))
Example #38
0
    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 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))
Example #40
0
    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)
Example #41
0
    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))
Example #42
0
    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))
Example #43
0
    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)
Example #45
0
    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)
Example #46
0
    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)