Use StackDefinition in intrinsic functions for resolving data

This unifies the 'lightweight stack' used in convergence with the how
things work the rest of the time: we now always obtain resource data from
the StackDefinition's ResourceProxy objects. This means that when we are
checking an individual resource, we will never create all of the other
Resource objects for the stack in memory (although we already avoided
loading data for them from the database) - now we will only create
ResourceProxy objects as needed.

Change-Id: Id7472557e26d172df88841ff7f20afdd7f5bfada
Implements: blueprint stack-definition
changes/77/435177/30
Zane Bitter 5 years ago
parent c889f08ab5
commit 764b8fb251
  1. 3
      heat/engine/function.py
  2. 4
      heat/engine/hot/functions.py
  3. 6
      heat/engine/resource.py
  4. 2
      heat/engine/resources/openstack/heat/instance_group.py
  5. 3
      heat/engine/resources/openstack/heat/resource_group.py
  6. 85
      heat/engine/stack.py
  7. 2
      heat/engine/update.py
  8. 2
      heat/tests/autoscaling/test_heat_scaling_policy.py
  9. 2
      heat/tests/autoscaling/test_launch_config.py
  10. 2
      heat/tests/autoscaling/test_scaling_policy.py
  11. 28
      heat/tests/aws/test_eip.py
  12. 5
      heat/tests/aws/test_loadbalancer.py
  13. 2
      heat/tests/aws/test_user.py
  14. 2
      heat/tests/aws/test_waitcondition.py
  15. 5
      heat/tests/openstack/cinder/test_volume_utils.py
  16. 4
      heat/tests/openstack/heat/test_instance_group.py
  17. 2
      heat/tests/openstack/heat/test_random_string.py
  18. 2
      heat/tests/openstack/heat/test_remote_stack.py
  19. 4
      heat/tests/openstack/heat/test_resource_chain.py
  20. 4
      heat/tests/openstack/heat/test_resource_group.py
  21. 3
      heat/tests/openstack/heat/test_software_deployment.py
  22. 2
      heat/tests/openstack/heat/test_swiftsignal.py
  23. 6
      heat/tests/openstack/mistral/test_workflow.py
  24. 8
      heat/tests/openstack/neutron/test_neutron_floating_ip.py
  25. 10
      heat/tests/openstack/neutron/test_neutron_subnet.py
  26. 8
      heat/tests/openstack/neutron/test_neutron_trunk.py
  27. 2
      heat/tests/openstack/nova/test_floatingip.py
  28. 2
      heat/tests/openstack/nova/test_keypair.py
  29. 2
      heat/tests/openstack/swift/test_container.py
  30. 49
      heat/tests/test_function.py
  31. 210
      heat/tests/test_hot.py
  32. 9
      heat/tests/test_metadata_refresh.py
  33. 2
      heat/tests/test_nested_stack.py
  34. 45
      heat/tests/test_resource.py
  35. 25
      heat/tests/test_stack.py
  36. 5
      heat/tests/test_stack_collect_attributes.py
  37. 4
      heat/tests/test_stack_resource.py
  38. 16
      heat/tests/test_stack_update.py
  39. 19
      heat/tests/test_template.py

@ -45,7 +45,8 @@ class Function(object):
return None
stack = ref()
assert stack is not None, "Need a reference to the Stack object"
assert stack is not None, ("Need a reference to the "
"StackDefinition object")
return stack
def validate(self):

@ -222,9 +222,7 @@ class GetAttThenSelect(function.Function):
return
attr = function.resolve(self._attribute)
from heat.engine import resource
if (type(res).get_attribute == resource.Resource.get_attribute and
attr not in res.attributes_schema):
if attr not in res.attributes_schema:
raise exception.InvalidTemplateAttribute(
resource=self._resource_name, key=attr)

