Add ScalingInPolicy and ScalingOutPolicy

These two simple scaling policies are just used to support some
existed use cases of autoscaling where user may want to define
webhook trigger condition without Senlin's help. In most cases,
users are recommonded to use ScalingPolicyComplex to provide
autoscaling support of a cluster.

Implements: blueprint scaling-policy-simple
Change-Id: I2ce01b68e055c94cebac230fce25adb0d04f14fa
This commit is contained in:
yanyanhu 2015-05-04 03:12:37 -04:00
parent 3bfd92054b
commit 03b51a868a
4 changed files with 180 additions and 49 deletions

View File

@ -459,20 +459,19 @@ class ClusterAction(base.Action):
if count == 0:
return self.RES_OK, 'No scaling needed based on policy checking'
if count > 0:
result, reason = self._create_nodes(cluster, count, policy_data)
else:
candidates = []
nodes = db_api.node_get_all_by_cluster(self.context, cluster.id)
i = count
while i > 0:
r = random.randrange(len(nodes))
candidates.append(nodes[r].id)
nodes.remove(nodes[r])
i = i - 1
# Update desired_capacity of cluster
nodes = db_api.node_get_all_by_cluster(self.context, cluster.id)
current_size = len(nodes)
desired_capacity = current_size + count
result, reason = self._update_cluster_properties(
cluster, desired_capacity, None, None)
if result != self.RES_OK:
return result, reason
result, reason = self._delete_nodes(cluster, candidates,
policy_data)
# Create new nodes to meet desired_capacity
# TODO(Anyone): Use unified interface(e.g. do_cluster_update)
# to do cluster resizing.
result, reason = self._create_nodes(cluster, count, policy_data)
if result == self.RES_OK:
reason = 'Cluster scaling succeeded'
@ -504,6 +503,15 @@ class ClusterAction(base.Action):
if count == 0:
return self.RES_OK, 'No scaling needed based on policy checking'
# Update desired_capacity of cluster
nodes = db_api.node_get_all_by_cluster(self.context, cluster.id)
current_size = len(nodes)
desired_capacity = current_size - count
result, reason = self._update_cluster_properties(
cluster, desired_capacity, None, None)
if result != self.RES_OK:
return result, reason
# Choose victims randomly
if len(candidates) == 0:
nodes = db_api.node_get_all_by_cluster(self.context, cluster.id)
@ -515,6 +523,8 @@ class ClusterAction(base.Action):
i = i - 1
# The policy data may contain destroy flag and grace period option
# TODO(Anyone): Use unified interface(e.g. do_cluster_update)
# to do cluster resizing.
result, new_reason = self._delete_nodes(cluster, candidates,
policy_data)

View File

@ -0,0 +1,134 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_log import log as logging
from senlin.common import constraints
from senlin.common import consts
from senlin.common.i18n import _
from senlin.common import schema
from senlin.db import api as db_api
from senlin.policies import base
LOG = logging.getLogger(__name__)
class ScalingInPolicy(base.Policy):
'''Policy for decreasing the size of a cluster.
This policy is expected to be enforced before the node count of a cluster
is decreased.
'''
__type_name__ = 'ScalingInPolicy'
TARGET = [
('BEFORE', consts.CLUSTER_SCALE_IN),
]
PROFILE_TYPE = [
'ANY',
]
KEYS = (
ADJUSTMENT,
) = (
'adjustment',
)
ADJUSTMENT_TYPES = (
EXACT_CAPACITY, CHANGE_IN_CAPACITY, CHANGE_IN_PERCENTAGE,
) = (
'EXACT_CAPACITY', 'CHANGE_IN_CAPACITY', 'CHANGE_IN_PERCENTAGE',
)
_ADJUSTMENT_KEYS = (
ADJUSTMENT_TYPE, ADJUSTMENT_NUMBER, MIN_STEP,
) = (
'type', 'number', 'min_step',
)
spec_schema = {
ADJUSTMENT: schema.Map(
_('Detailed specification for scaling adjustments.'),
schema={
ADJUSTMENT_TYPE: schema.String(
_('Type of adjustment when scaling is triggered.'),
constraints=[
constraints.AllowedValues(ADJUSTMENT_TYPES),
],
default=CHANGE_IN_CAPACITY,
),
ADJUSTMENT_NUMBER: schema.Number(
_('A number specifying the amount of adjustment.'),
default=-1,
),
MIN_STEP: schema.Integer(
_('When adjustment type is set to "CHANGE_IN_PERCENTAGE",'
' this specifies the cluster size will be changed by '
'at least this number of nodes.'),
default=1,
),
}
),
}
def __init__(self, type_name, name, **kwargs):
super(ScalingInPolicy, self).__init__(type_name, name, **kwargs)
adjustment = self.spec_data[self.ADJUSTMENT]
self.adjustment_type = adjustment[self.ADJUSTMENT_TYPE]
self.adjustment_number = adjustment[self.ADJUSTMENT_NUMBER]
self.adjustment_min_step = adjustment[self.MIN_STEP]
# TODO(anyone): Make sure the default cooldown can be used if
# not specified. Need support from ClusterPolicy.
def pre_op(self, cluster_id, action, policy_data):
cluster = db_api.cluster_get(action.context, cluster_id)
nodes = db_api.node_get_all_by_cluster(action.context, cluster_id)
current_size = len(nodes)
if self.adjustment_type == self.EXACT_CAPACITY:
count = self.adjustment_number - current_size
elif self.adjustment_type == self.CHANGE_IN_CAPACITY:
count = self.adjustment_number
elif self.adjustment_type == self.CHANGE_IN_PERCENTAGE:
count = int((self.adjustment_number * current_size) / 100.0)
if count < self.adjustment_min_step:
count = self.adjustment_min_step
# If action has input count, use it in prior
count = action.inputs.get('count', count)
# Sanity check
if count > 0:
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('ScalingInPolicy generates a positive '
'count for scaling in operation.')
elif current_size + count < cluster.min_size:
# TODO(YanyanHu): Provide an optiaon in spec to allow user to
# decide whether they want a best-effort scaling in this case.
# If so, the size of cluster will be decreased to the min_size
# and a warning will be sent back to user. If not, just reject
# this scaling request directly.
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('Attempted scaling exceeds minimum size')
else:
policy_data.status = base.CHECK_OK
policy_data.reason = _('Scaling request validated')
pd = {'count': abs(count)}
policy_data['deletion'] = pd
return policy_data

