Source code for heat.engine.stk_defn

#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import itertools

from heat.common import exception
from heat.engine import attributes
from heat.engine import status


[docs]class StackDefinition(object): """Class representing the definition of a Stack, but not its current state. This is the interface through which template functions will access data about the stack definition, including the template and current values of resource reference IDs and attributes. This API can be considered stable by third-party Template or Function plugins, and no part of it should be changed or removed without an appropriate deprecation process. """ def __init__(self, context, template, stack_identifier, resource_data, parent_info=None): self._context = context self._template = template self._resource_data = {} if resource_data is None else resource_data self._parent_info = parent_info self._zones = None self.parameters = template.parameters(stack_identifier, template.env.params, template.env.param_defaults) self._resource_defns = None self._resources = {} self._output_defns = None
[docs] def clone_with_new_template(self, new_template, stack_identifier, clear_resource_data=False): """Create a new StackDefinition with a different template.""" res_data = {} if clear_resource_data else dict(self._resource_data) return type(self)(self._context, new_template, stack_identifier, res_data, self._parent_info)
@property def t(self): """The stack's template.""" return self._template @property def env(self): """The stack's environment.""" return self._template.env def _load_rsrc_defns(self): self._resource_defns = self._template.resource_definitions(self)
[docs] def resource_definition(self, resource_name): """Return the definition of the given resource.""" if self._resource_defns is None: self._load_rsrc_defns() return self._resource_defns[resource_name]
[docs] def enabled_rsrc_names(self): """Return the set of names of all enabled resources in the template.""" if self._resource_defns is None: self._load_rsrc_defns() return set(self._resource_defns)
def _load_output_defns(self): self._output_defns = self._template.outputs(self)
[docs] def output_definition(self, output_name): """Return the definition of the given output.""" if self._output_defns is None: self._load_output_defns() return self._output_defns[output_name]
[docs] def enabled_output_names(self): """Return the set of names of all enabled outputs in the template.""" if self._output_defns is None: self._load_output_defns() return set(self._output_defns)
[docs] def all_rsrc_names(self): """Return the set of names of all resources in the template. This includes resources that are disabled due to false conditionals. """ if hasattr(self._template, 'RESOURCES'): return set(self._template.get(self._template.RESOURCES, self._resource_defns or [])) else: return self.enabled_rsrc_names()
[docs] def all_resource_types(self): """Return the set of types of all resources in the template.""" if self._resource_defns is None: self._load_rsrc_defns() return set(self._resource_defns[res].resource_type for res in self._resource_defns)
[docs] def get_availability_zones(self): """Return the list of Nova availability zones.""" if self._zones is None: nova = self._context.clients.client('nova') zones = nova.availability_zones.list(detailed=False) self._zones = [zone.zoneName for zone in zones] return self._zones
def __contains__(self, resource_name): """Return True if the given resource name is present and enabled.""" if self._resource_defns is not None: return resource_name in self._resource_defns else: # In Cfn templates, we need to know whether Ref refers to a # resource or a parameter in order to parse the resource # definitions return resource_name in self._template[self._template.RESOURCES] def __getitem__(self, resource_name): """Return a proxy for the given resource.""" if resource_name not in self._resources: res_proxy = ResourceProxy(resource_name, self.resource_definition(resource_name), self._resource_data.get(resource_name)) self._resources[resource_name] = res_proxy return self._resources[resource_name] @property def parent_resource(self): """Return a proxy for the parent resource. Returns None if the stack is not a provider stack for a TemplateResource. """ return self._parent_info
[docs]class ResourceProxy(status.ResourceStatus): """A lightweight API for essential data about a resource. This is the interface through which template functions will access data about particular resources in the stack definition, such as the resource definition and current values of reference IDs and attributes. Resource proxies for some or all resources in the stack will potentially be loaded for every check resource operation, so it is essential that this API is implemented efficiently, using only the data received over RPC and without reference to the resource data stored in the database. This API can be considered stable by third-party Template or Function plugins, and no part of it should be changed or removed without an appropriate deprecation process. """ __slots__ = ('name', '_definition', '_resource_data') def __init__(self, name, definition, resource_data): self.name = name self._definition = definition self._resource_data = resource_data @property def t(self): """The resource definition.""" return self._definition def _res_data(self): assert self._resource_data is not None, "Resource data not available" return self._resource_data @property def attributes_schema(self): """A set of the valid top-level attribute names. This is provided for backwards-compatibility for functions that require a container with all of the valid attribute names in order to validate the template. Other operations on it are invalid because we don't actually have access to the attributes schema here; hence we return a set instead of a dict. """ return set(self._res_data().attribute_names()) @property def external_id(self): """The external ID of the resource.""" return self._definition.external_id() @property def state(self): """The current state (action, status) of the resource.""" return self.action, self.status @property def action(self): """The current action of the resource.""" if self._resource_data is None: return self.INIT return self._resource_data.action @property def status(self): """The current status of the resource.""" if self._resource_data is None: return self.COMPLETE return self._resource_data.status
[docs] def FnGetRefId(self): """For the intrinsic function get_resource.""" if self._resource_data is None: return self.name return self._resource_data.reference_id()
[docs] def FnGetAtt(self, attr, *path): """For the intrinsic function get_attr.""" if path: attr = (attr,) + path try: return self._res_data().attribute(attr) except KeyError: raise exception.InvalidTemplateAttribute(resource=self.name, key=attr)
[docs] def FnGetAtts(self): """For the intrinsic function get_attr when getting all attributes. :returns: a dict of all of the resource's attribute values, excluding the "show" attribute. """ all_attrs = self._res_data().attributes() return dict((k, v) for k, v in all_attrs.items() if k != attributes.SHOW_ATTR)
[docs]def update_resource_data(stack_definition, resource_name, resource_data): """Store new resource state data for the specified resource. This function enables the legacy (non-convergence) path to store updated NodeData as resources are created/updated in a single StackDefinition that lasts for the entire lifetime of the stack operation. """ stack_definition._resource_data[resource_name] = resource_data stack_definition._resources.pop(resource_name, None) # Clear the cached dep_attrs for any resource or output that directly # depends on the resource whose data we are updating. This ensures that if # any of the data we just updated is referenced in the path of a get_attr # function, future calls to dep_attrs() will reflect this new data. res_defns = stack_definition._resource_defns or {} op_defns = stack_definition._output_defns or {} all_defns = itertools.chain(res_defns.values(), op_defns.values()) for defn in all_defns: if resource_name in defn.required_resource_names(): defn._all_dep_attrs = None
[docs]def add_resource(stack_definition, resource_definition): """Insert the given resource definition into the stack definition. Add the resource to the template and store any temporary data. """ resource_name = resource_definition.name stack_definition._resources.pop(resource_name, None) stack_definition._resource_data.pop(resource_name, None) stack_definition.t.add_resource(resource_definition) if stack_definition._resource_defns is not None: stack_definition._resource_defns[resource_name] = resource_definition
[docs]def remove_resource(stack_definition, resource_name): """Remove the named resource from the stack definition. Remove the resource from the template and eliminate references to it. """ stack_definition.t.remove_resource(resource_name) if stack_definition._resource_defns is not None: stack_definition._resource_defns.pop(resource_name, None) stack_definition._resource_data.pop(resource_name, None) stack_definition._resources.pop(resource_name, None)