@ -340,8 +340,7 @@ class Resource(status.ResourceStatus):
# 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]
res_defn = resource_owning_stack.defn.resource_definition(db_res.name)
resource = cls(db_res.name, res_defn, resource_owning_stack)
resource._load_data(db_res)
@ -1343,8 +1342,7 @@ class Resource(status.ResourceStatus):
self._calling_engine_id = engine_id
registry = new_stack.env.registry
new_res_def = new_stack.t.resource_definitions(
new_stack)[self.name]
new_res_def = new_stack.defn.resource_definition(self.name)
new_res_type = registry.get_class_to_instantiate(
new_res_def.resource_type, resource_name=self.name)
restricted_actions = registry.get_rsrc_restricted_actions(

@ -309,7 +309,7 @@ class InstanceGroup(stack_resource.StackResource):
def changing_instances(tmpl):
instances = grouputils.get_members(self)
current = set((i.name, i.t) for i in instances)
updated = set(tmpl.resource_definitions(self.nested()).items())
updated = set(tmpl.resource_definitions(None).items())
# includes instances to be updated and deleted
affected = set(k for k, v in current ^ updated)
return set(i.FnGetRefId() for i in instances if i.name in affected)

@ -283,8 +283,7 @@ class ResourceGroup(stack_resource.StackResource):
first_name = next(self._resource_names(update_rsrc_data=False))
test_tmpl = self._assemble_nested([first_name],
include_all=True)
res_def = next(six.itervalues(
test_tmpl.resource_definitions(self.stack)))
res_def = next(six.itervalues(test_tmpl.resource_definitions(None)))
# make sure we can resolve the nested resource type
self.stack.env.get_class_to_instantiate(res_def.resource_type)

@ -298,18 +298,18 @@ class Stack(collections.Mapping):
@property
def outputs(self):
if self._outputs is None:
self._outputs = self.t.outputs(self)
return self._outputs
return {n: self.defn.output_definition(n)
for n in self.defn.enabled_output_names()}
@property
def resources(self):
if self._resources is None:
res_defns = self.t.resource_definitions(self)
self._resources = dict((name,
resource.Resource(name, data, self))
for (name, data) in res_defns.items())
self._resources = {
name: resource.Resource(name,
self.defn.resource_definition(name),
self)
for name in self.defn.enabled_rsrc_names()
}
return self._resources
@ -320,7 +320,6 @@ class Stack(collections.Mapping):
stk_defn.update_resource_data(self.defn, rsrc.name, node_data)
def _find_filtered_resources(self, filters=None):
rsrc_def_cache = {self.t.id: self.t.resource_definitions(self)}
if filters:
assert self.cache_data is None, \
"Resources should not be loaded from the DB"
@ -328,8 +327,10 @@ class Stack(collections.Mapping):
self.context, self.id, filters)
else:
resources = self._db_resources_get()
stk_def_cache = {}
for rsc in six.itervalues(resources):
loaded_res = self._resource_from_db_resource(rsc, rsrc_def_cache)
loaded_res = self._resource_from_db_resource(rsc, stk_def_cache)
if loaded_res is not None:
yield loaded_res
@ -374,7 +375,7 @@ class Stack(collections.Mapping):
self._db_resources = _db_resources
return self._db_resources
def _resource_from_db_resource(self, db_res, rsrc_def_cache=None):
def _resource_from_db_resource(self, db_res, stk_def_cache=None):
tid = db_res.current_template_id
if tid is None:
tid = self.t.id
@ -384,19 +385,19 @@ class Stack(collections.Mapping):
if cur_res is not None and (cur_res.id == db_res.id):
return cur_res
if rsrc_def_cache and tid in rsrc_def_cache:
rsrc_def = rsrc_def_cache[tid]
stk_def = self.defn
elif stk_def_cache and tid in stk_def_cache:
stk_def = stk_def_cache[tid]
else:
if tid == self.t.id:
rsrc_def = self.t.resource_definitions(self)
else:
t = tmpl.Template.load(self.context, tid)
rsrc_def = t.resource_definitions(self)
if rsrc_def_cache:
rsrc_def_cache[tid] = rsrc_def
defn = rsrc_def.get(db_res.name)
t = tmpl.Template.load(self.context, tid)
stk_def = self.defn.clone_with_new_template(t,
self.identifier())
if stk_def_cache is not None:
stk_def_cache[tid] = stk_def
if defn is None:
try:
defn = stk_def.resource_definition(db_res.name)
except KeyError:
return None
return resource.Resource(db_res.name, defn, self)
@ -724,7 +725,7 @@ class Stack(collections.Mapping):
"""Insert the given resource into the stack."""
template = resource.stack.t
resource.stack = self
definition = resource.t.reparse(self, template)
definition = resource.t.reparse(self.defn, template)
resource.t = definition
resource.reparse()
self.resources[resource.name] = resource
@ -769,16 +770,23 @@ class Stack(collections.Mapping):
not found.
"""
for r in six.itervalues(self):
if (r.state in (
(r.INIT, r.COMPLETE),
(r.CREATE, r.IN_PROGRESS),
(r.CREATE, r.COMPLETE),
(r.RESUME, r.IN_PROGRESS),
(r.RESUME, r.COMPLETE),
(r.UPDATE, r.IN_PROGRESS),
(r.UPDATE, r.COMPLETE),
(r.CHECK, r.COMPLETE)) and
(r.FnGetRefId() == refid or r.name == refid)):
if r.state not in ((r.INIT, r.COMPLETE),
(r.CREATE, r.IN_PROGRESS),
(r.CREATE, r.COMPLETE),
(r.RESUME, r.IN_PROGRESS),
(r.RESUME, r.COMPLETE),
(r.UPDATE, r.IN_PROGRESS),
(r.UPDATE, r.COMPLETE),
(r.CHECK, r.COMPLETE)):
continue
proxy = self.defn[r.name]
if proxy._resource_data is None:
matches = r.FnGetRefId() == refid or r.name == refid
else:
matches = proxy.FnGetRefId() == refid
if matches:
if self.cache_data is not None and r.id is not None:
# We don't have resources loaded from the database at this
# point, so load the data for just this one from the DB.
@ -786,7 +794,6 @@ class Stack(collections.Mapping):
r.id)
if db_res is not None:
r._load_data(db_res)
return r
def register_access_allowed_handler(self, credential_id, handler):
@ -852,6 +859,8 @@ class Stack(collections.Mapping):
raise exception.StackValidationFailed(
message=_("Duplicate names %s") % dup_names)
self._update_all_resource_data(for_resources=True, for_outputs=True)
if self.strict_validate:
iter_rsc = self.dependencies
else:
@ -1252,7 +1261,9 @@ class Stack(collections.Mapping):
self.prev_raw_template_id = getattr(self.t, 'id', None)
# switch template and reset dependencies
self.t = template
self.defn = self.defn.clone_with_new_template(template,
self.identifier(),
clear_resource_data=True)
self.reset_dependencies()
self._resources = None
self.cache_data = None
@ -1937,8 +1948,8 @@ class Stack(collections.Mapping):
timeout_mins=self.timeout_mins,
disable_rollback=self.disable_rollback)
for name, defn in six.iteritems(
template.resource_definitions(newstack)):
for name in newstack.defn.enabled_rsrc_names():
defn = newstack.defn.resource_definition(name)
rsrc = resource.Resource(name, defn, self)
data = snapshot.data['resources'].get(name)
handle_restore = getattr(rsrc, 'handle_restore', None)

@ -205,7 +205,7 @@ class StackUpdate(object):
# of the existing stack (which is the stack being updated)
# but with the template of the new stack (in case the update
# is switching template implementations)
new_snippet = new_res.t.reparse(self.existing_stack,
new_snippet = new_res.t.reparse(self.existing_stack.defn,
self.new_stack.t)
if is_substituted:
substitute = type(new_res)(existing_res.name,

@ -166,7 +166,7 @@ class TestAutoScalingPolicy(common.HeatTestCase):
'reference_id': 'convg_xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['my-policy']
rsrc = stack.defn['my-policy']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -70,7 +70,7 @@ class LaunchConfigurationTest(common.HeatTestCase):
})}
stack = utils.parse_stack(t, params=inline_templates.as_params,
cache_data=cache_data)
rsrc = stack['LaunchConfig']
rsrc = stack.defn['LaunchConfig']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())
def test_launch_config_create_with_instanceid(self):

@ -166,7 +166,7 @@ class TestAutoScalingPolicy(common.HeatTestCase):
'reference_id': 'http://convg_signed_url'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['WebServerScaleUpPolicy']
rsrc = stack.defn['WebServerScaleUpPolicy']
self.assertEqual('http://convg_signed_url', rsrc.FnGetRefId())

@ -405,7 +405,7 @@ class EIPTest(common.HeatTestCase):
'status': 'COMPLETE',
'reference_id': '1.1.1.1'})})
rsrc = stack['eip']
rsrc = stack.defn['eip']
self.assertEqual('1.1.1.1', rsrc.FnGetRefId())
@ -480,9 +480,8 @@ class AllocTest(common.HeatTestCase):
self.fc.servers.get(server).AndReturn(mock_server)
def create_eip(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)
rsrc = eip.ElasticIp(resource_name,
resource_defns[resource_name],
stack.defn.resource_definition(resource_name),
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
@ -492,9 +491,9 @@ class AllocTest(common.HeatTestCase):
return rsrc
def create_association(self, t, stack, resource_name):
resource_defns = stack.t.resource_definitions(stack)
resource_defn = stack.defn.resource_definition(resource_name)
rsrc = eip.ElasticIpAssociation(resource_name,
resource_defns[resource_name],
resource_defn,
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()
@ -530,7 +529,7 @@ class AllocTest(common.HeatTestCase):
}).AndReturn({'floatingip': {
"status": "ACTIVE",
"id": "fc68ea2c-b60b-4b4f-bd82-94ec81110766",
"floating_ip_address": "192.168.9.3"
"floating_ip_address": "11.0.0.1"
}})
def mock_show_floatingip(self, refid):
@ -766,7 +765,6 @@ class AllocTest(common.HeatTestCase):
mock_again=True)
self.mock_create_floatingip()
self.mock_show_floatingip('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
self.m.ReplayAll()
t = template_format.parse(eip_template_ipassoc)
@ -780,7 +778,7 @@ class AllocTest(common.HeatTestCase):
update_server_id = '5678'
props['InstanceId'] = update_server_id
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -791,7 +789,6 @@ class AllocTest(common.HeatTestCase):
server = self.fc.servers.list()[0]
self._mock_server_get(mock_server=server, multiple=True)
self.mock_create_floatingip()
self.mock_show_floatingip('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
self.m.ReplayAll()
t = template_format.parse(eip_template_ipassoc)
@ -804,7 +801,7 @@ class AllocTest(common.HeatTestCase):
update_eip = '11.0.0.2'
props['EIP'] = update_eip
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -815,7 +812,6 @@ class AllocTest(common.HeatTestCase):
server = self.fc.servers.list()[0]
self._mock_server_get(mock_server=server, multiple=True)
self.mock_create_floatingip()
self.mock_show_floatingip('fc68ea2c-b60b-4b4f-bd82-94ec81110766')
self.mock_list_instance_ports('WebServer')
self.mock_show_network()
@ -838,7 +834,7 @@ class AllocTest(common.HeatTestCase):
props['AllocationId'] = update_allocationId
props.pop('EIP')
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -850,7 +846,7 @@ class AllocTest(common.HeatTestCase):
props['EIP'] = update_eip
props.pop('AllocationId')
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -944,7 +940,7 @@ class AllocTest(common.HeatTestCase):
props['NetworkInterfaceId'] = update_networkInterfaceId
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -956,7 +952,7 @@ class AllocTest(common.HeatTestCase):
props['InstanceId'] = instance_id
update_snippet = rsrc_defn.ResourceDefinition(ass.name, ass.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(ass.update, update_snippet)()
self.assertEqual((ass.UPDATE, ass.COMPLETE), ass.state)
@ -992,5 +988,5 @@ class AllocTest(common.HeatTestCase):
'reference_id': 'convg_xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['IPAssoc']
rsrc = stack.defn['IPAssoc']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -169,7 +169,7 @@ class LoadBalancerTest(common.HeatTestCase):
self.stack = utils.parse_stack(template, cache_data=cache_data)
resource_name = 'LoadBalancer'
lb_defn = self.stack.t.resource_definitions(self.stack)[resource_name]
lb_defn = self.stack.defn.resource_definition(resource_name)
return lb.LoadBalancer(resource_name, lb_defn, self.stack)
def test_loadbalancer_refid(self):
@ -185,7 +185,8 @@ class LoadBalancerTest(common.HeatTestCase):
'reference_id': 'LoadBalancer_convg_mock'
})}
rsrc = self.setup_loadbalancer(cache_data=cache_data)
self.assertEqual('LoadBalancer_convg_mock', rsrc.FnGetRefId())
self.assertEqual('LoadBalancer_convg_mock',
self.stack.defn[rsrc.name].FnGetRefId())
def test_loadbalancer_attr_dnsname(self):
rsrc = self.setup_loadbalancer()

@ -292,7 +292,7 @@ class UserTest(common.HeatTestCase):
'reference_id': 'convg_xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['CfnUser']
rsrc = stack.defn['CfnUser']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -282,7 +282,7 @@ class WaitConditionTest(common.HeatTestCase):
'reference_id': 'http://convg_signed_url'
})})
rsrc = stack['WaitHandle']
rsrc = stack.defn['WaitHandle']
self.assertEqual('http://convg_signed_url', rsrc.FnGetRefId())
def test_validate_handle_url_bad_stackid(self):

@ -73,7 +73,7 @@ class BaseVolumeTest(common.HeatTestCase):
data['Properties']['AvailabilityZone'] = 'nova'
Volume = aws_vol.Volume
vol = Volume(resource_name,
stack.t.resource_definitions(stack)[resource_name],
stack.defn.resource_definition(resource_name),
stack)
return vol
@ -94,9 +94,8 @@ class BaseVolumeTest(common.HeatTestCase):
Attachment = os_vol.CinderVolumeAttachment
else:
Attachment = aws_vol.VolumeAttachment
resource_defns = stack.t.resource_definitions(stack)
rsrc = Attachment(resource_name,
resource_defns[resource_name],
stack.defn.resource_definition(resource_name),
stack)
self.assertIsNone(rsrc.validate())
scheduler.TaskRunner(rsrc.create)()

@ -25,7 +25,7 @@ from heat.engine import resource
from heat.engine.resources.openstack.heat import instance_group as instgrp
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.engine import stack as parser
from heat.engine import stk_defn
from heat.tests.autoscaling import inline_templates
from heat.tests import common
from heat.tests import utils
@ -320,7 +320,7 @@ class LoadbalancerReloadTest(common.HeatTestCase):
properties = t['Resources']['ElasticLoadBalancer']['Properties']
properties['AvailabilityZones'] = {'Fn::GetAZs': ''}
self.patchobject(parser.Stack, 'get_availability_zones',
self.patchobject(stk_defn.StackDefinition, 'get_availability_zones',
return_value=['abc', 'xyz'])
mock_members = self.patchobject(grouputils, 'get_member_refids')

@ -171,7 +171,7 @@ Resources:
'reference_id': 'xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['secret1']
rsrc = stack.defn['secret1']
self.assertEqual('xyz', rsrc.FnGetRefId())
def test_invalid_length(self):

@ -677,7 +677,7 @@ class RemoteStackTest(tests_common.HeatTestCase):
'reference_id': 'convg_xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['remote_stack']
rsrc = stack.defn['remote_stack']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())
def test_update_in_check_failed_state(self):

@ -228,5 +228,5 @@ class ResourceChainTest(common.HeatTestCase):
'attrs': {'refs': ['rsrc1', 'rsrc2']}
})}
stack = utils.parse_stack(TEMPLATE, cache_data=cache_data)
rsrc = stack['test-chain']
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt(rsrc.REFS))
rsrc = stack.defn['test-chain']
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt('refs'))

@ -884,8 +884,8 @@ class ResourceGroupAttrTest(common.HeatTestCase):
'attrs': {'refs': ['rsrc1', 'rsrc2']}
})}
stack = utils.parse_stack(template, cache_data=cache_data)
rsrc = stack['group1']
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt(rsrc.REFS))
rsrc = stack.defn['group1']
self.assertEqual(['rsrc1', 'rsrc2'], rsrc.FnGetAtt('refs'))
def test_get_attribute_blacklist(self):
resg = self._create_dummy_stack()

@ -1104,7 +1104,8 @@ class SoftwareDeploymentTest(common.HeatTestCase):
'attrs': {'foo': 'bar'}
})}
self._create_stack(self.template, cache_data=cache_data)
self.assertEqual('bar', self.deployment.FnGetAtt('foo'))
self.assertEqual('bar',
self.stack.defn[self.deployment.name].FnGetAtt('foo'))
def test_fn_get_att_error(self):
self._create_stack(self.template)

@ -291,7 +291,7 @@ class SwiftSignalHandleTest(common.HeatTestCase):
})
}
st = create_stack(swiftsignalhandle_template, cache_data=cache_data)
rsrc = st['test_wait_condition_handle']
rsrc = st.defn['test_wait_condition_handle']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -821,7 +821,7 @@ class TestMistralWorkflow(common.HeatTestCase):
return [FakeWorkflow('create_vm')]
def test_mistal_workflow_refid(self):
def test_mistral_workflow_refid(self):
tmpl = template_format.parse(workflow_template)
stack = utils.parse_stack(tmpl, stack_name='test')
rsrc = stack['workflow']
@ -830,7 +830,7 @@ class TestMistralWorkflow(common.HeatTestCase):
rsrc.action = 'CREATE'
self.assertEqual('test-workflow-owevpzgiqw66', rsrc.FnGetRefId())
def test_mistal_workflow_refid_convergence_cache_data(self):
def test_mistral_workflow_refid_convergence_cache_data(self):
tmpl = template_format.parse(workflow_template)
cache_data = {'workflow': node_data.NodeData.from_dict({
'uuid': mock.ANY,
@ -841,7 +841,7 @@ class TestMistralWorkflow(common.HeatTestCase):
})}
stack = utils.parse_stack(tmpl, stack_name='test',
cache_data=cache_data)
rsrc = stack['workflow']
rsrc = stack.defn['workflow']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())
def test_policies_translation_successful(self):

@ -296,7 +296,7 @@ class NeutronFloatingIPTest(common.HeatTestCase):
'status': 'COMPLETE',
'reference_id': 'abc'})})
rsrc = stack['floating_ip']
rsrc = stack.defn['floating_ip']
self.assertEqual('abc', rsrc.FnGetRefId())
def test_floatip_association_port(self):
@ -449,7 +449,7 @@ class NeutronFloatingIPTest(common.HeatTestCase):
update_port_id = '2146dfbf-ba77-4083-8e86-d052f671ece5'
props['port_id'] = update_port_id
update_snippet = rsrc_defn.ResourceDefinition(fipa.name, fipa.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(fipa.update, update_snippet)()
@ -708,7 +708,7 @@ class NeutronFloatingIPTest(common.HeatTestCase):
update_port_id = '2146dfbf-ba77-4083-8e86-d052f671ece5'
props['port_id'] = update_port_id
update_snippet = rsrc_defn.ResourceDefinition(fip.name, fip.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(fip.update, update_snippet)()
self.assertEqual((fip.UPDATE, fip.COMPLETE), fip.state)
@ -718,7 +718,7 @@ class NeutronFloatingIPTest(common.HeatTestCase):
props = copy.deepcopy(fip.properties.data)
del(props['port_id'])
update_snippet = rsrc_defn.ResourceDefinition(fip.name, fip.type(),
stack.t.parse(stack,
stack.t.parse(stack.defn,
props))
scheduler.TaskRunner(fip.update, update_snippet)()
self.assertEqual((fip.UPDATE, fip.COMPLETE), fip.state)

@ -22,10 +22,12 @@ from heat.common import template_format
from heat.engine.clients.os import neutron
from heat.engine.clients.os import openstacksdk
from heat.engine.hot import functions as hot_funcs
from heat.engine import node_data
from heat.engine import resource
from heat.engine.resources.openstack.neutron import subnet
from heat.engine import rsrc_defn
from heat.engine import scheduler
from heat.engine import stk_defn
from heat.tests import common
from heat.tests import utils
@ -682,9 +684,10 @@ class NeutronSubnetTest(common.HeatTestCase):
"""
t = template_format.parse(template)
stack = utils.parse_stack(t)
self.patchobject(stack['net'], 'FnGetRefId',
return_value='fc68ea2c-b60b-4b4f-bd82-94ec81110766')
rsrc = stack['subnet']
nd = {'reference_id': 'fc68ea2c-b60b-4b4f-bd82-94ec81110766'}
stk_defn.update_resource_data(stack.defn, 'net',
node_data.NodeData.from_dict(nd))
self.create_mock.return_value = {
"subnet": {
"id": "91e47a57-7508-46fe-afc9-fc454e8580e1",
@ -696,6 +699,7 @@ class NeutronSubnetTest(common.HeatTestCase):
}
stack.create()
self.assertEqual(hot_funcs.GetResource(stack, 'get_resource', 'net'),
self.assertEqual(hot_funcs.GetResource(stack.defn, 'get_resource',
'net'),
rsrc.properties.get('network'))
self.assertIsNone(rsrc.properties.get('network_id'))

@ -369,7 +369,7 @@ class NeutronTrunkTest(common.HeatTestCase):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.t.resource_definitions(stack)['trunk']
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
@ -390,7 +390,7 @@ class NeutronTrunkTest(common.HeatTestCase):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.t.resource_definitions(stack)['trunk']
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
@ -411,7 +411,7 @@ class NeutronTrunkTest(common.HeatTestCase):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.t.resource_definitions(stack)['trunk']
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
@ -440,7 +440,7 @@ class NeutronTrunkTest(common.HeatTestCase):
t = template_format.parse(update_template)
stack = utils.parse_stack(t)
rsrc_defn = stack.t.resource_definitions(stack)['trunk']
rsrc_defn = stack.defn.resource_definition('trunk')
rsrc = trunk.Trunk('trunk', rsrc_defn, stack)
scheduler.TaskRunner(rsrc.create)()
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)

@ -357,5 +357,5 @@ class NovaFloatingIPTest(common.HeatTestCase):
'reference_id': 'convg_xyz'
})}
stack = utils.parse_stack(t, cache_data=cache_data)
rsrc = stack['MyFloatingIPAssociation']
rsrc = stack.defn['MyFloatingIPAssociation']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -286,5 +286,5 @@ class NovaKeyPairTest(common.HeatTestCase):
'reference_id': 'convg_xyz'
}}
stack = utils.parse_stack(self.kp_template, cache_data=cache_data)
rsrc = stack['kp']
rsrc = stack.defn['kp']
self.assertEqual('convg_xyz', rsrc.FnGetRefId())

