Allow Resources to select paths from attributes

Previously when get_attr included a path to select from the attribute, the
selection was done by the get_attr intrinsic function. Starting with the
Juno version of HOT, move control of this to the resource plugin, so that
individual resources (particularly ResourceGroup) can handle it themselves.

Related-Bug: #1341048
Change-Id: Ic3dc94a99a19e28a4f775198ff6e960e2efb8256
This commit is contained in:
Zane Bitter 2014-07-11 15:17:59 -04:00
parent 3d89c8b55b
commit eca6faa83e
5 changed files with 56 additions and 22 deletions

View File

@ -83,7 +83,7 @@ class GetParam(function.Function):
return ''
class GetAtt(cfn_funcs.GetAtt):
class GetAttThenSelect(cfn_funcs.GetAtt):
'''
A function for resolving resource attributes.
@ -112,7 +112,7 @@ class GetAtt(cfn_funcs.GetAtt):
return tuple(self.args[:2])
def result(self):
attribute = super(GetAtt, self).result()
attribute = super(GetAttThenSelect, self).result()
if attribute is None:
return None
@ -120,6 +120,32 @@ class GetAtt(cfn_funcs.GetAtt):
return attributes.select_from_attribute(attribute, path_components)
class GetAtt(GetAttThenSelect):
'''
A function for resolving resource attributes.
Takes the form::
get_attr:
- <resource_name>
- <attribute_name>
- <path1>
- ...
'''
def result(self):
path_components = function.resolve(self._path_components)
attribute = function.resolve(self._attribute)
r = self._resource()
if (r.status in (r.IN_PROGRESS, r.COMPLETE) and
r.action in (r.CREATE, r.ADOPT, r.SUSPEND, r.RESUME,
r.UPDATE)):
return r.FnGetAtt(attribute, *path_components)
else:
return None
class Replace(cfn_funcs.Replace):
'''
A function for performing string substitutions.
@ -247,7 +273,7 @@ def function_mapping(version_key, version):
'get_param': GetParam,
'get_resource': cfn_funcs.ResourceRef,
'Ref': cfn_funcs.Ref,
'get_attr': GetAtt,
'get_attr': GetAttThenSelect,
'Fn::Select': cfn_funcs.Select,
'Fn::Join': cfn_funcs.Join,
'Fn::Split': cfn_funcs.Split,

View File

@ -21,7 +21,7 @@ from heat.common import exception
from heat.common import identifier
from heat.common import short_id
from heat.db import api as db_api
from heat.engine.attributes import Attributes
from heat.engine import attributes
from heat.engine import environment
from heat.engine import event
from heat.engine import function
@ -146,9 +146,9 @@ class Resource(object):
self.name = name
self.t = definition
self.reparse()
self.attributes = Attributes(self.name,
self.attributes_schema,
self._resolve_attribute)
self.attributes = attributes.Attributes(self.name,
self.attributes_schema,
self._resolve_attribute)
self.abandon_in_progress = False
@ -827,18 +827,21 @@ class Resource(object):
else:
return unicode(self.name)
def FnGetAtt(self, key):
def FnGetAtt(self, key, *path):
'''
For the intrinsic function Fn::GetAtt.
:param key: the attribute key.
:param path: a list of path components to select from the attribute.
:returns: the attribute value.
'''
try:
return self.attributes[key]
attribute = self.attributes[key]
except KeyError:
raise exception.InvalidTemplateAttribute(resource=self.name,
key=key)
else:
return attributes.select_from_attribute(attribute, path)
def FnBase64(self, data):
'''
@ -927,7 +930,7 @@ class Resource(object):
'Properties': properties
}
},
'Outputs': Attributes.as_outputs(resource_name, cls)
'Outputs': attributes.Attributes.as_outputs(resource_name, cls)
}
def data(self):

View File

@ -125,27 +125,28 @@ class ResourceGroup(stack_resource.StackResource):
def handle_delete(self):
return self.delete_nested()
def FnGetAtt(self, key):
def FnGetAtt(self, key, *path):
nested_stack = self.nested()
def get_rsrc_attr(resource_name, attr_name=None):
def get_rsrc_attr(resource_name, *attr_path):
try:
resource = nested_stack[resource_name]
except KeyError:
raise exception.InvalidTemplateAttribute(resource=self.name,
key=key)
if attr_name is None:
if not attr_path:
return resource.FnGetRefId()
else:
return resource.FnGetAtt(attr_name)
return resource.FnGetAtt(*attr_path)
if key.startswith("resource."):
parts = key.split(".", 2)
return get_rsrc_attr(*parts[1:])
path = key.split(".", 2)[1:] + list(path)
return get_rsrc_attr(*path)
else:
count = self.properties[self.COUNT]
attr = None if key == self.REFS else key
return [get_rsrc_attr(str(n), attr) for n in range(count)]
attr = [] if key == self.REFS else [key]
attribute = [get_rsrc_attr(str(n), *attr) for n in range(count)]
return attributes.select_from_attribute(attribute, path)
def _assemble_nested(self, count, include_all=False):
child_template = copy.deepcopy(template_template)

View File

@ -462,13 +462,14 @@ class SoftwareDeployment(signal_responder.SignalResponder):
status_reason = _('Outputs received')
sd.update(output_values=ov, status=status, status_reason=status_reason)
def FnGetAtt(self, key):
def FnGetAtt(self, key, *path):
'''
Resource attributes map to deployment outputs values
'''
sd = self.heat().software_deployments.get(self.resource_id)
if key in sd.output_values:
return sd.output_values.get(key)
attribute = sd.output_values.get(key)
return attributes.select_from_attribute(attribute, path)
# Since there is no value for this key yet, check the output schemas
# to find out if the key is valid
@ -477,6 +478,7 @@ class SoftwareDeployment(signal_responder.SignalResponder):
if key not in output_keys and key not in self.ATTRIBUTES:
raise exception.InvalidTemplateAttribute(resource=self.name,
key=key)
return None
def validate(self):
'''

View File

@ -16,6 +16,7 @@ from requests import exceptions
from heat.common import exception
from heat.common import template_format
from heat.common import urlfetch
from heat.engine import attributes
from heat.engine import properties
from heat.engine import stack_resource
@ -82,11 +83,12 @@ class NestedStack(stack_resource.StackResource):
def handle_delete(self):
return self.delete_nested()
def FnGetAtt(self, key):
def FnGetAtt(self, key, *path):
if key and not key.startswith('Outputs.'):
raise exception.InvalidTemplateAttribute(resource=self.name,
key=key)
return self.get_output(key.partition('.')[-1])
attribute = self.get_output(key.partition('.')[-1])
return attributes.select_from_attribute(attribute, path)
def FnGetRefId(self):
return self.nested().identifier().arn()