New default planner

Co-Authored-By: Vincent Francoise <Vincent.FRANCOISE@b-com.com>
Change-Id: Ide2c8fc521488e486eac8f9f89d3f808ccf4b4d7
Implements: blueprint planner-storage-action-plan
This commit is contained in:
Alexander Chadin
2016-12-05 17:32:15 +03:00
parent 7039a9d247
commit 0e440d37ee
30 changed files with 2358 additions and 554 deletions

View File

@@ -34,7 +34,7 @@ def post_get_test_action(**kw):
del action['action_plan_id']
action['action_plan_uuid'] = kw.get('action_plan_uuid',
action_plan['uuid'])
action['next'] = None
action['parents'] = None
return action
@@ -42,7 +42,7 @@ class TestActionObject(base.TestCase):
def test_action_init(self):
action_dict = api_utils.action_post_data(action_plan_id=None,
next=None)
parents=None)
del action_dict['state']
action = api_action.Action(**action_dict)
self.assertEqual(wtypes.Unset, action.state)
@@ -67,13 +67,13 @@ class TestListAction(api_base.FunctionalTest):
self.assertIn(field, action)
def test_one(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
response = self.get_json('/actions')
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
self._assert_action_fields(response['actions'][0])
def test_one_soft_deleted(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
action.soft_delete()
response = self.get_json('/actions',
headers={'X-Show-Deleted': 'True'})
@@ -84,7 +84,7 @@ class TestListAction(api_base.FunctionalTest):
self.assertEqual([], response['actions'])
def test_get_one(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
response = self.get_json('/actions/%s' % action['uuid'])
self.assertEqual(action.uuid, response['uuid'])
self.assertEqual(action.action_type, response['action_type'])
@@ -92,7 +92,7 @@ class TestListAction(api_base.FunctionalTest):
self._assert_action_fields(response)
def test_get_one_soft_deleted(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
action.soft_delete()
response = self.get_json('/actions/%s' % action['uuid'],
headers={'X-Show-Deleted': 'True'})
@@ -104,13 +104,13 @@ class TestListAction(api_base.FunctionalTest):
self.assertEqual(404, response.status_int)
def test_detail(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
response = self.get_json('/actions/detail')
self.assertEqual(action.uuid, response['actions'][0]["uuid"])
self._assert_action_fields(response['actions'][0])
def test_detail_soft_deleted(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
action.soft_delete()
response = self.get_json('/actions/detail',
headers={'X-Show-Deleted': 'True'})
@@ -121,7 +121,7 @@ class TestListAction(api_base.FunctionalTest):
self.assertEqual([], response['actions'])
def test_detail_against_single(self):
action = obj_utils.create_test_action(self.context, next=None)
action = obj_utils.create_test_action(self.context, parents=None)
response = self.get_json('/actions/%s/detail' % action['uuid'],
expect_errors=True)
self.assertEqual(404, response.status_int)
@@ -312,18 +312,23 @@ class TestListAction(api_base.FunctionalTest):
set([act['uuid'] for act in response['actions']
if act['action_plan_uuid'] == action_plan2.uuid]))
def test_many_with_next_uuid(self):
def test_many_with_parents(self):
action_list = []
for id_ in range(5):
action = obj_utils.create_test_action(self.context, id=id_,
uuid=utils.generate_uuid(),
next=id_ + 1)
if id_ > 0:
action = obj_utils.create_test_action(
self.context, id=id_, uuid=utils.generate_uuid(),
parents=[action_list[id_ - 1]])
else:
action = obj_utils.create_test_action(
self.context, id=id_, uuid=utils.generate_uuid(),
parents=[])
action_list.append(action.uuid)
response = self.get_json('/actions')
response_actions = response['actions']
for id_ in range(4):
self.assertEqual(response_actions[id_]['next_uuid'],
response_actions[id_ + 1]['uuid'])
self.assertEqual(response_actions[id_]['uuid'],
response_actions[id_ + 1]['parents'][0])
def test_many_without_soft_deleted(self):
action_list = []
@@ -357,30 +362,6 @@ class TestListAction(api_base.FunctionalTest):
uuids = [s['uuid'] for s in response['actions']]
self.assertEqual(sorted(action_list), sorted(uuids))
def test_many_with_sort_key_next_uuid(self):
for id_ in range(5):
obj_utils.create_test_action(self.context, id=id_,
uuid=utils.generate_uuid(),
next=id_ + 1)
response = self.get_json('/actions/')
reference_uuids = [
s.get('next_uuid', '') for s in response['actions']
]
response = self.get_json('/actions/?sort_key=next_uuid')
self.assertEqual(5, len(response['actions']))
uuids = [(s['next_uuid'] if 'next_uuid' in s else '')
for s in response['actions']]
self.assertEqual(sorted(reference_uuids), uuids)
response = self.get_json('/actions/?sort_key=next_uuid&sort_dir=desc')
self.assertEqual(5, len(response['actions']))
uuids = [(s['next_uuid'] if 'next_uuid' in s else '')
for s in response['actions']]
self.assertEqual(sorted(reference_uuids, reverse=True), uuids)
def test_links(self):
uuid = utils.generate_uuid()
obj_utils.create_test_action(self.context, id=1, uuid=uuid)
@@ -393,18 +374,15 @@ class TestListAction(api_base.FunctionalTest):
self.assertTrue(self.validate_link(l['href'], bookmark=bookmark))
def test_collection_links(self):
next = -1
parents = None
for id_ in range(5):
action = obj_utils.create_test_action(self.context, id=id_,
uuid=utils.generate_uuid(),
next=next)
next = action.id
parents=parents)
parents = [action.id]
response = self.get_json('/actions/?limit=3')
self.assertEqual(3, len(response['actions']))
next_marker = response['actions'][-1]['uuid']
self.assertIn(next_marker, response['next'])
def test_collection_links_default_limit(self):
cfg.CONF.set_override('max_limit', 3, 'api',
enforce_type=True)
@@ -414,9 +392,6 @@ class TestListAction(api_base.FunctionalTest):
response = self.get_json('/actions')
self.assertEqual(3, len(response['actions']))
next_marker = response['actions'][-1]['uuid']
self.assertIn(next_marker, response['next'])
class TestPatch(api_base.FunctionalTest):
@@ -426,7 +401,7 @@ class TestPatch(api_base.FunctionalTest):
obj_utils.create_test_strategy(self.context)
obj_utils.create_test_audit(self.context)
obj_utils.create_test_action_plan(self.context)
self.action = obj_utils.create_test_action(self.context, next=None)
self.action = obj_utils.create_test_action(self.context, parents=None)
p = mock.patch.object(db_api.BaseConnection, 'update_action')
self.mock_action_update = p.start()
self.mock_action_update.side_effect = self._simulate_rpc_action_update
@@ -461,7 +436,7 @@ class TestDelete(api_base.FunctionalTest):
self.strategy = obj_utils.create_test_strategy(self.context)
self.audit = obj_utils.create_test_audit(self.context)
self.action_plan = obj_utils.create_test_action_plan(self.context)
self.action = obj_utils.create_test_action(self.context, next=None)
self.action = obj_utils.create_test_action(self.context, parents=None)
p = mock.patch.object(db_api.BaseConnection, 'update_action')
self.mock_action_update = p.start()
self.mock_action_update.side_effect = self._simulate_rpc_action_update

View File

@@ -77,14 +77,6 @@ class TestListActionPlan(api_base.FunctionalTest):
'unit': '%'}],
response['efficacy_indicators'])
def test_get_one_with_first_action(self):
action_plan = obj_utils.create_test_action_plan(self.context)
action = obj_utils.create_test_action(self.context, id=1)
response = self.get_json('/action_plans/%s' % action_plan['uuid'])
self.assertEqual(action_plan.uuid, response['uuid'])
self.assertEqual(action.uuid, response['first_action_uuid'])
self._assert_action_plans_fields(response)
def test_get_one_soft_deleted(self):
action_plan = obj_utils.create_test_action_plan(self.context)
action_plan.soft_delete()
@@ -322,7 +314,7 @@ class TestDelete(api_base.FunctionalTest):
def test_delete_action_plan_with_action(self):
action = obj_utils.create_test_action(
self.context, id=self.action_plan.first_action_id)
self.context, id=1)
self.delete('/action_plans/%s' % self.action_plan.uuid)
ap_response = self.get_json('/action_plans/%s' % self.action_plan.uuid,

View File

@@ -0,0 +1,102 @@
# -*- encoding: utf-8 -*-
#
# 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 __future__ import unicode_literals
import mock
import voluptuous
from watcher.applier.actions import base as baction
from watcher.applier.actions import resize
from watcher.common import clients
from watcher.common import nova_helper
from watcher.tests import base
class TestResize(base.TestCase):
INSTANCE_UUID = "94ae2f92-b7fd-4da7-9e97-f13504ae98c4"
def setUp(self):
super(TestResize, self).setUp()
self.r_osc_cls = mock.Mock()
self.r_helper_cls = mock.Mock()
self.r_helper = mock.Mock(spec=nova_helper.NovaHelper)
self.r_helper_cls.return_value = self.r_helper
self.r_osc = mock.Mock(spec=clients.OpenStackClients)
self.r_osc_cls.return_value = self.r_osc
r_openstack_clients = mock.patch.object(
clients, "OpenStackClients", self.r_osc_cls)
r_nova_helper = mock.patch.object(
nova_helper, "NovaHelper", self.r_helper_cls)
r_openstack_clients.start()
r_nova_helper.start()
self.addCleanup(r_openstack_clients.stop)
self.addCleanup(r_nova_helper.stop)
self.input_parameters = {
"flavor": "x1",
baction.BaseAction.RESOURCE_ID: self.INSTANCE_UUID,
}
self.action = resize.Resize(mock.Mock())
self.action.input_parameters = self.input_parameters
def test_parameters(self):
params = {baction.BaseAction.RESOURCE_ID:
self.INSTANCE_UUID,
self.action.FLAVOR: 'x1'}
self.action.input_parameters = params
self.assertTrue(self.action.validate_parameters())
def test_parameters_exception_empty_fields(self):
parameters = {baction.BaseAction.RESOURCE_ID:
self.INSTANCE_UUID,
self.action.FLAVOR: None}
self.action.input_parameters = parameters
exc = self.assertRaises(
voluptuous.MultipleInvalid, self.action.validate_parameters)
self.assertEqual([(['flavor'], voluptuous.TypeInvalid)],
[(e.path, type(e)) for e in exc.errors])
def test_parameters_exception_flavor(self):
parameters = {baction.BaseAction.RESOURCE_ID:
self.INSTANCE_UUID,
self.action.FLAVOR: None}
self.action.input_parameters = parameters
exc = self.assertRaises(
voluptuous.MultipleInvalid, self.action.validate_parameters)
self.assertEqual(
[(['flavor'], voluptuous.TypeInvalid)],
[(e.path, type(e)) for e in exc.errors])
def test_parameters_exception_resource_id(self):
parameters = {baction.BaseAction.RESOURCE_ID: "EFEF",
self.action.FLAVOR: 'x1'}
self.action.input_parameters = parameters
exc = self.assertRaises(
voluptuous.MultipleInvalid, self.action.validate_parameters)
self.assertEqual(
[(['resource_id'], voluptuous.Invalid)],
[(e.path, type(e)) for e in exc.errors])
def test_execute_resize(self):
self.r_helper.find_instance.return_value = self.INSTANCE_UUID
self.action.execute()
self.r_helper.resize_instance.assert_called_once_with(
instance_id=self.INSTANCE_UUID, flavor='x1')

View File

@@ -70,14 +70,15 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
except Exception as exc:
self.fail(exc)
def create_action(self, action_type, parameters, next):
def create_action(self, action_type, parameters, parents):
action = {
'uuid': utils.generate_uuid(),
'action_plan_id': 0,
'action_type': action_type,
'input_parameters': parameters,
'state': objects.action.State.PENDING,
'next': next,
'parents': parents,
}
new_action = objects.Action(self.context, **action)
new_action.create()
@@ -116,7 +117,7 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
def test_execute_with_two_actions(self):
actions = []
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'test'}, second.id)
first = self.create_action("nop", {'message': 'test'}, None)
actions.append(first)
actions.append(second)
@@ -132,8 +133,8 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
actions = []
third = self.create_action("nop", {'message': 'next'}, None)
second = self.create_action("sleep", {'duration': 0.0}, third.id)
first = self.create_action("nop", {'message': 'hello'}, second.id)
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'hello'}, None)
self.check_action_state(first, objects.action.State.PENDING)
self.check_action_state(second, objects.action.State.PENDING)
@@ -154,8 +155,8 @@ class TestDefaultWorkFlowEngine(base.DbTestCase):
actions = []
third = self.create_action("no_exist", {'message': 'next'}, None)
second = self.create_action("sleep", {'duration': 0.0}, third.id)
first = self.create_action("nop", {'message': 'hello'}, second.id)
second = self.create_action("sleep", {'duration': 0.0}, None)
first = self.create_action("nop", {'message': 'hello'}, None)
self.check_action_state(first, objects.action.State.PENDING)
self.check_action_state(second, objects.action.State.PENDING)

