Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-139-attributes d9cc24dce -> 6f349dd72
Progress on new function mechanism Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/6f349dd7 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/6f349dd7 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/6f349dd7 Branch: refs/heads/ARIA-139-attributes Commit: 6f349dd723c82b9a889edbe3bb5a7e9d0b137c62 Parents: d9cc24d Author: Tal Liron <[email protected]> Authored: Wed Apr 26 21:16:55 2017 -0500 Committer: Tal Liron <[email protected]> Committed: Wed Apr 26 21:16:55 2017 -0500 ---------------------------------------------------------------------- aria/core.py | 6 +- aria/modeling/functions.py | 99 ++++++++++- aria/modeling/mixins.py | 2 +- aria/modeling/service_common.py | 178 +++++++++++++++++-- aria/modeling/service_instance.py | 72 ++++---- aria/modeling/service_template.py | 80 ++++----- aria/modeling/utils.py | 45 +---- aria/parser/consumption/__init__.py | 9 +- aria/parser/consumption/modeling.py | 4 +- .../simple_v1_0/functions.py | 103 ++++++----- .../simple_v1_0/modeling/__init__.py | 4 +- .../simple_v1_0/modeling/data_types.py | 2 + 12 files changed, 417 insertions(+), 187 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/core.py ---------------------------------------------------------------------- diff --git a/aria/core.py b/aria/core.py index af1984a..cc943ef 100644 --- a/aria/core.py +++ b/aria/core.py @@ -77,10 +77,14 @@ class Core(object): consumption.ConsumerChain( context, ( + consumption.CoerceServiceInstanceValues, + consumption.ValidateServiceInstance, consumption.SatisfyRequirements, + consumption.CoerceServiceInstanceValues, consumption.ValidateCapabilities, consumption.FindHosts, - consumption.ConfigureOperations + consumption.ConfigureOperations, + consumption.CoerceServiceInstanceValues )).consume() if context.validation.dump_issues(): raise exceptions.InstantiationError('Failed to instantiate service template') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/functions.py ---------------------------------------------------------------------- diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py index 02f4454..f0d6c81 100644 --- a/aria/modeling/functions.py +++ b/aria/modeling/functions.py @@ -13,20 +13,113 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ..parser.consumption import ConsumptionContext +from ..parser.exceptions import InvalidValueError +from ..utils.collections import OrderedDict +from . import exceptions + + class Function(object): """ - An intrinsic function. + Base class for intrinsic functions. Serves as a placeholder for a value that should eventually + be derived by "evaluating" (calling) the function. - Serves as a placeholder for a value that should eventually be derived by calling the function. + Note that this base class is provided as a convenience and you do not have to inherit it: any + object with an ``__evaluate__`` method would be treated similarly. """ @property def as_raw(self): raise NotImplementedError - def _evaluate(self, context, container): + def __evaluate__(self, container_holder): + """ + Evaluates the function if possible. If impossible, raises + :class:`CannotEvaluateFunctionException` (do not just return None). + + :rtype: Evaluation (or any object with ``value`` and ``final`` properties) + """ + raise NotImplementedError def __deepcopy__(self, memo): # Circumvent cloning in order to maintain our state return self + + +class Evaluation(object): + """ + A wrapper for an evaluated :class:`Function` value. + """ + + def __init__(self, value, final=False): + self.value = value + self.final = final + + +def evaluate(value, container_holder, report_issues=False): + """ + Recursively attempts to call ``__evaluate__``. If an evaluation occurred will return an + :class:`Evaluation`, otherwise it will be None. If any evaluation is non-final, then the entire + evaluation will also be non-final. + """ + + evaluated = False + final = True + + if hasattr(value, '__evaluate__'): + try: + evaluation = value.__evaluate__(container_holder) + + # Verify evaluation structure + if (evaluation is None) \ + or (not hasattr(evaluation, 'value')) \ + or (not hasattr(evaluation, 'final')): + raise InvalidValueError('bad __evaluate__ implementation') + + evaluated = True + value = evaluation.value + final = evaluation.final + + # The evaluated value might itself be evaluable + evaluation = evaluate(value, container_holder, report_issues) + if evaluation is not None: + value = evaluation.value + if not evaluation.final: + final = False + except exceptions.CannotEvaluateFunctionException: + pass + except InvalidValueError as e: + if report_issues: + context = ConsumptionContext.get_thread_local() + context.validation.report(e.issue) + + elif isinstance(value, list): + evaluated_list = [] + for v in value: + evaluation = evaluate(v, container_holder, report_issues) + if evaluation is not None: + evaluated_list.append(evaluation.value) + evaluated = True + if not evaluation.final: + final = False + else: + evaluated_list.append(v) + if evaluated: + value = evaluated_list + + elif isinstance(value, dict): + evaluated_dict = OrderedDict() + for k, v in value.iteritems(): + evaluation = evaluate(v, container_holder, report_issues) + if evaluation is not None: + evaluated_dict[k] = evaluation.value + evaluated = True + if not evaluation.final: + final = False + else: + evaluated_dict[k] = v + if evaluated: + value = evaluated_dict + + return Evaluation(value, final) if evaluated else None http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/mixins.py ---------------------------------------------------------------------- diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py index e6db5a3..38c812d 100644 --- a/aria/modeling/mixins.py +++ b/aria/modeling/mixins.py @@ -124,7 +124,7 @@ class InstanceModelMixin(ModelMixin): def validate(self): pass - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def dump(self): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index 01458ad..9539979 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -30,7 +30,7 @@ from ..utils import collections, formatting, console from .mixins import InstanceModelMixin, TemplateModelMixin from . import ( relationship, - utils + functions ) @@ -53,10 +53,165 @@ class ParameterBase(TemplateModelMixin): name = Column(Text) type_name = Column(Text) - value = Column(PickleType) + _value = Column(PickleType) description = Column(Text) @property + def value(self): + value = self._value + if value is not None: + evaluation = functions.evaluate(value, self) + if evaluation is not None: + print '>>> EVALUATED' + value = evaluation.value + return value + + @value.setter + def value(self, value): + self._value = value + + @property + def container(self): + """ + The container for this parameter, which would be another model. + + *All* parameters should have a container model. In case this property method fails to find + it, it will raise a ValueError, which should signify an orphaned parameter. + """ + + def get_interface_container(interface): + # Node + if interface.node: + return interface.node + elif interface.relationship: + return interface.relationship.source_node + # Group + elif interface.group: + return interface.group + raise ValueError('interface parameter does not have a container: {0}' + .format(self.name)) + + def get_interface_template_container(interface_template): + # NodeTemplate + if interface_template.node_template: + return interface_template.node_template + elif interface_template.relationship_template: + relationship_template = interface_template.relationship_template + requirement_template = relationship_template.requirement_template + return requirement_template.node_template + # GroupTemplate + elif interface_template.group_template: + return interface_template.group_template + raise ValueError('interface_template parameter does not have a container: {0}' + .format(self.name)) + + # Node + if self.properties_nodes: + return self.properties_nodes[0] + elif self.attributes_nodes: + return self.attributes_nodes[0] + elif self.properties_capabilities: + capability = self.properties_capabilities[0] + return capability.node + elif self.properties_artifacts: + artifact = self.properties_artifacts[0] + return artifact.node + elif self.properties_relationships: + relationship = self.properties_relationships[0] + return relationship.source_node + # Group + elif self.properties_groups: + return self.properties_groups[0] + # Policy + elif self.properties_policies: + return self.properties_policies[0] + # Service + elif self.inputs_services: + return self.inputs_services[0] + elif self.outputs_services: + return self.outputs_services[0] + # Execution + elif self.inputs_executions: + return self.inputs_executions[0] + # Node or Group + elif self.inputs_interfaces: + interface = self.inputs_interfaces[0] + return get_interface_container(interface) + elif self.inputs_operations: + operation = self.inputs_operations[0] + # Node or Group + if operation.interface: + return get_interface_container(operation.interface) + # Service + elif operation.service: + return operation.service + raise ValueError('operation parameter does not have a container: {0}' + .format(self.name)) + + # NodeTemplate + elif self.properties_node_templates: + return self.properties_node_templates[0] + elif self.attributes_node_templates: + return self.attributes_node_templates[0] + elif self.properties_capability_templates: + capability_template = self.properties_capability_templates[0] + return capability_template.node_template + elif self.properties_artifact_templates: + artifact_template = self.properties_artifact_templates[0] + return artifact_template.node_template + elif self.properties_relationship_templates: + relationship_template = self.properties_relationship_templates[0] + requirement_template = relationship_template.requirement_template + return requirement_template.node_template + # GroupTemplate + elif self.properties_group_templates: + return self.properties_group_templates[0] + # PolicyTemplate + elif self.properties_policy_templates: + return self.properties_policy_templates[0] + # ServiceTemplate + elif self.inputs_service_templates: + return self.inputs_service_templates[0] + elif self.outputs_service_templates: + return self.outputs_service_templates[0] + # NodeTemplate or GroupTemplate + elif self.inputs_interface_templates: + interface_template = self.inputs_interface_templates[0] + return get_interface_template_container(interface_template) + elif self.inputs_operation_templates: + operation_template = self.inputs_operation_templates[0] + # NodeTemplate or GroupTemplate + if operation_template.interface_template: + return get_interface_template_container(operation_template.interface_template) + # ServiceTemplate + elif operation_template.service_template: + return operation_template.service_template + raise ValueError('operation_template parameter does not have a container: {0}' + .format(self.name)) + + raise ValueError('parameter does not have a container: {0}'.format(self.name)) + + @property + def service(self): + from . import models + container = self.container + if isinstance(container, models.Service): + return container + elif hasattr(container, 'service'): + return container.service + return None + + @property + def service_template(self): + from . import models + container = self.container + if isinstance(container, models.ServiceTemplate): + return container + elif hasattr(container, 'service_template'): + return container.service_template + return None + + @property def as_raw(self): return collections.OrderedDict(( ('name', self.name), @@ -68,15 +223,16 @@ class ParameterBase(TemplateModelMixin): from . import models return models.Parameter(name=self.name, type_name=self.type_name, - value=self.value, + _value=self._value, description=self.description) - def coerce_values(self, container, report_issues): - if self.value is not None: - final, value = utils.coerce_value(container, self.value, - report_issues) - # TODO: final? - self.value = value + def coerce_values(self, report_issues): + value = self._value + if value is not None: + evaluation = functions.evaluate(value, self, report_issues) + if (evaluation is not None) and evaluation.final: + # A final evaluation can safely replace the existing value + self._value = evaluation.value def dump(self): context = ConsumptionContext.get_thread_local() @@ -212,7 +368,7 @@ class TypeBase(InstanceModelMixin): self._append_raw_children(types) return types - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def dump(self): @@ -261,7 +417,7 @@ class MetadataBase(TemplateModelMixin): ('name', self.name), ('value', self.value))) - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def instantiate(self, container): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index b293535..3ef91d6 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -261,16 +261,16 @@ class ServiceBase(InstanceModelMixin): utils.validate_dict_values(self.outputs) utils.validate_dict_values(self.workflows) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.meta_data, report_issues) - utils.coerce_dict_values(container, self.nodes, report_issues) - utils.coerce_dict_values(container, self.groups, report_issues) - utils.coerce_dict_values(container, self.policies, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.meta_data, report_issues) + utils.coerce_dict_values(self.nodes, report_issues) + utils.coerce_dict_values(self.groups, report_issues) + utils.coerce_dict_values(self.policies, report_issues) if self.substitution is not None: - self.substitution.coerce_values(container, report_issues) - utils.coerce_dict_values(container, self.inputs, report_issues) - utils.coerce_dict_values(container, self.outputs, report_issues) - utils.coerce_dict_values(container, self.workflows, report_issues) + self.substitution.coerce_values(report_issues) + utils.coerce_dict_values(self.inputs, report_issues) + utils.coerce_dict_values(self.outputs, report_issues) + utils.coerce_dict_values(self.workflows, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -678,13 +678,13 @@ class NodeBase(InstanceModelMixin): utils.validate_dict_values(self.capabilities) utils.validate_list_values(self.outbound_relationships) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) - utils.coerce_dict_values(self, self.attributes, report_issues) - utils.coerce_dict_values(self, self.interfaces, report_issues) - utils.coerce_dict_values(self, self.artifacts, report_issues) - utils.coerce_dict_values(self, self.capabilities, report_issues) - utils.coerce_list_values(self, self.outbound_relationships, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.attributes, report_issues) + utils.coerce_dict_values(self.interfaces, report_issues) + utils.coerce_dict_values(self.artifacts, report_issues) + utils.coerce_dict_values(self.capabilities, report_issues) + utils.coerce_list_values(self.outbound_relationships, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -808,9 +808,9 @@ class GroupBase(InstanceModelMixin): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.interfaces) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) - utils.coerce_dict_values(container, self.interfaces, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.interfaces, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -927,8 +927,8 @@ class PolicyBase(InstanceModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1028,8 +1028,8 @@ class SubstitutionBase(InstanceModelMixin): def validate(self): utils.validate_dict_values(self.mappings) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.mappings, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.mappings, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1132,7 +1132,7 @@ class SubstitutionMappingBase(InstanceModelMixin): return collections.OrderedDict(( ('name', self.name))) - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def validate(self): @@ -1320,9 +1320,9 @@ class RelationshipBase(InstanceModelMixin): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.interfaces) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) - utils.coerce_dict_values(container, self.interfaces, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.interfaces, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1460,8 +1460,8 @@ class CapabilityBase(InstanceModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1607,9 +1607,9 @@ class InterfaceBase(InstanceModelMixin): utils.validate_dict_values(self.inputs) utils.validate_dict_values(self.operations) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.inputs, report_issues) - utils.coerce_dict_values(container, self.operations, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.inputs, report_issues) + utils.coerce_dict_values(self.operations, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1774,8 +1774,8 @@ class OperationBase(InstanceModelMixin): # TODO must be associated with interface or service utils.validate_dict_values(self.inputs) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.inputs, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.inputs, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1914,8 +1914,8 @@ class ArtifactBase(InstanceModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index 48eeb83..ec9bed1 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -346,16 +346,16 @@ class ServiceTemplateBase(TemplateModelMixin): if self.artifact_types is not None: self.artifact_types.validate() - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.meta_data, report_issues) - utils.coerce_dict_values(container, self.node_templates, report_issues) - utils.coerce_dict_values(container, self.group_templates, report_issues) - utils.coerce_dict_values(container, self.policy_templates, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.meta_data, report_issues) + utils.coerce_dict_values(self.node_templates, report_issues) + utils.coerce_dict_values(self.group_templates, report_issues) + utils.coerce_dict_values(self.policy_templates, report_issues) if self.substitution_template is not None: - self.substitution_template.coerce_values(container, report_issues) - utils.coerce_dict_values(container, self.inputs, report_issues) - utils.coerce_dict_values(container, self.outputs, report_issues) - utils.coerce_dict_values(container, self.workflow_templates, report_issues) + self.substitution_template.coerce_values(report_issues) + utils.coerce_dict_values(self.inputs, report_issues) + utils.coerce_dict_values(self.outputs, report_issues) + utils.coerce_dict_values(self.workflow_templates, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -585,13 +585,13 @@ class NodeTemplateBase(TemplateModelMixin): utils.validate_dict_values(self.capability_templates) utils.validate_list_values(self.requirement_templates) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) - utils.coerce_dict_values(self, self.attributes, report_issues) - utils.coerce_dict_values(self, self.interface_templates, report_issues) - utils.coerce_dict_values(self, self.artifact_templates, report_issues) - utils.coerce_dict_values(self, self.capability_templates, report_issues) - utils.coerce_list_values(self, self.requirement_templates, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.attributes, report_issues) + utils.coerce_dict_values(self.interface_templates, report_issues) + utils.coerce_dict_values(self.artifact_templates, report_issues) + utils.coerce_dict_values(self.capability_templates, report_issues) + utils.coerce_list_values(self.requirement_templates, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -731,9 +731,9 @@ class GroupTemplateBase(TemplateModelMixin): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.interface_templates) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) - utils.coerce_dict_values(self, self.interface_templates, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.interface_templates, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -862,8 +862,8 @@ class PolicyTemplateBase(TemplateModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -956,8 +956,8 @@ class SubstitutionTemplateBase(TemplateModelMixin): def validate(self): utils.validate_dict_values(self.mappings) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.mappings, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.mappings, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1060,7 +1060,7 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin): return collections.OrderedDict(( ('name', self.name))) - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def instantiate(self, container): @@ -1295,9 +1295,9 @@ class RequirementTemplateBase(TemplateModelMixin): if self.relationship_template: self.relationship_template.validate() - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): if self.relationship_template is not None: - self.relationship_template.coerce_values(container, report_issues) + self.relationship_template.coerce_values(report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1428,9 +1428,9 @@ class RelationshipTemplateBase(TemplateModelMixin): utils.validate_dict_values(self.properties) utils.validate_dict_values(self.interface_templates) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) - utils.coerce_dict_values(self, self.interface_templates, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) + utils.coerce_dict_values(self.interface_templates, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1585,8 +1585,8 @@ class CapabilityTemplateBase(TemplateModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(self, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1739,9 +1739,9 @@ class InterfaceTemplateBase(TemplateModelMixin): utils.validate_dict_values(self.inputs) utils.validate_dict_values(self.operation_templates) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.inputs, report_issues) - utils.coerce_dict_values(container, self.operation_templates, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.inputs, report_issues) + utils.coerce_dict_values(self.operation_templates, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -1893,7 +1893,7 @@ class OperationTemplateBase(TemplateModelMixin): plugin = None implementation = None else: - # using the execution plugin + # Using the execution plugin plugin = None implementation = self.implementation @@ -1914,8 +1914,8 @@ class OperationTemplateBase(TemplateModelMixin): def validate(self): utils.validate_dict_values(self.inputs) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.inputs, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.inputs, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -2062,8 +2062,8 @@ class ArtifactTemplateBase(TemplateModelMixin): def validate(self): utils.validate_dict_values(self.properties) - def coerce_values(self, container, report_issues): - utils.coerce_dict_values(container, self.properties, report_issues) + def coerce_values(self, report_issues): + utils.coerce_dict_values(self.properties, report_issues) def dump(self): context = ConsumptionContext.get_thread_local() @@ -2139,7 +2139,7 @@ class PluginSpecificationBase(TemplateModelMixin): ('version', self.version), ('enabled', self.enabled))) - def coerce_values(self, container, report_issues): + def coerce_values(self, report_issues): pass def resolve(self, model_storage): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/modeling/utils.py ---------------------------------------------------------------------- diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py index 7be20d6..c786965 100644 --- a/aria/modeling/utils.py +++ b/aria/modeling/utils.py @@ -19,8 +19,6 @@ from StringIO import StringIO from . import exceptions from ..parser.consumption import ConsumptionContext -from ..parser.exceptions import InvalidValueError -from ..utils.collections import OrderedDict from ..utils.console import puts from ..utils.type import validate_value_type @@ -108,52 +106,17 @@ def _merge_and_validate_inputs(inputs, template_inputs): return merged_inputs -def coerce_value(container, value, report_issues=False): - final = True - - if isinstance(value, list): - coerced_list = [] - for v in value: - child_final, v = coerce_value(container, v, report_issues) - if not child_final: - final = False - coerced_list.append(v) - value = coerced_list - elif isinstance(value, dict): - coerced_dict = OrderedDict() - for k, v in value.iteritems(): - child_final, v = coerce_value(container, v, report_issues) - if not child_final: - final = False - coerced_dict[k] = v - value = coerced_dict - elif hasattr(value, '_evaluate'): - context = ConsumptionContext.get_thread_local() - try: - final, value = value._evaluate(context, container) - child_final, value = coerce_value(container, value, report_issues) - if not child_final: - final = False - except exceptions.CannotEvaluateFunctionException: - final = False - except InvalidValueError as e: - if report_issues: - context.validation.report(e.issue) - - return final, value - - -def coerce_dict_values(container, the_dict, report_issues=False): +def coerce_dict_values(the_dict, report_issues=False): if not the_dict: return - coerce_list_values(container, the_dict.itervalues(), report_issues) + coerce_list_values(the_dict.itervalues(), report_issues) -def coerce_list_values(container, the_list, report_issues=False): +def coerce_list_values(the_list, report_issues=False): if not the_list: return for value in the_list: - value.coerce_values(container, report_issues) + value.coerce_values(report_issues) def validate_dict_values(the_dict): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/parser/consumption/__init__.py ---------------------------------------------------------------------- diff --git a/aria/parser/consumption/__init__.py b/aria/parser/consumption/__init__.py index 8f6d2b6..97b3db4 100644 --- a/aria/parser/consumption/__init__.py +++ b/aria/parser/consumption/__init__.py @@ -28,9 +28,11 @@ from .modeling import ( Types, ServiceInstance, FindHosts, + ValidateServiceInstance, ConfigureOperations, SatisfyRequirements, - ValidateCapabilities + ValidateCapabilities, + CoerceServiceInstanceValues ) from .inputs import Inputs @@ -45,7 +47,10 @@ __all__ = ( 'ServiceTemplate', 'Types', 'ServiceInstance', - 'Inputs', + 'FindHosts', + 'ValidateServiceInstance', + 'ConfigureOperations', 'SatisfyRequirements', 'ValidateCapabilities' + 'CoerceServiceInstanceValues' ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/aria/parser/consumption/modeling.py ---------------------------------------------------------------------- diff --git a/aria/parser/consumption/modeling.py b/aria/parser/consumption/modeling.py index 771fd7f..44027b9 100644 --- a/aria/parser/consumption/modeling.py +++ b/aria/parser/consumption/modeling.py @@ -42,7 +42,7 @@ class CoerceServiceTemplateValues(Consumer): """ def consume(self): - self.context.modeling.template.coerce_values(None, True) + self.context.modeling.template.coerce_values(True) class ValidateServiceTemplate(Consumer): @@ -116,7 +116,7 @@ class CoerceServiceInstanceValues(Consumer): """ def consume(self): - self.context.modeling.instance.coerce_values(None, True) + self.context.modeling.instance.coerce_values(True) class ValidateServiceInstance(Consumer): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/functions.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/functions.py b/extensions/aria_extension_tosca/simple_v1_0/functions.py index 125452e..c698953 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/functions.py +++ b/extensions/aria_extension_tosca/simple_v1_0/functions.py @@ -18,10 +18,12 @@ from cStringIO import StringIO from aria.utils.collections import FrozenList from aria.utils.formatting import as_raw, safe_repr from aria.parser import dsl_specification +from aria.parser.consumption import ConsumptionContext from aria.parser.exceptions import InvalidValueError from aria.parser.validation import Issue from aria.modeling.exceptions import CannotEvaluateFunctionException -from aria.modeling.functions import Function +from aria.modeling.models import Parameter +from aria.modeling.functions import (Function, Evaluation) # @@ -59,16 +61,16 @@ class Concat(Function): string_expressions.append(string_expression) return {'concat': string_expressions} - def _evaluate(self, context, container): + def __evaluate__(self, container_holder): final = True value = StringIO() for e in self.string_expressions: - if hasattr(e, '_evaluate'): - child_final, e = e._evaluate(context, container) - if not child_final: + if hasattr(e, '__evaluate__'): + e = e.__evaluate__(container_holder) + if not e.final: final = False value.write(str(e)) - return final, value.getvalue() + return Evaluation(value.getvalue(), final) @dsl_specification('4.3.2', 'tosca-simple-1.0') @@ -104,10 +106,10 @@ class Token(Function): string_of_token_chars = as_raw(string_of_token_chars) return {'token': [string_with_tokens, string_of_token_chars, self.substring_index]} - def _evaluate(self, context, container): + def __evaluate__(self, container_holder): string_with_tokens = self.string_with_tokens - if hasattr(string_with_tokens, '_evaluate'): - string_with_tokens = string_with_tokens._evaluate(context, container) # pylint: disable=no-member + if hasattr(string_with_tokens, '__evaluate__'): + string_with_tokens = string_with_tokens.__evaluate__(container_holder) # pylint: disable=no-member # TODO # @@ -141,13 +143,18 @@ class GetInput(Function): def as_raw(self): return {'get_input': as_raw(self.input_property_name)} - def _evaluate(self, context, container): # pylint: disable=unused-argument - if not context.modeling.instance: + def __evaluate__(self, container_holder): + if not isinstance(container_holder, Parameter): raise CannotEvaluateFunctionException() - the_input = context.modeling.instance.inputs.get( - self.input_property_name, - context.modeling.template.inputs.get(self.input_property_name)) - return True, (as_raw(the_input.value) if the_input is not None else None) + + service = container_holder.service + if service is None: + raise CannotEvaluateFunctionException() + + the_input = service.inputs.get(self.input_property_name) + if the_input is not None: + the_input = as_raw(the_input.value) + return Evaluation(the_input, False) @dsl_specification('4.4.2', 'tosca-simple-1.0') @@ -175,8 +182,9 @@ class GetProperty(Function): def as_raw(self): return {'get_property': [self.modelable_entity_name] + self.nested_property_name_or_index} - def _evaluate(self, context, container): - modelable_entities = get_modelable_entities(context, container, self.locator, + def __evaluate__(self, container_holder): + context = ConsumptionContext.get_thread_local() + modelable_entities = get_modelable_entities(context, container_holder, self.locator, self.modelable_entity_name) req_or_cap_name = self.nested_property_name_or_index[0] @@ -203,10 +211,10 @@ class GetProperty(Function): properties = modelable_entity.properties nested_property_name_or_index = self.nested_property_name_or_index - found, final, value = get_modelable_entity_parameter(context, modelable_entity, properties, - nested_property_name_or_index) - if found: - return final, as_raw(value.value) + evaluation = get_modelable_entity_parameter(context, modelable_entity, properties, + nested_property_name_or_index) + if evaluation is not None: + return evaluation raise InvalidValueError( 'function "get_property" could not find "{0}" in modelable entity "{1}"' @@ -243,25 +251,24 @@ class GetAttribute(Function): def as_raw(self): return {'get_attribute': [self.modelable_entity_name] + self.nested_attribute_name_or_index} - def _evaluate(self, context, container): # pylint: disable=no-self-use,unused-argument - raise CannotEvaluateFunctionException() - - modelable_entities = get_modelable_entities(context, container, self.locator, + def __evaluate__(self, container_holder): + modelable_entities = get_modelable_entities(container_holder, self.locator, self.modelable_entity_name) for modelable_entity in modelable_entities: attributes = modelable_entity.attributes nested_attribute_name_or_index = self.nested_attribute_name_or_index - - found, value = get_modelable_entity_parameter(context, modelable_entity, attributes, - nested_attribute_name_or_index) - if found: - return False, as_raw(value.value) + evaluation = get_modelable_entity_parameter(modelable_entity, attributes, + nested_attribute_name_or_index) + if evaluation is not None: + evaluation.final = False # We never return final evaluations! + return evaluation raise InvalidValueError( 'function "get_attribute" could not find "{0}" in modelable entity "{1}"' .format('.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name), locator=self.locator) + # # Operation # @@ -340,7 +347,7 @@ class GetNodesOfType(Function): node_type_name = as_raw(node_type_name) return {'get_nodes_of_type': node_type_name} - def _evaluate(self, context, container): + def __evaluate__(self, container): pass @@ -477,20 +484,20 @@ def parse_self(presentation): @dsl_specification('4.1', 'tosca-simple-1.0') -def get_modelable_entities(context, container, locator, modelable_entity_name): +def get_modelable_entities(container_holder, locator, modelable_entity_name): """ The following keywords MAY be used in some TOSCA function in place of a TOSCA Node or Relationship Template name. """ if modelable_entity_name == 'SELF': - return get_self(context, container) + return get_self(container_holder) elif modelable_entity_name == 'HOST': - return get_hosts(context, container) + return get_hosts(container_holder) elif modelable_entity_name == 'SOURCE': - return get_source(context, container) + return get_source(container_holder) elif modelable_entity_name == 'TARGET': - return get_target(context, container) + return get_target(container_holder) elif isinstance(modelable_entity_name, basestring): node_templates = \ context.presentation.get('service_template', 'topology_template', 'node_templates') \ @@ -509,16 +516,16 @@ def get_modelable_entities(context, container, locator, modelable_entity_name): locator=locator) -def get_self(context, container): # pylint: disable=unused-argument +def get_self(container_holder): """ A TOSCA orchestrator will interpret this keyword as the Node or Relationship Template instance that contains the function at the time the function is evaluated. """ - return [container] + return [container_holder.container] -def get_hosts(context, container): # pylint: disable=unused-argument +def get_hosts(container_holder): # pylint: disable=unused-argument """ A TOSCA orchestrator will interpret this keyword to refer to the all nodes that "host" the node using this reference (i.e., as identified by its HostedOn relationship). @@ -533,7 +540,7 @@ def get_hosts(context, container): # pylint: disable=unused-argument return [] -def get_source(context, container): # pylint: disable=unused-argument +def get_source(container_holder): # pylint: disable=unused-argument """ A TOSCA orchestrator will interpret this keyword as the Node Template instance that is at the source end of the relationship that contains the referencing function. @@ -542,15 +549,14 @@ def get_source(context, container): # pylint: disable=unused-argument return [] -def get_target(context, container): # pylint: disable=unused-argument +def get_target(container_holder): # pylint: disable=unused-argument """ A TOSCA orchestrator will interpret this keyword as the Node Template instance that is at the target end of the relationship that contains the referencing function. """ -def get_modelable_entity_parameter(context, modelable_entity, parameters, - nested_parameter_name_or_index): +def get_modelable_entity_parameter(modelable_entity, parameters, nested_parameter_name_or_index): if not parameters: return False, True, None @@ -561,16 +567,17 @@ def get_modelable_entity_parameter(context, modelable_entity, parameters, for name in nested_parameter_name_or_index: if (isinstance(value, dict) and (name in value)) \ or (isinstance(value, list) and name < len(list)): - value = value[name] - if hasattr(value, '_evaluate'): - child_final, value = value._evaluate(context, modelable_entity) - if not child_final: + value = value[name].value + if hasattr(value, '__evaluate__'): + evaluation = value.__evaluate__(modelable_entity) + value = evaluation.value + if not evaluation.final: final = False else: found = False break - return found, final, value + return Evaluation(value, final) if found else None def invalid_modelable_entity_name(name, index, value, locator, contexts): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py index 945f795..c7e1a58 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -591,8 +591,8 @@ def create_constraint_clause_lambda(context, node_filter, constraint_clause, pro def coerce_constraint(constraint, container): constraint = coerce_value(context, node_filter, the_type, None, None, constraint, constraint_key) if the_type is not None else constraint - if hasattr(constraint, '_evaluate'): - constraint = constraint._evaluate(context, container) + if hasattr(constraint, '__evaluate__'): + constraint = constraint.__evaluate__(container).value return constraint def get_value(node_type): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/6f349dd7/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py index 6c7b780..8bf45e0 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py @@ -371,6 +371,8 @@ def coerce_value(context, presentation, the_type, entry_schema, constraints, val If the extension is present, we will delegate to that hook. """ + # TODO: should support models as well as presentations + is_function, func = get_function(context, presentation, value) if is_function: return func
