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:
parent
3d89c8b55b
commit
eca6faa83e
|
@ -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,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
'''
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue