Repository: incubator-ariatosca Updated Branches: refs/heads/ARIA-180-convert-parameter-to-one-to-many 7cb6accf5 -> 517574533
Add attribute model and attribute many-to-one relationships To node template and node. Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/51757453 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/51757453 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/51757453 Branch: refs/heads/ARIA-180-convert-parameter-to-one-to-many Commit: 5175745338b017ff2de888c52162eb67d50c2966 Parents: 7cb6acc Author: Avia Efrat <a...@gigaspaces.com> Authored: Thu May 25 01:04:11 2017 +0300 Committer: Avia Efrat <a...@gigaspaces.com> Committed: Thu May 25 01:04:11 2017 +0300 ---------------------------------------------------------------------- aria/modeling/models.py | 12 +- aria/modeling/service_common.py | 221 +++++++++++++++++++ aria/modeling/service_instance.py | 14 +- aria/modeling/service_template.py | 12 +- .../simple_v1_0/modeling/__init__.py | 10 +- 5 files changed, 240 insertions(+), 29 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51757453/aria/modeling/models.py ---------------------------------------------------------------------- diff --git a/aria/modeling/models.py b/aria/modeling/models.py index 6d43327..9b95f19 100644 --- a/aria/modeling/models.py +++ b/aria/modeling/models.py @@ -76,6 +76,7 @@ __all__ = ( 'Input', 'Output', 'Property', + 'Attribute' 'Type', 'Metadata', @@ -214,20 +215,18 @@ class Parameter(aria_declarative_base, service_common.ParameterBase): class Input(aria_declarative_base, service_common.InputBase): - # Temporarily, until we will separate the Parameter model into Input, Output, Property and - # Attribute, Parameter will represent only Attribute. pass class Output(aria_declarative_base, service_common.OutputBase): - # Temporarily, until we will separate the Parameter model into Input, Output, Property and - # Attribute, Parameter will represent only Attribute. pass class Property(aria_declarative_base, service_common.PropertyBase): - # Temporarily, until we will separate the Parameter model into Input, Output, Property and - # Attribute, Parameter will represent only Attribute. + pass + + +class Attribute(aria_declarative_base, service_common.AttributeBase): pass @@ -301,6 +300,7 @@ models_to_register = [ Input, Output, Property, + Attribute, Type, Metadata, http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51757453/aria/modeling/service_common.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py index c9bd0d8..0542e00 100644 --- a/aria/modeling/service_common.py +++ b/aria/modeling/service_common.py @@ -1146,6 +1146,227 @@ class PropertyBase(TemplateModelMixin, caching.HasCachedMethods): description=description) +class AttributeBase(TemplateModelMixin, caching.HasCachedMethods): + """ + Represents a typed value. The value can contain nested intrinsic functions. + + This model can be used as the ``container_holder`` argument for :func:`functions.evaluate`. + + :ivar name: Name + :vartype name: basestring + :ivar type_name: Type name + :vartype type_name: basestring + :ivar value: Value + :ivar description: Description + :vartype description: basestring + """ + + __tablename__ = 'attribute' + + name = Column(Text) + type_name = Column(Text) + description = Column(Text) + _value = Column(PickleType) + + @property + def value(self): + value = self._value + if value is not None: + evaluation = functions.evaluate(value, self) + if evaluation is not None: + value = evaluation.value + return value + + @value.setter + def value(self, value): + self._value = value + + # region foreign keys + + @declared_attr + def node_template_fk(cls): + """For Attribute many-to-one to NodeTemplate""" + return relationship.foreign_key('node_template', nullable=True) + + @declared_attr + def node_fk(cls): + """For Attribute many-to-one to Node""" + return relationship.foreign_key('node', nullable=True) + + # endregion + + # region many_to_one relationships + + @declared_attr + def node_template(cls): + return relationship.many_to_one(cls, 'node_template') + + @declared_attr + def node(cls): + return relationship.many_to_one(cls, 'node') + + # endregion + + @property + @caching.cachedmethod + def owner(self): + """ + The sole owner of this attribute, which is another model that relates to it. + + *All* parameters should have an owner model. In case this property method fails to find + it, it will raise a ValueError, which should signify an abnormal, orphaned parameter. + """ + + # Find first non-null relationship + for the_relationship in self.__mapper__.relationships: + v = getattr(self, the_relationship.key) + if v: + return v # because we are many-to-many, the back reference will be a list + + raise ValueError('orphaned attribute: does not have an owner: {0}'.format(self.name)) + + @property + @caching.cachedmethod + def container(self): # pylint: disable=too-many-return-statements,too-many-branches + """ + The logical container for this attribute, which would be another model: service, node, + group, or policy (or their templates). + + The logical container is equivalent to the ``SELF`` keyword used by intrinsic functions in + TOSCA. + + *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 abnormal, orphaned parameter. + """ + + from . import models + + container = self.owner + + # Extract interface from operation + if isinstance(container, models.Operation): + container = container.interface + elif isinstance(container, models.OperationTemplate): + container = container.interface_template + + # Extract from other models + if isinstance(container, models.Interface): + container = container.node or container.group or container.relationship + elif isinstance(container, models.InterfaceTemplate): + container = container.node_template or container.group_template \ + or container.relationship_template + elif isinstance(container, models.Capability) or isinstance(container, models.Artifact): + container = container.node + elif isinstance(container, models.CapabilityTemplate) \ + or isinstance(container, models.ArtifactTemplate): + container = container.node_template + elif isinstance(container, models.Task): + container = container.actor + + # Extract node from relationship + if isinstance(container, models.Relationship): + container = container.source_node + elif isinstance(container, models.RelationshipTemplate): + container = container.requirement_template.node_template + + if container is not None: + return container + + raise ValueError('orphaned attribute: does not have a container: {0}'.format(self.name)) + + @property + @caching.cachedmethod + def service(self): + """ + The :class:`Service` containing this parameter, or None if not contained in a service. + """ + + from . import models + container = self.container + if isinstance(container, models.Service): + return container + elif hasattr(container, 'service'): + return container.service + return None + + @property + @caching.cachedmethod + def service_template(self): + """ + The :class:`ServiceTemplate` containing this parameter, or None if not contained in a + service template. + """ + + 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), + ('type_name', self.type_name), + ('value', self.value), + ('description', self.description))) + + def instantiate(self, container): + from . import models + return models.Attribute(name=self.name, # pylint: disable=unexpected-keyword-arg + type_name=self.type_name, + _value=self._value, + description=self.description) + + 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() + if self.type_name is not None: + console.puts('{0}: {1} ({2})'.format( + context.style.property(self.name), + context.style.literal(formatting.as_raw(self.value)), + context.style.type(self.type_name))) + else: + console.puts('{0}: {1}'.format( + context.style.property(self.name), + context.style.literal(formatting.as_raw(self.value)))) + if self.description: + console.puts(context.style.meta(self.description)) + + def unwrap(self): + return self.name, self.value + + @classmethod + def wrap(cls, name, value, description=None): + """ + Wraps an arbitrary value as a parameter. The type will be guessed via introspection. + + :param name: Parameter name + :type name: basestring + :param value: Parameter value + :param description: Description (optional) + :type description: basestring + """ + + from . import models + type_name = canonical_type_name(value) + if type_name is None: + type_name = full_type_name(value) + return models.Attribute(name=name, # pylint: disable=unexpected-keyword-arg + type_name=type_name, + value=value, + description=description) + + class MetadataBase(TemplateModelMixin): """ Custom values associated with the service. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51757453/aria/modeling/service_instance.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py index baae6af..2d68a9d 100644 --- a/aria/modeling/service_instance.py +++ b/aria/modeling/service_instance.py @@ -336,7 +336,7 @@ class NodeBase(InstanceModelMixin): :ivar runtime_properties: TODO: should be replaced with attributes :vartype runtime_properties: {} :ivar state: The state of the node, according to to the TOSCA-defined node states - :vartype state: string + :vartype stateg: string :ivar version: Used by `aria.storage.instrumentation` :vartype version: int :ivar service: Containing service @@ -467,6 +467,10 @@ class NodeBase(InstanceModelMixin): return relationship.one_to_many(cls, 'property', dict_key='name') @declared_attr + def attributes(cls): + return relationship.one_to_many(cls, 'attribute', dict_key='name') + + @declared_attr def artifacts(cls): return relationship.one_to_many(cls, 'artifact', dict_key='name') @@ -512,14 +516,6 @@ class NodeBase(InstanceModelMixin): # endregion - # region many_to_many relationships - - @declared_attr - def attributes(cls): - return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name') - - # endregion - description = Column(Text) runtime_properties = Column(modeling_types.Dict) state = Column(Enum(*STATES, name='node_state'), nullable=False, default=INITIAL) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51757453/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index 7fd693a..95e2714 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -499,6 +499,10 @@ class NodeTemplateBase(TemplateModelMixin): def properties(cls): return relationship.one_to_many(cls, 'property', dict_key='name') + @declared_attr + def attributes(cls): + return relationship.one_to_many(cls, 'attribute', dict_key='name') + # endregion # region many_to_one relationships @@ -513,14 +517,6 @@ class NodeTemplateBase(TemplateModelMixin): # endregion - # region many_to_many relationships - - @declared_attr - def attributes(cls): - return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name') - - # endregion - description = Column(Text) default_instances = Column(Integer, default=1) min_instances = Column(Integer, default=0) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/51757453/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 18cba47..d89fc90 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -33,7 +33,7 @@ from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate, GroupTemplate, PolicyTemplate, SubstitutionTemplate, SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate, ArtifactTemplate, Metadata, Parameter, Input, Output, Property, - PluginSpecification) + Attribute, PluginSpecification) from .constraints import (Equal, GreaterThan, GreaterOrEqual, LessThan, LessOrEqual, InRange, ValidValues, Length, MinLength, MaxLength, Pattern) @@ -173,7 +173,8 @@ def create_node_template_model(context, service_template, node_template): node_template._get_property_values(context), model_class=Property) create_parameter_models_from_values(model.attributes, - node_template._get_attribute_default_values(context)) + node_template._get_attribute_default_values(context), + model_class=Attribute) create_interface_template_models(context, service_template, model.interface_templates, node_template._get_interfaces(context)) @@ -243,7 +244,7 @@ def create_policy_template_model(context, service_template, policy): model.description = policy.description.value create_parameter_models_from_values(model.properties, policy._get_property_values(context), - Property) + model_class=Property) node_templates, groups = policy._get_targets(context) if node_templates: @@ -546,9 +547,6 @@ def create_types(context, root, types): def create_parameter_models_from_values(properties, source_properties, model_class=None): - if model_class is None: - model_class = Parameter - if source_properties: for property_name, prop in source_properties.iteritems(): properties[property_name] = model_class(name=property_name, # pylint: disable=unexpected-keyword-arg