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

Reply via email to