Convergence: Load resource stack with correct template
When loading a resource, load the stack with template of the resource. Appropriate stack needs to be assigned to resource(resource.stack), else resource actions will fail. Co-Authored-By: Anant Patil <anant.patil@hp.com> Partial-Bug: #1512343 Change-Id: Ic4526152c8fd027049514b71554036321a61efd2
This commit is contained in:
parent
7252e659ee
commit
77c11d037c
|
@ -44,6 +44,7 @@ from heat.engine import support
|
|||
from heat.engine import template
|
||||
from heat.objects import resource as resource_objects
|
||||
from heat.objects import resource_data as resource_data_objects
|
||||
from heat.objects import stack as stack_objects
|
||||
from heat.rpc import client as rpc_client
|
||||
|
||||
cfg.CONF.import_opt('action_retry_limit', 'heat.common.config')
|
||||
|
@ -263,30 +264,32 @@ class Resource(object):
|
|||
def load(cls, context, resource_id, is_update, data):
|
||||
from heat.engine import stack as stack_mod
|
||||
db_res = resource_objects.Resource.get_obj(context, resource_id)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def special_stack(tmpl, swap_template):
|
||||
stk = stack_mod.Stack.load(context, db_res.stack_id,
|
||||
curr_stack = stack_mod.Stack.load(context, stack_id=db_res.stack_id,
|
||||
cache_data=data)
|
||||
|
||||
# NOTE(sirushtim): Because on delete/cleanup operations, we simply
|
||||
# update with another template, the stack object won't have the
|
||||
# template of the previous stack-run.
|
||||
if swap_template:
|
||||
prev_tmpl = stk.t
|
||||
stk.t = tmpl
|
||||
stk.resources
|
||||
yield stk
|
||||
if swap_template:
|
||||
stk.t = prev_tmpl
|
||||
resource_owning_stack = curr_stack
|
||||
if db_res.current_template_id != curr_stack.t.id:
|
||||
# load stack with template owning the resource
|
||||
db_stack = stack_objects.Stack.get_by_id(context, db_res.stack_id)
|
||||
db_stack.raw_template = None
|
||||
db_stack.raw_template_id = db_res.current_template_id
|
||||
resource_owning_stack = stack_mod.Stack.load(context,
|
||||
stack=db_stack)
|
||||
|
||||
tmpl = template.Template.load(context, db_res.current_template_id)
|
||||
with special_stack(tmpl, not is_update) as stack:
|
||||
stack_res = tmpl.resource_definitions(stack)[db_res.name]
|
||||
resource = cls(db_res.name, stack_res, stack)
|
||||
# Load only the resource in question; don't load all resources
|
||||
# by invoking stack.resources. Maintain light-weight stack.
|
||||
res_defn = resource_owning_stack.t.resource_definitions(
|
||||
resource_owning_stack)[db_res.name]
|
||||
resource = cls(db_res.name, res_defn, resource_owning_stack)
|
||||
resource._load_data(db_res)
|
||||
|
||||
return resource, stack
|
||||
# assign current stack to the resource for updates
|
||||
if is_update:
|
||||
resource.stack = curr_stack
|
||||
|
||||
# return resource owning stack so that it is not GCed since it
|
||||
# is the only stack instance with a weak-ref from resource
|
||||
return resource, resource_owning_stack, curr_stack
|
||||
|
||||
def make_replacement(self, new_tmpl_id):
|
||||
# 1. create the replacement with "replaces" = self.id
|
||||
|
@ -886,7 +889,7 @@ class Resource(object):
|
|||
return True
|
||||
|
||||
def update_convergence(self, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
timeout, new_stack):
|
||||
"""Update the resource synchronously.
|
||||
|
||||
Persist the resource's current_template_id to template_id and
|
||||
|
@ -903,7 +906,7 @@ class Resource(object):
|
|||
|
||||
with self.lock(engine_id):
|
||||
new_temp = template.Template.load(self.context, template_id)
|
||||
new_res_def = new_temp.resource_definitions(self.stack)[self.name]
|
||||
new_res_def = new_temp.resource_definitions(new_stack)[self.name]
|
||||
if self.stack.action == self.stack.ROLLBACK and \
|
||||
self.stack.status == self.stack.IN_PROGRESS \
|
||||
and self.replaced_by:
|
||||
|
|
|
@ -466,7 +466,7 @@ class Port(neutron.NeutronResource):
|
|||
|
||||
props = {'fixed_ips': []}
|
||||
if convergence:
|
||||
existing_port, stack = resource.Resource.load(
|
||||
existing_port, rsrc_owning_stack, stack = resource.Resource.load(
|
||||
prev_port.context, prev_port.replaced_by, True,
|
||||
prev_port.stack.cache_data
|
||||
)
|
||||
|
|
|
@ -397,7 +397,7 @@ class ServerNetworkMixin(object):
|
|||
self.stack._backup_stack().resources.get(self.name)
|
||||
|
||||
if convergence:
|
||||
rsrc, stack = resource.Resource.load(
|
||||
rsrc, rsrc_owning_stack, stack = resource.Resource.load(
|
||||
prev_server.context, prev_server.replaced_by, True,
|
||||
prev_server.stack.cache_data
|
||||
)
|
||||
|
|
|
@ -140,14 +140,13 @@ class WorkerService(service.Service):
|
|||
# no data to resolve in cleanup phase
|
||||
cache_data = {}
|
||||
|
||||
rsrc, stack = None, None
|
||||
try:
|
||||
rsrc, stack = resource.Resource.load(cnxt, resource_id, is_update,
|
||||
cache_data)
|
||||
return resource.Resource.load(cnxt, resource_id,
|
||||
is_update, cache_data)
|
||||
except (exception.ResourceNotFound, exception.NotFound):
|
||||
pass # can be ignored
|
||||
|
||||
return rsrc, stack
|
||||
return None, None, None
|
||||
|
||||
def _do_check_resource(self, cnxt, current_traversal, tmpl, resource_data,
|
||||
is_update, rsrc, stack, adopt_stack_data):
|
||||
|
@ -156,7 +155,7 @@ class WorkerService(service.Service):
|
|||
try:
|
||||
check_resource_update(rsrc, tmpl.id, resource_data,
|
||||
self.engine_id,
|
||||
stack.time_remaining())
|
||||
stack)
|
||||
except exception.UpdateReplace:
|
||||
new_res_id = rsrc.make_replacement(tmpl.id)
|
||||
LOG.info("Replacing resource with new id %s", new_res_id)
|
||||
|
@ -229,7 +228,7 @@ class WorkerService(service.Service):
|
|||
|
||||
def _get_input_data(req, fwd):
|
||||
if fwd:
|
||||
return construct_input_data(rsrc)
|
||||
return construct_input_data(rsrc, stack)
|
||||
else:
|
||||
# Don't send data if initiating clean-up for self i.e.
|
||||
# initiating delete of a replaced resource
|
||||
|
@ -272,7 +271,8 @@ class WorkerService(service.Service):
|
|||
associated resource.
|
||||
"""
|
||||
resource_data = dict(sync_point.deserialize_input_data(data))
|
||||
rsrc, stack = self._load_resource(cnxt, resource_id, resource_data,
|
||||
rsrc, rsrc_owning_stack, stack = self._load_resource(cnxt, resource_id,
|
||||
resource_data,
|
||||
is_update)
|
||||
|
||||
if rsrc is None:
|
||||
|
@ -307,10 +307,10 @@ class WorkerService(service.Service):
|
|||
rsrc, stack)
|
||||
|
||||
|
||||
def construct_input_data(rsrc):
|
||||
attributes = rsrc.stack.get_dep_attrs(
|
||||
six.itervalues(rsrc.stack.resources),
|
||||
rsrc.stack.outputs,
|
||||
def construct_input_data(rsrc, curr_stack):
|
||||
attributes = curr_stack.get_dep_attrs(
|
||||
six.itervalues(curr_stack.resources),
|
||||
curr_stack.outputs,
|
||||
rsrc.name)
|
||||
resolved_attributes = {}
|
||||
for attr in attributes:
|
||||
|
@ -366,12 +366,14 @@ def propagate_check_resource(cnxt, rpc_client, next_res_id,
|
|||
|
||||
|
||||
def check_resource_update(rsrc, template_id, resource_data, engine_id,
|
||||
timeout):
|
||||
stack):
|
||||
"""Create or update the Resource if appropriate."""
|
||||
if rsrc.action == resource.Resource.INIT:
|
||||
rsrc.create_convergence(template_id, resource_data, engine_id, timeout)
|
||||
rsrc.create_convergence(template_id, resource_data, engine_id,
|
||||
stack.time_remaining())
|
||||
else:
|
||||
rsrc.update_convergence(template_id, resource_data, engine_id, timeout)
|
||||
rsrc.update_convergence(template_id, resource_data, engine_id,
|
||||
stack.time_remaining(), stack)
|
||||
|
||||
|
||||
def check_resource_cleanup(rsrc, template_id, resource_data, engine_id,
|
||||
|
|
|
@ -179,7 +179,7 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
mock_cru.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id,
|
||||
tr())
|
||||
mock.ANY)
|
||||
self.assertTrue(mock_mr.called)
|
||||
self.assertFalse(mock_crc.called)
|
||||
self.assertFalse(mock_pcr.called)
|
||||
|
@ -200,7 +200,7 @@ class CheckWorkflowUpdateTest(common.HeatTestCase):
|
|||
mock_cru.assert_called_once_with(self.resource,
|
||||
self.resource.stack.t.id,
|
||||
{}, self.worker.engine_id,
|
||||
tr())
|
||||
mock.ANY)
|
||||
self.assertFalse(mock_crc.called)
|
||||
self.assertFalse(mock_pcr.called)
|
||||
self.assertFalse(mock_csc.called)
|
||||
|
@ -547,7 +547,8 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
'uuid': mock.ANY,
|
||||
'action': mock.ANY,
|
||||
'status': mock.ANY}
|
||||
actual_input_data = worker.construct_input_data(self.resource)
|
||||
actual_input_data = worker.construct_input_data(self.resource,
|
||||
self.stack)
|
||||
self.assertEqual(expected_input_data, actual_input_data)
|
||||
|
||||
def test_construct_input_data_exception(self):
|
||||
|
@ -561,7 +562,8 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self.resource.FnGetAtt = mock.Mock(
|
||||
side_effect=exception.InvalidTemplateAttribute(resource='A',
|
||||
key='value'))
|
||||
actual_input_data = worker.construct_input_data(self.resource)
|
||||
actual_input_data = worker.construct_input_data(self.resource,
|
||||
self.stack)
|
||||
self.assertEqual(expected_input_data, actual_input_data)
|
||||
|
||||
@mock.patch.object(sync_point, 'sync')
|
||||
|
@ -608,7 +610,7 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self.resource.action = 'INIT'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.stack)
|
||||
self.assertTrue(mock_create.called)
|
||||
self.assertFalse(mock_update.called)
|
||||
|
||||
|
@ -619,7 +621,7 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self.resource.action = 'CREATE'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.stack)
|
||||
self.assertFalse(mock_create.called)
|
||||
self.assertTrue(mock_update.called)
|
||||
|
||||
|
@ -630,7 +632,7 @@ class MiscMethodsTest(common.HeatTestCase):
|
|||
self.resource.action = 'UPDATE'
|
||||
worker.check_resource_update(self.resource, self.resource.stack.t.id,
|
||||
{}, 'engine-id',
|
||||
self.stack.timeout_secs())
|
||||
self.stack)
|
||||
self.assertFalse(mock_create.called)
|
||||
self.assertTrue(mock_update.called)
|
||||
|
||||
|
|
|
@ -95,8 +95,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.current_template_id = self.stack.t.id
|
||||
res.state_set('CREATE', 'IN_PROGRESS')
|
||||
self.stack.add_resource(res)
|
||||
loaded_res, stack = resource.Resource.load(self.stack.context,
|
||||
res.id, True, {})
|
||||
loaded_res, res_owning_stack, stack = resource.Resource.load(
|
||||
self.stack.context, res.id, True, {})
|
||||
self.assertEqual(loaded_res.id, res.id)
|
||||
self.assertEqual(self.stack.t, stack.t)
|
||||
|
||||
|
@ -119,8 +119,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.current_template_id = self.old_stack.t.id
|
||||
res.state_set('CREATE', 'IN_PROGRESS')
|
||||
self.old_stack.add_resource(res)
|
||||
loaded_res, stack = resource.Resource.load(self.old_stack.context,
|
||||
res.id, False, {})
|
||||
loaded_res, res_owning_stack, stack = resource.Resource.load(
|
||||
self.old_stack.context, res.id, False, {})
|
||||
self.assertEqual(loaded_res.id, res.id)
|
||||
self.assertEqual(self.old_stack.t, stack.t)
|
||||
self.assertNotEqual(self.new_stack.t, stack.t)
|
||||
|
@ -141,12 +141,12 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.current_template_id = self.stack.t.id
|
||||
res.state_set('CREATE', 'IN_PROGRESS')
|
||||
self.stack.add_resource(res)
|
||||
origin_resources = self.stack._resources
|
||||
origin_resources = self.stack.resources
|
||||
self.stack._resources = None
|
||||
|
||||
loaded_res, stack = resource.Resource.load(self.stack.context,
|
||||
res.id, False, {})
|
||||
self.assertEqual(origin_resources, stack._resources)
|
||||
loaded_res, res_owning_stack, stack = resource.Resource.load(
|
||||
self.stack.context, res.id, False, {})
|
||||
self.assertEqual(origin_resources, stack.resources)
|
||||
self.assertEqual(loaded_res.id, res.id)
|
||||
self.assertEqual(self.stack.t, stack.t)
|
||||
|
||||
|
@ -1757,7 +1757,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
|
||||
res_data = {(1, True): {u'id': 4, u'name': 'A', 'attrs': {}},
|
||||
(2, True): {u'id': 3, u'name': 'B', 'attrs': {}}}
|
||||
res.update_convergence(new_temp.id, res_data, 'engine-007', 120)
|
||||
res.update_convergence(new_temp.id, res_data, 'engine-007', 120,
|
||||
mock.ANY)
|
||||
|
||||
expected_rsrc_def = new_temp.resource_definitions(self.stack)[res.name]
|
||||
mock_init.assert_called_once_with(res.update, expected_rsrc_def)
|
||||
|
@ -1783,7 +1784,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
res_data = {}
|
||||
self.assertRaises(scheduler.Timeout, res.update_convergence,
|
||||
new_temp.id, res_data, 'engine-007',
|
||||
0)
|
||||
0, mock.ANY)
|
||||
|
||||
def test_update_in_progress_convergence(self):
|
||||
tmpl = rsrc_defn.ResourceDefinition('test_res', 'Foo')
|
||||
|
@ -1800,7 +1801,8 @@ class ResourceTest(common.HeatTestCase):
|
|||
res.update_convergence,
|
||||
'template_key',
|
||||
res_data, 'engine-007',
|
||||
self.dummy_timeout)
|
||||
self.dummy_timeout,
|
||||
mock.ANY)
|
||||
msg = ("The resource %s is already being updated." %
|
||||
res.name)
|
||||
self.assertEqual(msg, six.text_type(ex))
|
||||
|
@ -1831,7 +1833,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
mock_update.side_effect = dummy_ex
|
||||
self.assertRaises(exception.ResourceFailure,
|
||||
res.update_convergence, new_temp.id, res_data,
|
||||
'engine-007', 120)
|
||||
'engine-007', 120, mock.ANY)
|
||||
|
||||
expected_rsrc_def = new_temp.resource_definitions(self.stack)[res.name]
|
||||
mock_update.assert_called_once_with(expected_rsrc_def)
|
||||
|
@ -1863,7 +1865,7 @@ class ResourceTest(common.HeatTestCase):
|
|||
mock_update.side_effect = exception.UpdateReplace
|
||||
self.assertRaises(exception.UpdateReplace,
|
||||
res.update_convergence, new_temp.id, res_data,
|
||||
'engine-007', 120)
|
||||
'engine-007', 120, mock.ANY)
|
||||
|
||||
expected_rsrc_def = new_temp.resource_definitions(self.stack)[res.name]
|
||||
mock_update.assert_called_once_with(expected_rsrc_def)
|
||||
|
@ -2096,12 +2098,14 @@ class ResourceTest(common.HeatTestCase):
|
|||
stack.store()
|
||||
mock_tmpl_load.return_value = tmpl
|
||||
res = stack['res']
|
||||
res.current_template_id = stack.t.id
|
||||
res._store()
|
||||
data = {'bar': {'atrr1': 'baz', 'attr2': 'baz2'}}
|
||||
mock_stack_load.return_value = stack
|
||||
resource.Resource.load(stack.context, res.id, True, data)
|
||||
mock_stack_load.assert_called_once_with(stack.context,
|
||||
stack.id,
|
||||
self.assertTrue(mock_stack_load.called)
|
||||
mock_stack_load.assert_called_with(stack.context,
|
||||
stack_id=stack.id,
|
||||
cache_data=data)
|
||||
self.assertTrue(mock_load_data.called)
|
||||
|
||||
|
|
Loading…
Reference in New Issue