View File

@@ -38,6 +38,7 @@ class TestNovaHelper(base.TestCase):
self.instance_uuid = "fb5311b7-37f3-457e-9cde-6494a3c59bfe"
self.source_node = "ldev-indeedsrv005"
self.destination_node = "ldev-indeedsrv006"
self.flavor_name = "x1"
@staticmethod
def fake_server(*args, **kwargs):
@@ -89,6 +90,22 @@ class TestNovaHelper(base.TestCase):
result = nova_util.set_host_offline("rennes")
self.assertFalse(result)
@mock.patch.object(time, 'sleep', mock.Mock())
def test_resize_instance(self, mock_glance, mock_cinder,
mock_neutron, mock_nova):
nova_util = nova_helper.NovaHelper()
server = self.fake_server(self.instance_uuid)
setattr(server, 'status', 'VERIFY_RESIZE')
self.fake_nova_find_list(nova_util, find=server, list=server)
is_success = nova_util.resize_instance(self.instance_uuid,
self.flavor_name)
self.assertTrue(is_success)
setattr(server, 'status', 'SOMETHING_ELSE')
is_success = nova_util.resize_instance(self.instance_uuid,
self.flavor_name)
self.assertFalse(is_success)
@mock.patch.object(time, 'sleep', mock.Mock())
def test_live_migrate_instance(self, mock_glance, mock_cinder,
mock_neutron, mock_nova):

View File

@@ -278,28 +278,28 @@ class DbActionTestCase(base.DbTestCase):
id=1,
uuid=w_utils.generate_uuid(),
audit_id=audit.id,
first_action_id=None,
parents=None,
state=objects.action_plan.State.RECOMMENDED)
action1 = self._create_test_action(
id=1,
action_plan_id=1,
description='description action 1',
uuid=w_utils.generate_uuid(),
next=None,
parents=None,
state=objects.action_plan.State.PENDING)
action2 = self._create_test_action(
id=2,
action_plan_id=2,
description='description action 2',
uuid=w_utils.generate_uuid(),
next=action1['uuid'],
parents=[action1['uuid']],
state=objects.action_plan.State.PENDING)
action3 = self._create_test_action(
id=3,
action_plan_id=1,
description='description action 3',
uuid=w_utils.generate_uuid(),
next=action2['uuid'],
parents=[action2['uuid']],
state=objects.action_plan.State.ONGOING)
res = self.dbapi.get_action_list(
self.context,

View File

@@ -283,13 +283,11 @@ class DbActionPlanTestCase(base.DbTestCase):
id=1,
uuid=w_utils.generate_uuid(),
audit_id=audit['id'],
first_action_id=None,
state=ap_objects.State.RECOMMENDED)
action_plan2 = self._create_test_action_plan(
id=2,
uuid=w_utils.generate_uuid(),
audit_id=audit['id'],
first_action_id=action_plan1['id'],
state=ap_objects.State.ONGOING)
res = self.dbapi.get_action_plan_list(

View File

@@ -130,7 +130,7 @@ def get_test_action(**kwargs):
'resource_id':
'10a47dd1-4874-4298-91cf-eff046dbdb8d'}),
'state': kwargs.get('state', objects.action_plan.State.PENDING),
'next': kwargs.get('next', 2),
'parents': kwargs.get('parents', []),
'created_at': kwargs.get('created_at'),
'updated_at': kwargs.get('updated_at'),
'deleted_at': kwargs.get('deleted_at'),
@@ -166,7 +166,6 @@ def get_test_action_plan(**kwargs):
'audit_id': kwargs.get('audit_id', 1),
'strategy_id': kwargs.get('strategy_id', 1),
'global_efficacy': kwargs.get('global_efficacy', {}),
'first_action_id': kwargs.get('first_action_id', 1),
'created_at': kwargs.get('created_at'),
'updated_at': kwargs.get('updated_at'),
'deleted_at': kwargs.get('deleted_at'),

View File

