def test_get_positive_int(self): cases = {1: 1, 2: 2, '1': 1, '2': 2} for value, expected in cases.items(): res, actual = utils.get_positive_int(value) self.assertTrue(res) self.assertEqual(expected, actual) bad_values = ['foo', {}, [], -1, 1.5, 0.2, None] for value in bad_values: res, actual = utils.get_positive_int(value) self.assertFalse(res) self.assertEqual(0, actual)
def do_scale_in(self): """Handler for the CLUSTER_SCALE_IN action. :returns: A tuple containing the result and the corresponding reason. """ # We use policy data if any, deletion policy and scaling policy might # be attached. pd = self.data.get('deletion', None) grace_period = 0 if pd: grace_period = pd.get('grace_period', 0) candidates = pd.get('candidates', []) # if scaling policy is attached, get 'count' from action data count = len(candidates) or pd['count'] else: # If no scaling policy is attached, use the input count directly candidates = [] value = self.inputs.get('count', 1) success, count = utils.get_positive_int(value) if not success: reason = _('Invalid count (%s) for scaling in.') % value return self.RES_ERROR, reason # check provided params against current properties # desired is checked when strict is True curr_size = no.Node.count_by_cluster(self.context, self.target) if count > curr_size: msg = _("Triming count (%(count)s) to current " "cluster size (%(curr)s) for scaling in") LOG.warning(msg, {'count': count, 'curr': curr_size}) count = curr_size new_size = curr_size - count result = scaleutils.check_size_params(self.entity, new_size, None, None, True) if result: return self.RES_ERROR, result self.entity.set_status(self.context, consts.CS_RESIZING, _('Cluster scale in started.'), desired_capacity=new_size) # Choose victims randomly if len(candidates) == 0: candidates = scaleutils.nodes_by_random(self.entity.nodes, count) # self._sleep(grace_period) result, reason = self._delete_nodes(candidates) if result == self.RES_OK: reason = _('Cluster scaling succeeded.') self.entity.eval_status(self.context, consts.CLUSTER_SCALE_IN) return result, reason
def do_scale_out(self): """Handler for the CLUSTER_SCALE_OUT action. :returns: A tuple containing the result and the corresponding reason. """ # We use policy output if any, or else the count is # set to 1 as default. pd = self.data.get('creation', None) if pd is not None: count = pd.get('count', 1) else: # If no scaling policy is attached, use the input count directly value = self.inputs.get('count', 1) success, count = utils.get_positive_int(value) if not success: reason = 'Invalid count (%s) for scaling out.' % value return self.RES_ERROR, reason # check provided params against current properties # desired is checked when strict is True curr_size = no.Node.count_by_cluster(self.context, self.target) new_size = curr_size + count result = scaleutils.check_size_params(self.entity, new_size, None, None, True) if result: return self.RES_ERROR, result self.entity.set_status(self.context, consts.CS_RESIZING, 'Cluster scale out started.', desired_capacity=new_size) result, reason = self._create_nodes(count) if result == self.RES_OK: reason = 'Cluster scaling succeeded.' self.entity.eval_status(self.context, consts.CLUSTER_SCALE_OUT) return result, reason
def pre_op(self, cluster_id, action): """The hook function that is executed before the action. The checking result is stored in the ``data`` property of the action object rather than returned directly from the function. :param cluster_id: The ID of the target cluster. :param action: Action instance against which the policy is being checked. :return: None. """ # Use action input if count is provided count_value = action.inputs.get('count', None) cluster = action.entity current = len(cluster.nodes) if count_value is None: # count not specified, calculate it count_value = self._calculate_adjustment_count(current) # Count must be positive value success, count = utils.get_positive_int(count_value) if not success: action.data.update({ 'status': base.CHECK_ERROR, 'reason': _("Invalid count (%(c)s) for action '%(a)s'." ) % {'c': count_value, 'a': action.action} }) action.store(action.context) return # Check size constraints max_size = cluster.max_size if max_size == -1: max_size = cfg.CONF.max_nodes_per_cluster if action.action == consts.CLUSTER_SCALE_IN: if self.best_effort: count = min(count, current - cluster.min_size) result = su.check_size_params(cluster, current - count, strict=not self.best_effort) else: if self.best_effort: count = min(count, max_size - current) result = su.check_size_params(cluster, current + count, strict=not self.best_effort) if result: # failed validation pd = { 'status': base.CHECK_ERROR, 'reason': result } else: # passed validation pd = { 'status': base.CHECK_OK, 'reason': _('Scaling request validated.'), } if action.action == consts.CLUSTER_SCALE_IN: pd['deletion'] = {'count': count} else: pd['creation'] = {'count': count} action.data.update(pd) action.store(action.context) return