@ -472,7 +472,7 @@ class SwiftTest(common.HeatTestCase):
'reference_id': 'xyz_convg'
})}
stack = utils.parse_stack(self.t, cache_data=cache_data)
rsrc = stack['SwiftContainer']
rsrc = stack.defn['SwiftContainer']
self.assertEqual('xyz_convg', rsrc.FnGetRefId())
@mock.patch('swiftclient.client.Connection.head_account')

@ -24,6 +24,7 @@ from heat.engine import function
from heat.engine import resource
from heat.engine import rsrc_defn
from heat.engine import stack
from heat.engine import stk_defn
from heat.engine import template
from heat.tests import common
from heat.tests import utils
@ -241,25 +242,38 @@ class ValidateGetAttTest(common.HeatTestCase):
env.load({u'resource_registry':
{u'OS::Test::FakeResource': u'OverwrittenFnGetAttType'}})
tmpl = template.Template({"HeatTemplateFormatVersion": "2012-12-12",
"Resources": {
"test_rsrc": {
"Type": "OS::Test::GenericResource"
},
"get_att_rsrc": {
"Type": "OS::Heat::Value",
"Properties": {
"value": {
"Fn::GetAtt": ["test_rsrc",
"Foo"]
}
}
}
}},
env=env)
self.stack = stack.Stack(
utils.dummy_context(), 'test_stack',
template.Template({"HeatTemplateFormatVersion": "2012-12-12"},
env=env),
tmpl,
stack_id=str(uuid.uuid4()))
res_defn = rsrc_defn.ResourceDefinition('test_rsrc',
'OS::Test::GenericResource')
self.rsrc = resource.Resource('test_rsrc', res_defn, self.stack)
self.stack.add_resource(self.rsrc)
self.rsrc = self.stack['test_rsrc']
self.stack.validate()
def test_resource_is_appear_in_stack(self):
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
func = functions.GetAtt(self.stack.defn, 'Fn::GetAtt',
[self.rsrc.name, 'Foo'])
self.assertIsNone(func.validate())
def test_resource_is_not_appear_in_stack(self):
self.stack.remove_resource(self.rsrc.name)
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
func = functions.GetAtt(self.stack.defn, 'Fn::GetAtt',
[self.rsrc.name, 'Foo'])
ex = self.assertRaises(exception.InvalidTemplateReference,
func.validate)
@ -267,7 +281,15 @@ class ValidateGetAttTest(common.HeatTestCase):
'is incorrect.', six.text_type(ex))
def test_resource_no_attribute_with_default_fn_get_att(self):
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
res_defn = rsrc_defn.ResourceDefinition('test_rsrc',
'ResWithStringPropAndAttr')
self.rsrc = resource.Resource('test_rsrc', res_defn, self.stack)
self.stack.add_resource(self.rsrc)
stk_defn.update_resource_data(self.stack.defn, self.rsrc.name,
self.rsrc.node_data())
self.stack.validate()
func = functions.GetAtt(self.stack.defn, 'Fn::GetAtt',
[self.rsrc.name, 'Bar'])
ex = self.assertRaises(exception.InvalidTemplateAttribute,
func.validate)
@ -278,16 +300,19 @@ class ValidateGetAttTest(common.HeatTestCase):
res_defn = rsrc_defn.ResourceDefinition('test_rsrc',
'OS::Test::FakeResource')
self.rsrc = resource.Resource('test_rsrc', res_defn, self.stack)
self.stack.add_resource(self.rsrc)
self.rsrc.attributes_schema = {}
self.stack.add_resource(self.rsrc)
stk_defn.update_resource_data(self.stack.defn, self.rsrc.name,
self.rsrc.node_data())
self.stack.validate()
func = functions.GetAtt(self.stack, 'Fn::GetAtt',
func = functions.GetAtt(self.stack.defn, 'Fn::GetAtt',
[self.rsrc.name, 'Foo'])
self.assertIsNone(func.validate())
def test_get_attr_without_attribute_name(self):
ex = self.assertRaises(ValueError, functions.GetAtt,
self.stack, 'Fn::GetAtt', [self.rsrc.name])
self.stack.defn, 'Fn::GetAtt', [self.rsrc.name])
self.assertEqual('Arguments to "Fn::GetAtt" must be '
'of the form [resource_name, attribute]',
six.text_type(ex))

