Merge "Add CLUSTER_RESIZE support to region placement policy"
This commit is contained in:
commit
8aae0726c9
|
@ -55,11 +55,11 @@ from oslo_log import log as logging
|
|||
from senlin.common import consts
|
||||
from senlin.common.i18n import _
|
||||
from senlin.common.i18n import _LE
|
||||
from senlin.common import scaleutils
|
||||
from senlin.common import schema
|
||||
|
||||
from senlin.db import api as db_api
|
||||
from senlin.drivers import base as driver_base
|
||||
from senlin.engine import cluster as cluster_mod
|
||||
|
||||
from senlin.policies import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -73,9 +73,9 @@ class RegionPlacementPolicy(base.Policy):
|
|||
PRIORITY = 200
|
||||
|
||||
TARGET = [
|
||||
# TODO(anyone): enable this to handle CLUSTER_RESIZE action
|
||||
('BEFORE', consts.CLUSTER_SCALE_OUT),
|
||||
('BEFORE', consts.CLUSTER_SCALE_IN),
|
||||
('BEFORE', consts.CLUSTER_RESIZE),
|
||||
]
|
||||
|
||||
PROFILE_TYPE = [
|
||||
|
@ -205,6 +205,48 @@ class RegionPlacementPolicy(base.Policy):
|
|||
|
||||
return result
|
||||
|
||||
def _get_count(self, cluster_id, action):
|
||||
"""Get number of nodes to create or delete.
|
||||
|
||||
:param cluster_id: The ID of the target cluster.
|
||||
:param action: The action object which triggered this policy check.
|
||||
:return: An integer value which can be 1) positive - number of nodes
|
||||
to create; 2) negative - number of nodes to delete; 3) 0 -
|
||||
something wrong happened, and the policy check failed.
|
||||
"""
|
||||
if action.action == consts.CLUSTER_RESIZE:
|
||||
if action.data.get('deletion', None):
|
||||
return -action.data['deletion']['count']
|
||||
elif action.data.get('creation', None):
|
||||
return action.data['creation']['count']
|
||||
|
||||
db_cluster = db_api.cluster_get(action.context, cluster_id)
|
||||
res = scaleutils.parse_resize_params(action, db_cluster)
|
||||
if res[0] == base.CHECK_ERROR:
|
||||
action.data['status'] = base.CHECK_ERROR
|
||||
action.data['reason'] = res[1]
|
||||
LOG.error(res[1])
|
||||
return 0
|
||||
|
||||
if action.data.get('deletion', None):
|
||||
return -action.data['deletion']['count']
|
||||
else:
|
||||
return action.data['creation']['count']
|
||||
|
||||
if action.action == consts.CLUSTER_SCALE_IN:
|
||||
pd = action.data.get('deletion', None)
|
||||
if pd is None:
|
||||
return -action.inputs.get('count', 1)
|
||||
else:
|
||||
return -pd.get('count', 1)
|
||||
|
||||
# CLUSTER_SCALE_OUT: an action that inflates the cluster
|
||||
pd = action.data.get('creation', None)
|
||||
if pd is None:
|
||||
return action.inputs.get('count', 1)
|
||||
else:
|
||||
return pd.get('count', 1)
|
||||
|
||||
def pre_op(self, cluster_id, action):
|
||||
"""Callback function when cluster membership is about to change.
|
||||
|
||||
|
@ -212,23 +254,14 @@ class RegionPlacementPolicy(base.Policy):
|
|||
:param action: The action that triggers this policy check.
|
||||
:returns: ``None``.
|
||||
"""
|
||||
if action.action == consts.CLUSTER_SCALE_IN:
|
||||
count = self._get_count(cluster_id, action)
|
||||
if count == 0:
|
||||
return
|
||||
|
||||
expand = True
|
||||
if count < 0:
|
||||
expand = False
|
||||
pd = action.data.get('deletion', None)
|
||||
if pd is None:
|
||||
# use action input directly if available
|
||||
count = action.inputs.get('count', 1)
|
||||
else:
|
||||
# check if policy decisions available
|
||||
count = pd.get('count', 1)
|
||||
else:
|
||||
# this is an action that inflates the cluster
|
||||
expand = True
|
||||
pd = action.data.get('creation', None)
|
||||
if pd is None:
|
||||
count = action.inputs.get('count', 1)
|
||||
else:
|
||||
count = pd.get('count', 1)
|
||||
count = -count
|
||||
|
||||
cluster = cluster_mod.Cluster.load(action.context, cluster_id)
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
import mock
|
||||
|
||||
from senlin.common import consts
|
||||
from senlin.common import scaleutils as su
|
||||
from senlin.db.sqlalchemy import api as db_api
|
||||
from senlin.drivers import base as driver_base
|
||||
from senlin.engine import cluster as cluster_mod
|
||||
from senlin.policies import base as policy_base
|
||||
|
@ -109,6 +112,136 @@ class TestRegionPlacementPolicy(base.SenlinTestCase):
|
|||
answer = {'R2': 1, 'R3': 1, 'R4': 1}
|
||||
self.assertEqual(answer, plan)
|
||||
|
||||
def test__get_count_resize_deletion(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_RESIZE,
|
||||
data={'deletion': {'count': 3}})
|
||||
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(-3, res)
|
||||
|
||||
def test__get_count_resize_creation(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_RESIZE,
|
||||
data={'creation': {'count': 3}})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(3, res)
|
||||
|
||||
@mock.patch.object(su, 'parse_resize_params')
|
||||
@mock.patch.object(db_api, 'cluster_get')
|
||||
def test__get_count_resize_parse_error(self, mock_cluster, mock_parse):
|
||||
x_cluster = mock.Mock()
|
||||
mock_cluster.return_value = x_cluster
|
||||
mock_parse.return_value = (policy_base.CHECK_ERROR, 'Something wrong.')
|
||||
action = mock.Mock(action=consts.CLUSTER_RESIZE, data={})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
|
||||
self.assertEqual(0, res)
|
||||
self.assertEqual(policy_base.CHECK_ERROR, action.data['status'])
|
||||
self.assertEqual('Something wrong.', action.data['reason'])
|
||||
|
||||
@mock.patch.object(su, 'parse_resize_params')
|
||||
@mock.patch.object(db_api, 'cluster_get')
|
||||
def test__get_count_resize_parse_creation(self, mock_cluster, mock_parse):
|
||||
def fake_parse(action, cluster):
|
||||
action.data = {'creation': {'count': 3}}
|
||||
return policy_base.CHECK_OK, ''
|
||||
|
||||
x_cluster = mock.Mock()
|
||||
mock_cluster.return_value = x_cluster
|
||||
mock_parse.side_effect = fake_parse
|
||||
action = mock.Mock(action=consts.CLUSTER_RESIZE, data={})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
|
||||
self.assertEqual(3, res)
|
||||
|
||||
@mock.patch.object(su, 'parse_resize_params')
|
||||
@mock.patch.object(db_api, 'cluster_get')
|
||||
def test__get_count_resize_parse_deletion(self, mock_cluster, mock_parse):
|
||||
def fake_parse(action, cluster):
|
||||
action.data = {'deletion': {'count': 3}}
|
||||
return policy_base.CHECK_OK, ''
|
||||
|
||||
x_cluster = mock.Mock()
|
||||
mock_cluster.return_value = x_cluster
|
||||
mock_parse.side_effect = fake_parse
|
||||
action = mock.Mock(action=consts.CLUSTER_RESIZE, data={})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
|
||||
self.assertEqual(-3, res)
|
||||
|
||||
def test__get_count_scale_in_with_data(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_IN,
|
||||
data={'deletion': {'count': 3}})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(-3, res)
|
||||
|
||||
def test__get_count_scale_in_with_no_data(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_IN,
|
||||
data={'deletion': {'num': 3}})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(-1, res)
|
||||
|
||||
def test__get_count_scale_in_with_inputs(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_IN, data={},
|
||||
inputs={'count': 3})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(-3, res)
|
||||
|
||||
def test__get_count_scale_in_with_incorrect_inputs(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_IN, data={},
|
||||
inputs={'num': 3})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(-1, res)
|
||||
|
||||
def test__get_count_scale_out_with_data(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_OUT,
|
||||
data={'creation': {'count': 3}})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(3, res)
|
||||
|
||||
def test__get_count_scale_out_with_no_data(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_OUT,
|
||||
data={'creation': {'num': 3}})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(1, res)
|
||||
|
||||
def test__get_count_scale_out_with_inputs(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_OUT, data={},
|
||||
inputs={'count': 3})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(3, res)
|
||||
|
||||
def test__get_count_scale_out_with_incorrect_inputs(self):
|
||||
action = mock.Mock(action=consts.CLUSTER_SCALE_OUT, data={},
|
||||
inputs={'num': 3})
|
||||
policy = rp.RegionPlacementPolicy('p1', self.spec)
|
||||
|
||||
res = policy._get_count('FOO', action)
|
||||
self.assertEqual(1, res)
|
||||
|
||||
@mock.patch.object(rp.RegionPlacementPolicy, '_keystone')
|
||||
@mock.patch.object(cluster_mod.Cluster, 'load')
|
||||
def test_pre_op(self, mock_load, mock_keystone):
|
||||
|
|
Loading…
Reference in New Issue