View File

@ -22,18 +22,17 @@ from senlin.policies import base
LOG = logging.getLogger(__name__)
class ScalingPolicy(base.Policy):
'''Policy for changing the size of a cluster.
class ScalingOutPolicy(base.Policy):
'''Policy for increasing the size of a cluster.
This policy is expected to be enforced before the node list of a cluster
is changed.
This policy is expected to be enforced before the node count of a cluster
is increased.
'''
__type_name__ = 'ScalingPolicy'
__type_name__ = 'ScalingOutPolicy'
TARGET = [
('BEFORE', consts.CLUSTER_SCALE_OUT),
('BEFORE', consts.CLUSTER_SCALE_IN),
]
PROFILE_TYPE = [
@ -41,9 +40,9 @@ class ScalingPolicy(base.Policy):
]
KEYS = (
MIN_SIZE, MAX_SIZE, ADJUSTMENT,
ADJUSTMENT,
) = (
'min_size', 'max_size', 'adjustment',
'adjustment',
)
ADJUSTMENT_TYPES = (
@ -59,13 +58,6 @@ class ScalingPolicy(base.Policy):
)
spec_schema = {
MIN_SIZE: schema.Integer(
_('Minumum size for the cluster.'),
default=0,
),
MAX_SIZE: schema.Integer(
_('Maximum size for the cluster.'),
),
ADJUSTMENT: schema.Map(
_('Detailed specification for scaling adjustments.'),
schema={
@ -91,10 +83,8 @@ class ScalingPolicy(base.Policy):
}
def __init__(self, type_name, name, **kwargs):
super(ScalingPolicy, self).__init__(type_name, name, **kwargs)
super(ScalingOutPolicy, self).__init__(type_name, name, **kwargs)
self.min_size = self.spec_data[self.MIN_SIZE]
self.max_size = self.spec_data[self.MAX_SIZE]
adjustment = self.spec_data[self.ADJUSTMENT]
self.adjustment_type = adjustment[self.ADJUSTMENT_TYPE]
@ -102,9 +92,10 @@ class ScalingPolicy(base.Policy):
self.adjustment_min_step = adjustment[self.MIN_STEP]
# TODO(anyone): Make sure the default cooldown can be used if
# not specified
# not specified. Need support from ClusterPolicy.
def pre_op(self, cluster_id, action, policy_data):
cluster = db_api.cluster_get(action.context, cluster_id)
nodes = db_api.node_get_all_by_cluster(action.context, cluster_id)
current_size = len(nodes)
@ -121,28 +112,23 @@ class ScalingPolicy(base.Policy):
count = action.inputs.get('count', count)
# Sanity check
if current_size + count > self.max_size:
if count < 0:
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('ScalingOutPolicy generates a negative '
'count for scaling out operation.')
elif current_size + count > cluster.max_size:
# TODO(YanyanHu): Provide an optiaon in spec to allow user to
# decide whether they want a best-effort scaling in this case.
# If so, the size of cluster will be increased to the max_size
# and a warning will be sent back to user. If not, just reject
# this scaling request directly.
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('Attempted scaling exceeds maximum size')
elif current_size + count < self.min_size:
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('Attempted scaling exceeds minimum size')
elif action.action == consts.CLUSTER_SCALE_OUT and count < 0:
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('Scaling policy generates a negative '
'count for scaling out operation.')
elif action.action == consts.CLUSTER_SCALE_IN and count > 0:
policy_data.status = base.CHECK_ERROR
policy_data.reason = _('Scaling policy generates a positive '
'count for scaling in operation.')
else:
policy_data.status = base.CHECK_OK
policy_data.reason = _('Scaling request validated')
pd = {'count': abs(count)}
if action.action == consts.CLUSTER_SCALE_OUT:
policy_data['creation'] = pd
elif action.action == consts.CLUSTER_SCALE_IN:
policy_data['deletion'] = pd
policy_data['creation'] = pd
return policy_data

View File

@ -39,7 +39,8 @@ senlin.profiles =
senlin.policies =
DeletionPolicy = senlin.policies.deletion_policy:DeletionPolicy
ScalingPolicy = senlin.policies.scaling_policy:ScalingPolicy
ScalingInPolicy = senlin.policies.scaling_in_policy:ScalingInPolicy
ScalingOutPolicy = senlin.policies.scaling_out_policy:ScalingOutPolicy
HealthPolicy = senlin.policies.health_policy:HealthPolicy
LoadBalancingPolicy = senlin.policies.lb_policy:LoadBalancingPolicy
PlacementPolicy = senlin.policies.placement_policy:PlacementPolicy