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:
parent
3bfd92054b
commit
03b51a868a
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue