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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
102
watcher/tests/applier/actions/test_resize.py
Normal file
102
watcher/tests/applier/actions/test_resize.py
Normal 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')
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
943
watcher/tests/decision_engine/planner/test_weight_planner.py
Normal file
943
watcher/tests/decision_engine/planner/test_weight_planner.py
Normal 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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user