@ -30,6 +30,7 @@ from heat.engine import resource
from heat.engine import resources
from heat.engine import rsrc_defn
from heat.engine import stack as parser
from heat.engine import stk_defn
from heat.engine import template
from heat.tests import common
from heat.tests import generic_resource as generic_rsrc
@ -162,11 +163,12 @@ class HOTemplateTest(common.HeatTestCase):
@staticmethod
def resolve(snippet, template, stack=None):
return function.resolve(template.parse(stack, snippet))
return function.resolve(template.parse(stack and stack.defn, snippet))
@staticmethod
def resolve_condition(snippet, template, stack=None):
return function.resolve(template.parse_condition(stack, snippet))
return function.resolve(template.parse_condition(stack and stack.defn,
snippet))
def test_defaults(self):
"""Test default content behavior of HOT template."""
@ -1913,7 +1915,7 @@ conditions:
self.assertRaisesRegex(exception.StackValidationFailed,
regxp,
function.validate,
stack.t.parse(stack, snippet))
stack.t.parse(stack.defn, snippet))
def test_add_resource(self):
hot_tpl = template_format.parse('''
@ -2349,7 +2351,7 @@ class HotStackTest(common.HeatTestCase):
self.ctx = utils.dummy_context()
def resolve(self, snippet):
return function.resolve(self.stack.t.parse(self.stack, snippet))
return function.resolve(self.stack.t.parse(self.stack.defn, snippet))
def test_repeat_get_attr(self):
"""Test repeat function with get_attr function as an argument."""
@ -2358,7 +2360,7 @@ class HotStackTest(common.HeatTestCase):
snippet = {'repeat': {'template': 'this is %var%',
'for_each': {'%var%': {'get_attr': ['resource1', 'list']}}}}
repeat = self.stack.t.parse(self.stack, snippet)
repeat = self.stack.t.parse(self.stack.defn, snippet)
self.stack.store()
with mock.patch.object(rsrc_defn.ResourceDefinition,
@ -2724,7 +2726,8 @@ class StackAttributesTest(common.HeatTestCase):
template.Template(self.hot_tpl))
self.stack.store()
parsed = self.stack.t.parse(self.stack, self.snippet)
parsed = self.stack.t.parse(self.stack.defn, self.snippet)
dep_attrs = list(function.dep_attrs(parsed, self.resource_name))
self.stack.create()
self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
@ -2748,175 +2751,14 @@ class StackAttributesTest(common.HeatTestCase):
(rsrc.ADOPT, rsrc.COMPLETE)):
rsrc.state_set(action, status)
self.assertEqual(self.expected, function.resolve(parsed))
class StackGetAttributesTestConvergence(common.HeatTestCase):
def setUp(self):
super(StackGetAttributesTestConvergence, self).setUp()
self.ctx = utils.dummy_context()
self.m.ReplayAll()
with mock.patch.object(rsrc_defn.ResourceDefinition,
'dep_attrs') as mock_da:
mock_da.return_value = dep_attrs
node_data = rsrc.node_data()
stk_defn.update_resource_data(self.stack.defn, rsrc.name,
node_data)
scenarios = [
# for hot template 2013-05-23, get_attr: hot_funcs.GetAttThenSelect
('get_flat_attr',
dict(hot_tpl=hot_tpl_generic_resource,
snippet={'Value': {'get_attr': ['resource1', 'foo']}},
resource_name='resource1',
expected={'Value': 'resource1'})),
('get_list_attr',
dict(hot_tpl=hot_tpl_complex_attrs,
snippet={'Value': {'get_attr': ['resource1', 'list', 0]}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.list[0]})),
('get_flat_dict_attr',
dict(hot_tpl=hot_tpl_complex_attrs,
snippet={'Value': {'get_attr': ['resource1',
'flat_dict',
'key2']}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
flat_dict['key2']})),
('get_nested_attr_list',
dict(hot_tpl=hot_tpl_complex_attrs,
snippet={'Value': {'get_attr': ['resource1',
'nested_dict',
'list',
0]}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
nested_dict['list'][0]})),
('get_nested_attr_dict',
dict(hot_tpl=hot_tpl_complex_attrs,
snippet={'Value': {'get_attr': ['resource1',
'nested_dict',
'dict',
'a']}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
nested_dict['dict']['a']})),
('get_attr_none',
dict(hot_tpl=hot_tpl_complex_attrs,
snippet={'Value': {'get_attr': ['resource1',
'none',
'who_cares']}},
resource_name='resource1',
expected={'Value': None})),
# for hot template version 2014-10-16 and 2015-04-30,
# get_attr: hot_funcs.GetAtt
('get_flat_attr',
dict(hot_tpl=hot_tpl_generic_resource_20141016,
snippet={'Value': {'get_attr': ['resource1', 'foo']}},
resource_name='resource1',
expected={'Value': 'resource1'})),
('get_list_attr',
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
snippet={'Value': {'get_attr': ['resource1', 'list', 0]}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.list[0]})),
('get_flat_dict_attr',
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
snippet={'Value': {'get_attr': ['resource1',
'flat_dict',
'key2']}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
flat_dict['key2']})),
('get_nested_attr_list',
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
snippet={'Value': {'get_attr': ['resource1',
'nested_dict',
'list',
0]}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
nested_dict['list'][0]})),
('get_nested_attr_dict',
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
snippet={'Value': {'get_attr': ['resource1',
'nested_dict',
'dict',
'a']}},
resource_name='resource1',
expected={
'Value':
generic_rsrc.ResourceWithComplexAttributes.
nested_dict['dict']['a']})),
('get_attr_none',
dict(hot_tpl=hot_tpl_complex_attrs_20141016,
snippet={'Value': {'get_attr': ['resource1',
'none',
'who_cares']}},
resource_name='resource1',
expected={'Value': None}))
]
def _prepare_cache_data(self, rsrc):
dep_attrs = function.dep_attrs(
self.stack.t.parse(self.stack, self.snippet),
self.resource_name)
with mock.patch.object(rsrc_defn.ResourceDefinition,
'dep_attrs') as mock_da:
mock_da.return_value = dep_attrs
rsrc_data = rsrc.node_data()
# store as cache data
self.stack.cache_data = {
rsrc.name: rsrc_data
}
def test_get_attr_convergence(self):
"""Test resolution of get_attr occurrences with convergence."""
self.stack = parser.Stack(self.ctx, 'test_get_attr',
template.Template(self.hot_tpl))
self.stack.store()
self.stack.create()
self.assertEqual((parser.Stack.CREATE, parser.Stack.COMPLETE),
self.stack.state)
rsrc = self.stack[self.resource_name]
self._prepare_cache_data(rsrc)
with mock.patch.object(resource.Resource, 'get_attribute') as mock_ga:
for action, status in (
(rsrc.CREATE, rsrc.IN_PROGRESS),
(rsrc.CREATE, rsrc.COMPLETE),
(rsrc.RESUME, rsrc.IN_PROGRESS),
(rsrc.RESUME, rsrc.COMPLETE),
(rsrc.SUSPEND, rsrc.IN_PROGRESS),
(rsrc.SUSPEND, rsrc.COMPLETE),
(rsrc.UPDATE, rsrc.IN_PROGRESS),
(rsrc.UPDATE, rsrc.COMPLETE),
(rsrc.SNAPSHOT, rsrc.IN_PROGRESS),
(rsrc.SNAPSHOT, rsrc.COMPLETE),
(rsrc.CHECK, rsrc.IN_PROGRESS),
(rsrc.CHECK, rsrc.COMPLETE),
(rsrc.ADOPT, rsrc.IN_PROGRESS),
(rsrc.ADOPT, rsrc.COMPLETE)):
rsrc.state_set(action, status)
resolved = function.resolve(self.stack.t.parse(self.stack,
self.snippet))
self.assertEqual(self.expected, resolved)
# get_attribute should never be called, everything
# should be resolved from cache data
self.assertFalse(mock_ga.called)