Merge "convergence: add adopt support"
This commit is contained in:
commit
41e5d679cf
|
@ -259,6 +259,7 @@ class Resource(object):
|
||||||
db_res = resource_objects.Resource.get_obj(context, resource_id)
|
db_res = resource_objects.Resource.get_obj(context, resource_id)
|
||||||
# TODO(sirushtim): Load stack from cache
|
# TODO(sirushtim): Load stack from cache
|
||||||
stack = stack_mod.Stack.load(context, db_res.stack_id)
|
stack = stack_mod.Stack.load(context, db_res.stack_id)
|
||||||
|
stack.adopt_stack_data = data.get('adopt_stack_data')
|
||||||
# NOTE(sirushtim): Because on delete/cleanup operations, we simply
|
# NOTE(sirushtim): Because on delete/cleanup operations, we simply
|
||||||
# update with another template, the stack object won't have the
|
# update with another template, the stack object won't have the
|
||||||
# template of the previous stack-run.
|
# template of the previous stack-run.
|
||||||
|
@ -676,7 +677,11 @@ class Resource(object):
|
||||||
set(data[u'id'] for data in resource_data.values()
|
set(data[u'id'] for data in resource_data.values()
|
||||||
if data is not None)
|
if data is not None)
|
||||||
)
|
)
|
||||||
runner = scheduler.TaskRunner(self.create)
|
adopt_data = self.stack._adopt_kwargs(self)
|
||||||
|
if adopt_data['resource_data'] is None:
|
||||||
|
runner = scheduler.TaskRunner(self.create)
|
||||||
|
else:
|
||||||
|
runner = scheduler.TaskRunner(self.adopt, **adopt_data)
|
||||||
runner()
|
runner()
|
||||||
|
|
||||||
@scheduler.wrappertask
|
@scheduler.wrappertask
|
||||||
|
|
|
@ -688,7 +688,6 @@ class EngineService(service.Service):
|
||||||
six.text_type(ex))
|
six.text_type(ex))
|
||||||
|
|
||||||
def _stack_create(stack):
|
def _stack_create(stack):
|
||||||
_create_stack_user(stack)
|
|
||||||
# Create/Adopt a stack, and create the periodic task if successful
|
# Create/Adopt a stack, and create the periodic task if successful
|
||||||
if stack.adopt_stack_data:
|
if stack.adopt_stack_data:
|
||||||
stack.adopt()
|
stack.adopt()
|
||||||
|
@ -710,14 +709,15 @@ class EngineService(service.Service):
|
||||||
nested_depth, user_creds_id, stack_user_project_id, convergence,
|
nested_depth, user_creds_id, stack_user_project_id, convergence,
|
||||||
parent_resource_name)
|
parent_resource_name)
|
||||||
|
|
||||||
# once validations are done
|
stack.store()
|
||||||
# if convergence is enabled, take convergence path
|
_create_stack_user(stack)
|
||||||
if convergence:
|
if convergence:
|
||||||
# TODO(later): call _create_stack_user(stack)
|
|
||||||
# call stack.converge_stack(template=stack.t, action=stack.CREATE)
|
|
||||||
raise exception.NotSupported(feature=_('Convergence engine'))
|
raise exception.NotSupported(feature=_('Convergence engine'))
|
||||||
|
action = stack.CREATE
|
||||||
|
if stack.adopt_stack_data:
|
||||||
|
action = stack.ADOPT
|
||||||
|
stack.converge_stack(template=stack.t, action=action)
|
||||||
else:
|
else:
|
||||||
stack.store()
|
|
||||||
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
self.thread_group_mgr.start_with_lock(cnxt, stack, self.engine_id,
|
||||||
_stack_create, stack)
|
_stack_create, stack)
|
||||||
|
|
||||||
|
@ -782,8 +782,6 @@ class EngineService(service.Service):
|
||||||
self._validate_deferred_auth_context(cnxt, updated_stack)
|
self._validate_deferred_auth_context(cnxt, updated_stack)
|
||||||
updated_stack.validate()
|
updated_stack.validate()
|
||||||
|
|
||||||
# Once all the validations are done
|
|
||||||
# if convergence is enabled, take the convergence path
|
|
||||||
if current_kwargs['convergence']:
|
if current_kwargs['convergence']:
|
||||||
current_stack.converge_stack(template=tmpl,
|
current_stack.converge_stack(template=tmpl,
|
||||||
new_stack=updated_stack)
|
new_stack=updated_stack)
|
||||||
|
|
|
@ -939,7 +939,7 @@ class Stack(collections.Mapping):
|
||||||
"""
|
"""
|
||||||
Updates the stack and triggers convergence for resources
|
Updates the stack and triggers convergence for resources
|
||||||
"""
|
"""
|
||||||
if action is not self.CREATE:
|
if action not in [self.CREATE, self.ADOPT]:
|
||||||
# no back-up template for create action
|
# no back-up template for create action
|
||||||
self.prev_raw_template_id = getattr(self.t, 'id', None)
|
self.prev_raw_template_id = getattr(self.t, 'id', None)
|
||||||
|
|
||||||
|
@ -995,9 +995,11 @@ class Stack(collections.Mapping):
|
||||||
LOG.info(_LI("Triggering resource %(rsrc_id)s "
|
LOG.info(_LI("Triggering resource %(rsrc_id)s "
|
||||||
"for %(is_update)s update"),
|
"for %(is_update)s update"),
|
||||||
{'rsrc_id': rsrc_id, 'is_update': is_update})
|
{'rsrc_id': rsrc_id, 'is_update': is_update})
|
||||||
|
input_data = {'input_data': {},
|
||||||
|
'adopt_stack_data': self.adopt_stack_data}
|
||||||
self.worker_client.check_resource(self.context, rsrc_id,
|
self.worker_client.check_resource(self.context, rsrc_id,
|
||||||
self.current_traversal,
|
self.current_traversal,
|
||||||
{}, is_update)
|
input_data, is_update)
|
||||||
|
|
||||||
def _get_best_existing_rsrc_db(self, rsrc_name):
|
def _get_best_existing_rsrc_db(self, rsrc_name):
|
||||||
candidate = None
|
candidate = None
|
||||||
|
|
|
@ -123,7 +123,7 @@ class WorkerService(service.Service):
|
||||||
stack.state_set(stack.action, stack.FAILED, failure_reason)
|
stack.state_set(stack.action, stack.FAILED, failure_reason)
|
||||||
|
|
||||||
if (not stack.disable_rollback and
|
if (not stack.disable_rollback and
|
||||||
stack.action in (stack.CREATE, stack.UPDATE)):
|
stack.action in (stack.CREATE, stack.ADOPT, stack.UPDATE)):
|
||||||
self._trigger_rollback(cnxt, stack)
|
self._trigger_rollback(cnxt, stack)
|
||||||
else:
|
else:
|
||||||
stack.purge_db()
|
stack.purge_db()
|
||||||
|
@ -137,11 +137,13 @@ class WorkerService(service.Service):
|
||||||
The node may be associated with either an update or a cleanup of its
|
The node may be associated with either an update or a cleanup of its
|
||||||
associated resource.
|
associated resource.
|
||||||
'''
|
'''
|
||||||
|
adopt_data = data.get('adopt_stack_data')
|
||||||
data = dict(sync_point.deserialize_input_data(data))
|
data = dict(sync_point.deserialize_input_data(data))
|
||||||
try:
|
try:
|
||||||
cache_data = {in_data.get(
|
cache_data = {in_data.get(
|
||||||
'name'): in_data for in_data in data.values()
|
'name'): in_data for in_data in data.values()
|
||||||
if in_data is not None}
|
if in_data is not None}
|
||||||
|
cache_data['adopt_stack_data'] = adopt_data
|
||||||
rsrc, stack = resource.Resource.load(cnxt, resource_id, cache_data)
|
rsrc, stack = resource.Resource.load(cnxt, resource_id, cache_data)
|
||||||
except (exception.ResourceNotFound, exception.NotFound):
|
except (exception.ResourceNotFound, exception.NotFound):
|
||||||
return
|
return
|
||||||
|
|
|
@ -212,7 +212,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
for rsrc_id, is_update in leaves:
|
for rsrc_id, is_update in leaves:
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
stack.context, rsrc_id, stack.current_traversal, {},
|
stack.context, rsrc_id, stack.current_traversal,
|
||||||
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
is_update))
|
is_update))
|
||||||
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
||||||
|
|
||||||
|
@ -268,7 +269,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
for rsrc_id, is_update in leaves:
|
for rsrc_id, is_update in leaves:
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
stack.context, rsrc_id, stack.current_traversal, {},
|
stack.context, rsrc_id, stack.current_traversal,
|
||||||
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
is_update))
|
is_update))
|
||||||
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
||||||
|
|
||||||
|
@ -403,7 +405,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
for rsrc_id, is_update in leaves:
|
for rsrc_id, is_update in leaves:
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
stack.context, rsrc_id, stack.current_traversal, {},
|
stack.context, rsrc_id, stack.current_traversal,
|
||||||
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
is_update))
|
is_update))
|
||||||
|
|
||||||
leaves = curr_stack.convergence_dependencies.leaves()
|
leaves = curr_stack.convergence_dependencies.leaves()
|
||||||
|
@ -411,7 +414,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
curr_stack.context, rsrc_id, curr_stack.current_traversal,
|
curr_stack.context, rsrc_id, curr_stack.current_traversal,
|
||||||
{}, is_update))
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
|
is_update))
|
||||||
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
||||||
|
|
||||||
def test_conv_empty_template_stack_update_delete(self, mock_cr):
|
def test_conv_empty_template_stack_update_delete(self, mock_cr):
|
||||||
|
@ -482,7 +486,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
for rsrc_id, is_update in leaves:
|
for rsrc_id, is_update in leaves:
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
stack.context, rsrc_id, stack.current_traversal, {},
|
stack.context, rsrc_id, stack.current_traversal,
|
||||||
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
is_update))
|
is_update))
|
||||||
|
|
||||||
leaves = curr_stack.convergence_dependencies.leaves()
|
leaves = curr_stack.convergence_dependencies.leaves()
|
||||||
|
@ -490,7 +495,8 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
|
||||||
expected_calls.append(
|
expected_calls.append(
|
||||||
mock.call.worker_client.WorkerClient.check_resource(
|
mock.call.worker_client.WorkerClient.check_resource(
|
||||||
curr_stack.context, rsrc_id, curr_stack.current_traversal,
|
curr_stack.context, rsrc_id, curr_stack.current_traversal,
|
||||||
{}, is_update))
|
{'input_data': {}, 'adopt_stack_data': None},
|
||||||
|
is_update))
|
||||||
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
self.assertEqual(expected_calls, mock_cr.mock_calls)
|
||||||
|
|
||||||
def test_mark_complete_purges_db(self, mock_cr):
|
def test_mark_complete_purges_db(self, mock_cr):
|
||||||
|
|
|
@ -1430,6 +1430,7 @@ class ResourceTest(common.HeatTestCase):
|
||||||
def test_create_convergence(self, mock_create):
|
def test_create_convergence(self, mock_create):
|
||||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||||
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||||
|
res.action = res.CREATE
|
||||||
res._store()
|
res._store()
|
||||||
self._assert_resource_lock(res.id, None, None)
|
self._assert_resource_lock(res.id, None, None)
|
||||||
res_data = {(1, True): {u'id': 1, u'name': 'A', 'attrs': {}},
|
res_data = {(1, True): {u'id': 1, u'name': 'A', 'attrs': {}},
|
||||||
|
@ -1459,6 +1460,24 @@ class ResourceTest(common.HeatTestCase):
|
||||||
self.assertItemsEqual([5, 3], res.requires)
|
self.assertItemsEqual([5, 3], res.requires)
|
||||||
self._assert_resource_lock(res.id, None, 2)
|
self._assert_resource_lock(res.id, None, 2)
|
||||||
|
|
||||||
|
@mock.patch.object(resource.Resource, 'adopt')
|
||||||
|
def test_adopt_convergence(self, mock_adopt):
|
||||||
|
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||||
|
res = generic_rsrc.GenericResource('test_res', tmpl, self.stack)
|
||||||
|
res.action = res.ADOPT
|
||||||
|
res._store()
|
||||||
|
self.stack.adopt_stack_data = {'resources': {'test_res': {
|
||||||
|
'resource_id': 'fluffy'}}}
|
||||||
|
self._assert_resource_lock(res.id, None, None)
|
||||||
|
res_data = {(1, True): {u'id': 5, u'name': 'A', 'attrs': {}},
|
||||||
|
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||||
|
res.create_convergence(res_data, 'engine-007')
|
||||||
|
|
||||||
|
mock_adopt.assert_called_once_with(
|
||||||
|
resource_data={'resource_id': 'fluffy'})
|
||||||
|
self.assertItemsEqual([5, 3], res.requires)
|
||||||
|
self._assert_resource_lock(res.id, None, 2)
|
||||||
|
|
||||||
@mock.patch.object(resource.Resource, 'update')
|
@mock.patch.object(resource.Resource, 'update')
|
||||||
def test_update_convergence(self, mock_update):
|
def test_update_convergence(self, mock_update):
|
||||||
tmpl = rsrc_defn.ResourceDefinition('test_res',
|
tmpl = rsrc_defn.ResourceDefinition('test_res',
|
||||||
|
|
Loading…
Reference in New Issue