@@ -1,208 +0,0 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
import mock
from watcher.common import utils
from watcher.db import api as db_api
from watcher.decision_engine.planner import default as pbase
from watcher.decision_engine.solution import default as dsol
from watcher.decision_engine.strategy import strategies
from watcher import objects
from watcher.tests.db import base
from watcher.tests.db import utils as db_utils
from watcher.tests.decision_engine.model import ceilometer_metrics as fake
from watcher.tests.decision_engine.model import faker_cluster_state
from watcher.tests.objects import utils as obj_utils
class SolutionFaker(object):
@staticmethod
def build():
metrics = fake.FakeCeilometerMetrics()
current_state_cluster = faker_cluster_state.FakerModelCollector()
strategy = strategies.BasicConsolidation(
config=mock.Mock(datasource="ceilometer"))
strategy._compute_model = current_state_cluster.generate_scenario_1()
strategy.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return strategy.execute()
class SolutionFakerSingleHyp(object):
@staticmethod
def build():
metrics = fake.FakeCeilometerMetrics()
current_state_cluster = faker_cluster_state.FakerModelCollector()
strategy = strategies.BasicConsolidation(
config=mock.Mock(datasource="ceilometer"))
strategy._compute_model = (
current_state_cluster.generate_scenario_3_with_2_nodes())
strategy.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return strategy.execute()
class TestActionScheduling(base.DbTestCase):
def setUp(self):
super(TestActionScheduling, self).setUp()
self.strategy = db_utils.create_test_strategy(name="dummy")
self.audit = db_utils.create_test_audit(
uuid=utils.generate_uuid(), strategy_id=self.strategy.id)
self.default_planner = pbase.DefaultPlanner(mock.Mock())
def test_schedule_actions(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
with mock.patch.object(
pbase.DefaultPlanner, "create_action",
wraps=self.default_planner.create_action
) as m_create_action:
self.default_planner.config.weights = {'migrate': 3}
action_plan = self.default_planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_action.call_count)
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("migrate", actions[0].action_type)
def test_schedule_two_actions(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="nop",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.DefaultPlanner, "create_action",
wraps=self.default_planner.create_action
) as m_create_action:
self.default_planner.config.weights = {'migrate': 3, 'nop': 0}
action_plan = self.default_planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(2, m_create_action.call_count)
# check order
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("nop", actions[0].action_type)
self.assertEqual("migrate", actions[1].action_type)
def test_schedule_actions_with_unknown_action(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"src_uuid_node": "server1",
"dst_uuid_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="new_action_type",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.DefaultPlanner, "create_action",
wraps=self.default_planner.create_action
) as m_create_action:
self.default_planner.config.weights = {'migrate': 0}
self.assertRaises(KeyError, self.default_planner.schedule,
self.context, self.audit.id, solution)
self.assertEqual(2, m_create_action.call_count)
class TestDefaultPlanner(base.DbTestCase):
def setUp(self):
super(TestDefaultPlanner, self).setUp()
self.default_planner = pbase.DefaultPlanner(mock.Mock())
self.default_planner.config.weights = {
'nop': 0,
'sleep': 1,
'change_nova_service_state': 2,
'migrate': 3
}
self.goal = obj_utils.create_test_goal(self.context)
self.strategy = obj_utils.create_test_strategy(
self.context, goal_id=self.goal.id)
obj_utils.create_test_audit_template(
self.context, goal_id=self.goal.id, strategy_id=self.strategy.id)
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
self.mock_create_action_plan = p.start()
self.mock_create_action_plan.side_effect = (
self._simulate_action_plan_create)
self.addCleanup(p.stop)
q = mock.patch.object(db_api.BaseConnection, 'create_action')
self.mock_create_action = q.start()
self.mock_create_action.side_effect = (
self._simulate_action_create)
self.addCleanup(q.stop)
def _simulate_action_plan_create(self, action_plan):
action_plan.create()
return action_plan
def _simulate_action_create(self, action):
action.create()
return action
@mock.patch.object(objects.Strategy, 'get_by_name')
def test_schedule_scheduled_empty(self, m_get_by_name):
m_get_by_name.return_value = self.strategy
audit = db_utils.create_test_audit(
goal_id=self.goal.id, strategy_id=self.strategy.id)
fake_solution = SolutionFakerSingleHyp.build()
action_plan = self.default_planner.schedule(self.context,
audit.id, fake_solution)
self.assertIsNotNone(action_plan.uuid)
@mock.patch.object(objects.Strategy, 'get_by_name')
def test_scheduler_warning_empty_action_plan(self, m_get_by_name):
m_get_by_name.return_value = self.strategy
audit = db_utils.create_test_audit(
goal_id=self.goal.id, strategy_id=self.strategy.id)
fake_solution = SolutionFaker.build()
action_plan = self.default_planner.schedule(
self.context, audit.id, fake_solution)
self.assertIsNotNone(action_plan.uuid)

View File

@@ -16,13 +16,13 @@
from oslo_config import cfg
from watcher.decision_engine.planner import default
from watcher.decision_engine.planner import manager as planner
from watcher.decision_engine.planner import weight
from watcher.tests import base
class TestPlannerManager(base.TestCase):
def test_load(self):
cfg.CONF.set_override('planner', "default", group='watcher_planner')
cfg.CONF.set_override('planner', "weight", group='watcher_planner')
manager = planner.PlannerManager()
self.assertIsInstance(manager.load(), default.DefaultPlanner)
self.assertIsInstance(manager.load(), weight.WeightPlanner)

View File

@@ -0,0 +1,943 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
import mock
from watcher.common import nova_helper
from watcher.common import utils
from watcher.db import api as db_api
from watcher.decision_engine.planner import weight as pbase
from watcher.decision_engine.solution import default as dsol
from watcher.decision_engine.strategy import strategies
from watcher import objects
from watcher.tests.db import base
from watcher.tests.db import utils as db_utils
from watcher.tests.decision_engine.model import ceilometer_metrics as fake
from watcher.tests.decision_engine.model import faker_cluster_state
from watcher.tests.objects import utils as obj_utils
class SolutionFaker(object):
@staticmethod
def build():
metrics = fake.FakerMetricsCollector()
current_state_cluster = faker_cluster_state.FakerModelCollector()
sercon = strategies.BasicConsolidation(config=mock.Mock())
sercon.compute_model = current_state_cluster.generate_scenario_1()
sercon.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return sercon.execute()
class SolutionFakerSingleHyp(object):
@staticmethod
def build():
metrics = fake.FakerMetricsCollector()
current_state_cluster = faker_cluster_state.FakerModelCollector()
sercon = strategies.BasicConsolidation(config=mock.Mock())
sercon.compute_model = (
current_state_cluster.generate_scenario_3_with_2_nodes())
sercon.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return sercon.execute()
class TestActionScheduling(base.DbTestCase):
def setUp(self):
super(TestActionScheduling, self).setUp()
self.strategy = db_utils.create_test_strategy(name="dummy")
self.audit = db_utils.create_test_audit(
uuid=utils.generate_uuid(), strategy_id=self.strategy.id)
self.planner = pbase.WeightPlanner(
mock.Mock(
weights={
'turn_host_to_acpi_s3_state': 10,
'resize': 20,
'migrate': 30,
'sleep': 40,
'change_nova_service_state': 50,
'nop': 60,
'new_action_type': 70,
},
parallelization={
'turn_host_to_acpi_s3_state': 2,
'resize': 2,
'migrate': 2,
'sleep': 1,
'change_nova_service_state': 1,
'nop': 1,
'new_action_type': 70,
}))
@mock.patch.object(utils, "generate_uuid")
def test_schedule_actions(self, m_generate_uuid):
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222",
"33333333-3333-3333-3333-333333333333",
# "44444444-4444-4444-4444-444444444444",
# "55555555-5555-5555-5555-555555555555",
# "66666666-6666-6666-6666-666666666666",
# "77777777-7777-7777-7777-777777777777",
# "88888888-8888-8888-8888-888888888888",
# "99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
self.planner.config.weights = {'migrate': 3}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = []
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_two_actions(self, m_generate_uuid):
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222",
"33333333-3333-3333-3333-333333333333",
"44444444-4444-4444-4444-444444444444", # Migrate 1
"55555555-5555-5555-5555-555555555555", # Nop 1
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
# We create the migrate action before but we then schedule
# after the nop action
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="nop",
input_parameters={"message": "Hello world"})
self.planner.config.weights = {'migrate': 3, 'nop': 5}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'nop',
'parents': [],
'uuid': '55555555-5555-5555-5555-555555555555'},
{'action_type': 'migrate',
'parents': ['55555555-5555-5555-5555-555555555555'],
'uuid': '44444444-4444-4444-4444-444444444444'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_actions_with_unknown_action(self, m_generate_uuid):
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # new_action_type
"33333333-3333-3333-3333-333333333333",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"src_uuid_node": "server1",
"dst_uuid_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="new_action_type",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'new_action_type',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'migrate',
'parents': ['22222222-2222-2222-2222-222222222222'],
'uuid': '11111111-1111-1111-1111-111111111111'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
@mock.patch.object(nova_helper.NovaHelper, 'get_instance_by_uuid')
def test_schedule_migrate_resize_actions(self, m_nova, m_generate_uuid):
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Migrate 4
"55555555-5555-5555-5555-555555555555", # Migrate 5
"66666666-6666-6666-6666-666666666666", # Resize 1
"77777777-7777-7777-7777-777777777777", # Resize 2
"88888888-8888-8888-8888-888888888888", # Nop
"99999999-9999-9999-9999-999999999999",
]
m_nova.return_value = 'server1'
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={"flavor": "x1"})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111'],
'uuid': '22222222-2222-2222-2222-222222222222'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_3_migrate_1_resize_1_acpi_actions_1_swimlane(
self, m_generate_uuid):
self.planner.config.parallelization["migrate"] = 1
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Resize
"55555555-5555-5555-5555-555555555555", # ACPI
"66666666-6666-6666-6666-666666666666",
"77777777-7777-7777-7777-777777777777",
"88888888-8888-8888-8888-888888888888",
"99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server2",
"destination_node": "server3"})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="turn_host_to_acpi_s3_state",
resource_id="server1",
input_parameters={})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111'],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'migrate',
'parents': ['22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111'],
'uuid': '22222222-2222-2222-2222-222222222222'}),
({'action_type': 'resize',
'parents': ['33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'},
{'action_type': 'turn_host_to_acpi_s3_state',
'parents': ['44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': ['22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'resize',
'parents': ['33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_migrate_resize_acpi_actions_2_swimlanes(
self, m_generate_uuid):
self.planner.config.parallelization["migrate"] = 2
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Resize
"55555555-5555-5555-5555-555555555555", # ACPI
"66666666-6666-6666-6666-666666666666",
"77777777-7777-7777-7777-777777777777",
"88888888-8888-8888-8888-888888888888",
"99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server2",
"destination_node": "server3"})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="turn_host_to_acpi_s3_state",
resource_id="server1",
input_parameters={})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'}),
({'action_type': 'resize',
'parents': ['33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'},
{'action_type': 'turn_host_to_acpi_s3_state',
'parents': ['44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'}),
({'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'resize',
'parents': ['33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_migrate_resize_acpi_actions_3_swimlanes(
self, m_generate_uuid):
self.planner.config.parallelization["migrate"] = 3
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Resize
"55555555-5555-5555-5555-555555555555", # ACPI
"66666666-6666-6666-6666-666666666666",
"77777777-7777-7777-7777-777777777777",
"88888888-8888-8888-8888-888888888888",
"99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server2",
"destination_node": "server3"})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="turn_host_to_acpi_s3_state",
resource_id="server1",
input_parameters={})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'},
{'action_type': 'turn_host_to_acpi_s3_state',
'parents': ['44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '44444444-4444-4444-4444-444444444444'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_three_migrate_two_resize_actions(
self, m_generate_uuid):
self.planner.config.parallelization["migrate"] = 3
self.planner.config.parallelization["resize"] = 2
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Resize
"55555555-5555-5555-5555-555555555555", # ACPI
"66666666-6666-6666-6666-666666666666",
"77777777-7777-7777-7777-777777777777",
"88888888-8888-8888-8888-888888888888",
"99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters=parameters)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server2",
"destination_node": "server3"})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="resize",
resource_id="b189db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters={'flavor': 'x1'})
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'resize',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222',
'33333333-3333-3333-3333-333333333333'],
'uuid': '55555555-5555-5555-5555-555555555555'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
@mock.patch.object(utils, "generate_uuid")
def test_schedule_5_migrate_2_resize_actions_for_2_swimlanes(
self, m_generate_uuid):
self.planner.config.parallelization["migrate"] = 2
self.planner.config.parallelization["resize"] = 2
m_generate_uuid.side_effect = [
"00000000-0000-0000-0000-000000000000", # Action plan
"11111111-1111-1111-1111-111111111111", # Migrate 1
"22222222-2222-2222-2222-222222222222", # Migrate 2
"33333333-3333-3333-3333-333333333333", # Migrate 3
"44444444-4444-4444-4444-444444444444", # Migrate 4
"55555555-5555-5555-5555-555555555555", # Migrate 5
"66666666-6666-6666-6666-666666666666", # Resize 1
"77777777-7777-7777-7777-777777777777", # Resize 2
"88888888-8888-8888-8888-888888888888", # Nop
"99999999-9999-9999-9999-999999999999",
]
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server1",
"destination_node": "server6"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server2",
"destination_node": "server6"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server3",
"destination_node": "server6"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server4",
"destination_node": "server6"})
solution.add_action(action_type="migrate",
resource_id="DOESNOTMATTER",
input_parameters={"source_node": "server5",
"destination_node": "server6"})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="resize",
resource_id="DOESNOTMATTER",
input_parameters={'flavor': 'x2'})
solution.add_action(action_type="turn_host_to_acpi_s3_state",
resource_id="DOESNOTMATTER")
with mock.patch.object(
pbase.WeightPlanner, "create_scheduled_actions",
wraps=self.planner.create_scheduled_actions
) as m_create_scheduled_actions:
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_scheduled_actions.call_count)
action_graph = m_create_scheduled_actions.call_args[0][1]
expected_edges = \
[({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '11111111-1111-1111-1111-111111111111'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '44444444-4444-4444-4444-444444444444'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'}),
({'action_type': 'migrate',
'parents': [],
'uuid': '22222222-2222-2222-2222-222222222222'},
{'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '44444444-4444-4444-4444-444444444444'}),
({'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '33333333-3333-3333-3333-333333333333'},
{'action_type': 'migrate',
'parents': ['33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': ['11111111-1111-1111-1111-111111111111',
'22222222-2222-2222-2222-222222222222'],
'uuid': '44444444-4444-4444-4444-444444444444'},
{'action_type': 'migrate',
'parents': ['33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'}),
({'action_type': 'migrate',
'parents': ['33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'},
{'action_type': 'resize',
'parents': ['55555555-5555-5555-5555-555555555555'],
'uuid': '66666666-6666-6666-6666-666666666666'}),
({'action_type': 'migrate',
'parents': ['33333333-3333-3333-3333-333333333333',
'44444444-4444-4444-4444-444444444444'],
'uuid': '55555555-5555-5555-5555-555555555555'},
{'action_type': 'resize',
'parents': ['55555555-5555-5555-5555-555555555555'],
'uuid': '77777777-7777-7777-7777-777777777777'}),
({'action_type': 'resize',
'parents': ['55555555-5555-5555-5555-555555555555'],
'uuid': '66666666-6666-6666-6666-666666666666'},
{'action_type': 'turn_host_to_acpi_s3_state',
'parents': ['66666666-6666-6666-6666-666666666666',
'77777777-7777-7777-7777-777777777777'],
'uuid': '88888888-8888-8888-8888-888888888888'}),
({'action_type': 'resize',
'parents': ['55555555-5555-5555-5555-555555555555'],
'uuid': '77777777-7777-7777-7777-777777777777'},
{'action_type': 'turn_host_to_acpi_s3_state',
'parents': ['66666666-6666-6666-6666-666666666666',
'77777777-7777-7777-7777-777777777777'],
'uuid': '88888888-8888-8888-8888-888888888888'})]
edges = sorted([(src.as_dict(), dst.as_dict())
for src, dst in action_graph.edges()],
key=lambda pair: pair[0]['uuid'])
for src, dst in edges:
for key in ('id', 'action_plan', 'action_plan_id', 'created_at',
'input_parameters', 'deleted_at', 'updated_at',
'state'):
del src[key]
del dst[key]
self.assertEqual(len(expected_edges), len(edges))
for pair in expected_edges:
self.assertIn(pair, edges)
class TestWeightPlanner(base.DbTestCase):
def setUp(self):
super(TestWeightPlanner, self).setUp()
self.planner = pbase.WeightPlanner(mock.Mock())
self.planner.config.weights = {
'nop': 0,
'sleep': 1,
'change_nova_service_state': 2,
'migrate': 3
}
self.goal = obj_utils.create_test_goal(self.context)
self.strategy = obj_utils.create_test_strategy(
self.context, goal_id=self.goal.id)
obj_utils.create_test_audit_template(
self.context, goal_id=self.goal.id, strategy_id=self.strategy.id)
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
self.mock_create_action_plan = p.start()
self.mock_create_action_plan.side_effect = (
self._simulate_action_plan_create)
self.addCleanup(p.stop)
q = mock.patch.object(db_api.BaseConnection, 'create_action')
self.mock_create_action = q.start()
self.mock_create_action.side_effect = (
self._simulate_action_create)
self.addCleanup(q.stop)
def _simulate_action_plan_create(self, action_plan):
action_plan.create()
return action_plan
def _simulate_action_create(self, action):
action.create()
return action
@mock.patch.object(objects.Strategy, 'get_by_name')
def test_scheduler_warning_empty_action_plan(self, m_get_by_name):
m_get_by_name.return_value = self.strategy
audit = db_utils.create_test_audit(
goal_id=self.goal.id, strategy_id=self.strategy.id)
fake_solution = mock.MagicMock(efficacy_indicators=[],
actions=[])
action_plan = self.planner.schedule(
self.context, audit.id, fake_solution)
self.assertIsNotNone(action_plan.uuid)

View File

@@ -0,0 +1,378 @@
# -*- encoding: utf-8 -*-
# Copyright (c) 2015 b<>com
#
# 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.
import mock
from watcher.common import exception
from watcher.common import nova_helper
from watcher.common import utils
from watcher.db import api as db_api
from watcher.decision_engine.planner import workload_stabilization as pbase
from watcher.decision_engine.solution import default as dsol
from watcher.decision_engine.strategy import strategies
from watcher import objects
from watcher.tests.db import base
from watcher.tests.db import utils as db_utils
from watcher.tests.decision_engine.model import ceilometer_metrics as fake
from watcher.tests.decision_engine.model import faker_cluster_state
from watcher.tests.objects import utils as obj_utils
class SolutionFaker(object):
@staticmethod
def build():
metrics = fake.FakerMetricsCollector()
current_state_cluster = faker_cluster_state.FakerModelCollector()
sercon = strategies.BasicConsolidation(config=mock.Mock())
sercon._compute_model = current_state_cluster.generate_scenario_1()
sercon.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return sercon.execute()
class SolutionFakerSingleHyp(object):
@staticmethod
def build():
metrics = fake.FakerMetricsCollector()
current_state_cluster = faker_cluster_state.FakerModelCollector()
sercon = strategies.BasicConsolidation(config=mock.Mock())
sercon._compute_model = (
current_state_cluster.generate_scenario_3_with_2_nodes())
sercon.ceilometer = mock.MagicMock(
get_statistics=metrics.mock_get_statistics)
return sercon.execute()
class TestActionScheduling(base.DbTestCase):
def setUp(self):
super(TestActionScheduling, self).setUp()
self.strategy = db_utils.create_test_strategy(name="dummy")
self.audit = db_utils.create_test_audit(
uuid=utils.generate_uuid(), strategy_id=self.strategy.id)
self.planner = pbase.WorkloadStabilizationPlanner(mock.Mock())
self.nova_helper = nova_helper.NovaHelper(mock.Mock())
def test_schedule_actions(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
self.planner.config.weights = {'migrate': 3}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(1, m_create_action.call_count)
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("migrate", actions[0].action_type)
def test_schedule_two_actions(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="nop",
input_parameters={"message": "Hello world"})
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
self.planner.config.weights = {'migrate': 3, 'nop': 5}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(2, m_create_action.call_count)
# check order
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("nop", actions[0].action_type)
self.assertEqual("migrate", actions[1].action_type)
def test_schedule_actions_with_unknown_action(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"src_uuid_node": "server1",
"dst_uuid_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="new_action_type",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
with mock.patch.object(nova_helper, 'NovaHelper') as m_nova:
self.planner.config.weights = {'migrate': 0}
self.assertRaises(KeyError, self.planner.schedule,
self.context, self.audit.id, solution)
assert not m_nova.called
self.assertEqual(2, m_create_action.call_count)
def test_schedule_actions_with_unsupported_action(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"src_uuid_node": "server1",
"dst_uuid_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="new_action_type",
resource_id="",
input_parameters={})
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
with mock.patch.object(nova_helper, 'NovaHelper') as m_nova:
self.planner.config.weights = {
'turn_host_to_acpi_s3_state': 0,
'resize': 1,
'migrate': 2,
'sleep': 3,
'change_nova_service_state': 4,
'nop': 5,
'new_action_type': 6}
self.assertRaises(exception.UnsupportedActionType,
self.planner.schedule,
self.context, self.audit.id, solution)
assert not m_nova.called
self.assertEqual(2, m_create_action.call_count)
@mock.patch.object(nova_helper.NovaHelper, 'get_instance_by_uuid')
def test_schedule_migrate_resize_actions(self, mock_nova):
mock_nova.return_value = 'server1'
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="resize",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters={"flavor": "x1"})
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
with mock.patch.object(nova_helper, 'NovaHelper') as m_nova:
self.planner.config.weights = {'migrate': 3, 'resize': 2}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertEqual(1, m_nova.call_count)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(2, m_create_action.call_count)
# check order
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("migrate", actions[0].action_type)
self.assertEqual("resize", actions[1].action_type)
self.assertEqual(actions[0].uuid, actions[1].parents[0])
def test_schedule_migrate_resize_acpi_s3_actions(self):
solution = dsol.DefaultSolution(
goal=mock.Mock(), strategy=self.strategy)
parameters = {
"source_node": "server1",
"destination_node": "server2",
}
parent_migration = "b199db0c-1408-4d52-b5a5-5ca14de0ff36"
solution.add_action(action_type="migrate",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters=parameters)
solution.add_action(action_type="resize",
resource_id="b199db0c-1408-4d52-b5a5-5ca14de0ff36",
input_parameters={'flavor': 'x1'})
solution.add_action(action_type="migrate",
resource_id="f6416850-da28-4047-a547-8c49f53e95fe",
input_parameters={"source_node": "server1",
"destination_node": "server2"})
solution.add_action(action_type="migrate",
resource_id="bb404e74-2caf-447b-bd1e-9234db386ca5",
input_parameters={"source_node": "server2",
"destination_node": "server3"})
solution.add_action(action_type="turn_host_to_acpi_s3_state",
resource_id="server1",
input_parameters={})
with mock.patch.object(
pbase.WorkloadStabilizationPlanner, "create_action",
wraps=self.planner.create_action
) as m_create_action:
with mock.patch.object(
nova_helper, 'NovaHelper') as m_nova:
m_nova().get_hostname.return_value = 'server1'
m_nova().get_instance_by_uuid.return_value = ['uuid1']
self.planner.config.weights = {
'turn_host_to_acpi_s3_state': 0,
'resize': 1,
'migrate': 2,
'sleep': 3,
'change_nova_service_state': 4,
'nop': 5}
action_plan = self.planner.schedule(
self.context, self.audit.id, solution)
self.assertEqual(3, m_nova.call_count)
self.assertIsNotNone(action_plan.uuid)
self.assertEqual(5, m_create_action.call_count)
# check order
filters = {'action_plan_id': action_plan.id}
actions = objects.Action.dbapi.get_action_list(self.context, filters)
self.assertEqual("migrate", actions[0].action_type)
self.assertEqual("migrate", actions[1].action_type)
self.assertEqual("migrate", actions[2].action_type)
self.assertEqual("resize", actions[3].action_type)
self.assertEqual("turn_host_to_acpi_s3_state", actions[4].action_type)
for action in actions:
if action.input_parameters['resource_id'] == parent_migration:
parent_migration = action
break
self.assertEqual(parent_migration.uuid, actions[3].parents[0])
class TestDefaultPlanner(base.DbTestCase):
def setUp(self):
super(TestDefaultPlanner, self).setUp()
self.planner = pbase.WorkloadStabilizationPlanner(mock.Mock())
self.planner.config.weights = {
'nop': 0,
'sleep': 1,
'change_nova_service_state': 2,
'migrate': 3
}
self.goal = obj_utils.create_test_goal(self.context)
self.strategy = obj_utils.create_test_strategy(
self.context, goal_id=self.goal.id)
obj_utils.create_test_audit_template(
self.context, goal_id=self.goal.id, strategy_id=self.strategy.id)
p = mock.patch.object(db_api.BaseConnection, 'create_action_plan')
self.mock_create_action_plan = p.start()
self.mock_create_action_plan.side_effect = (
self._simulate_action_plan_create)
self.addCleanup(p.stop)
q = mock.patch.object(db_api.BaseConnection, 'create_action')
self.mock_create_action = q.start()
self.mock_create_action.side_effect = (
self._simulate_action_create)
self.addCleanup(q.stop)
def _simulate_action_plan_create(self, action_plan):
action_plan.create()
return action_plan
def _simulate_action_create(self, action):
action.create()
return action
@mock.patch.object(objects.Strategy, 'get_by_name')
def test_scheduler_warning_empty_action_plan(self, m_get_by_name):
m_get_by_name.return_value = self.strategy
audit = db_utils.create_test_audit(
goal_id=self.goal.id, strategy_id=self.strategy.id)
fake_solution = mock.MagicMock(efficacy_indicators=[],
actions=[])
action_plan = self.planner.schedule(
self.context, audit.id, fake_solution)
self.assertIsNotNone(action_plan.uuid)
class TestActionValidator(base.DbTestCase):
INSTANCE_UUID = "94ae2f92-b7fd-4da7-9e97-f13504ae98c4"
def setUp(self):
super(TestActionValidator, self).setUp()
self.r_osc_cls = mock.Mock()
self.r_helper_cls = mock.Mock()
self.r_helper = mock.Mock(spec=nova_helper.NovaHelper)
self.r_helper_cls.return_value = self.r_helper
r_nova_helper = mock.patch.object(
nova_helper, "NovaHelper", self.r_helper_cls)
r_nova_helper.start()
self.addCleanup(r_nova_helper.stop)
def test_resize_validate_parents(self):
resize_object = pbase.ResizeActionValidator()
action = {'uuid': 'fcec56cd-74c1-406b-a7c1-81ef9f0c1393',
'input_parameters': {'resource_id': self.INSTANCE_UUID}}
resource_action_map = {self.INSTANCE_UUID: [
('action_uuid', 'migrate')]}
self.r_helper.get_hostname.return_value = 'server1'
self.r_helper.get_instance_by_uuid.return_value = ['instance']
result = resize_object.validate_parents(resource_action_map, action)
self.assertEqual('action_uuid', result[0])
def test_migrate_validate_parents(self):
migrate_object = pbase.MigrationActionValidator()
action = {'uuid': '712f1701-4c1b-4076-bfcf-3f23cfec6c3b',
'input_parameters': {'source_node': 'server1',
'resource_id': self.INSTANCE_UUID}}
resource_action_map = {}
expected_map = {
'94ae2f92-b7fd-4da7-9e97-f13504ae98c4': [
('712f1701-4c1b-4076-bfcf-3f23cfec6c3b', 'migrate')],
'server1': [
('712f1701-4c1b-4076-bfcf-3f23cfec6c3b', 'migrate')]}
migrate_object.validate_parents(resource_action_map, action)
self.assertEqual(resource_action_map, expected_map)

View File

@@ -363,29 +363,25 @@ class TestSyncer(base.DbTestCase):
action_plan1 = objects.ActionPlan(
self.ctx, id=1, uuid=utils.generate_uuid(),
audit_id=audit1.id, strategy_id=strategy1.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
# Stale after syncing because the goal of the audit has been modified
# (compared to the defined fake goals)
action_plan2 = objects.ActionPlan(
self.ctx, id=2, uuid=utils.generate_uuid(),
audit_id=audit2.id, strategy_id=strategy2.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
# Stale after syncing because the strategy has been modified
# (compared to the defined fake strategies)
action_plan3 = objects.ActionPlan(
self.ctx, id=3, uuid=utils.generate_uuid(),
audit_id=audit3.id, strategy_id=strategy3.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
# Stale after syncing because both the strategy and the related audit
# have been modified (compared to the defined fake goals/strategies)
action_plan4 = objects.ActionPlan(
self.ctx, id=4, uuid=utils.generate_uuid(),
audit_id=audit4.id, strategy_id=strategy4.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
action_plan1.create()
action_plan2.create()
@@ -575,14 +571,12 @@ class TestSyncer(base.DbTestCase):
action_plan1 = objects.ActionPlan(
self.ctx, id=1, uuid=utils.generate_uuid(),
audit_id=audit1.id, strategy_id=strategy1.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
# Stale after syncing because its related goal has been soft deleted
action_plan2 = objects.ActionPlan(
self.ctx, id=2, uuid=utils.generate_uuid(),
audit_id=audit2.id, strategy_id=strategy2.id,
first_action_id=None, state='DOESNOTMATTER',
global_efficacy={})
state='DOESNOTMATTER', global_efficacy={})
action_plan1.create()
action_plan2.create()

View File

@@ -413,8 +413,8 @@ expected_object_fingerprints = {
'Strategy': '1.1-73f164491bdd4c034f48083a51bdeb7b',
'AuditTemplate': '1.1-b291973ffc5efa2c61b24fe34fdccc0b',
'Audit': '1.2-910522db78b7b1cb59df614754656db4',
'ActionPlan': '1.2-42709eadf6b2bd228ea87817e8c3e31e',
'Action': '1.1-52c77e4db4ce0aa9480c9760faec61a1',
'ActionPlan': '2.0-394f1abbf5d73d7b6675a118fe1a0284',
'Action': '2.0-1dd4959a7e7ac30c62ef170fe08dd935',
'EfficacyIndicator': '1.0-655b71234a82bc7478aff964639c4bb0',
'ScoringEngine': '1.0-4abbe833544000728e17bd9e83f97576',
'Service': '1.0-4b35b99ada9677a882c9de2b30212f35',