You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by em...@apache.org on 2017/05/09 20:50:35 UTC

[1/3] incubator-ariatosca git commit: ARIA-140 Version utils [Forced Update!]

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-139-attributes d06a01657 -> b940419b6 (forced update)


ARIA-140 Version utils

Provided mainly to support version comparisons for plugins in order to
select the highest version of available plugins that would match a
plugin specification. In the future may be useful for other version
comparisons.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/eae44d0b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/eae44d0b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/eae44d0b

Branch: refs/heads/ARIA-139-attributes
Commit: eae44d0b0d051fd50cadf94d734bdf7a3b8d3171
Parents: d0411d3
Author: Tal Liron <ta...@gmail.com>
Authored: Fri Apr 14 13:39:02 2017 -0500
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue May 9 15:37:46 2017 -0500

----------------------------------------------------------------------
 aria/modeling/service_template.py |  17 ++--
 aria/utils/versions.py            | 162 +++++++++++++++++++++++++++++++++
 tests/utils/test_versions.py      |  85 +++++++++++++++++
 3 files changed, 257 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/eae44d0b/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index f721b64..e3320fa 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -33,7 +33,8 @@ from sqlalchemy.ext.associationproxy import association_proxy
 from ..parser import validation
 from ..parser.consumption import ConsumptionContext
 from ..parser.reading import deepcopy_with_locators
-from ..utils import collections, formatting, console
+from ..utils import (collections, formatting, console)
+from ..utils.versions import VersionString
 from .mixins import TemplateModelMixin
 from . import (
     relationship,
@@ -2135,13 +2136,15 @@ class PluginSpecificationBase(TemplateModelMixin):
         # moved to.
         plugins = model_storage.plugin.list()
         matching_plugins = []
-        for plugin in plugins:
-            # TODO: we need to use a version comparator
-            if (plugin.name == self.name) and \
-                    ((self.version is None) or (plugin.package_version >= self.version)):
-                matching_plugins.append(plugin)
+        if plugins:
+            for plugin in plugins:
+                if (plugin.name == self.name) and \
+                    ((self.version is None) or \
+                     (VersionString(plugin.package_version) >= self.version)):
+                    matching_plugins.append(plugin)
         self.plugin = None
         if matching_plugins:
             # Return highest version of plugin
-            self.plugin = sorted(matching_plugins, key=lambda plugin: plugin.package_version)[-1]
+            key = lambda plugin: VersionString(plugin.package_version).key
+            self.plugin = sorted(matching_plugins, key=key)[-1]
         return self.plugin is not None

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/eae44d0b/aria/utils/versions.py
----------------------------------------------------------------------
diff --git a/aria/utils/versions.py b/aria/utils/versions.py
new file mode 100644
index 0000000..925f59e
--- /dev/null
+++ b/aria/utils/versions.py
@@ -0,0 +1,162 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+"""
+General-purpose version string handling
+"""
+
+import re
+
+
+_INF = float('inf')
+
+_NULL = (), _INF
+
+_DIGITS_RE = re.compile(r'^\d+$')
+
+_PREFIXES = {
+    'dev':   0.0001,
+    'alpha': 0.001,
+    'beta':  0.01,
+    'rc':    0.1
+}
+
+
+class VersionString(unicode):
+    """
+    Version string that can be compared, sorted, made unique in a set, and used as a unique dict
+    key.
+
+    The primary part of the string is one or more dot-separated natural numbers. Trailing zeroes
+    are treated as redundant, e.g. "1.0.0" == "1.0" == "1".
+
+    An optional qualifier can be added after a "-". The qualifier can be a natural number or a
+    specially treated prefixed natural number, e.g. "1.1-beta1" > "1.1-alpha2". The case of the
+    prefix is ignored.
+
+    Numeric qualifiers will always be greater than prefixed integer qualifiers, e.g. "1.1-1" >
+    "1.1-beta1".
+
+    Versions without a qualifier will always be greater than their equivalents with a qualifier,
+    e.g. e.g. "1.1" > "1.1-1".
+
+    Any value that does not conform to this format will be treated as a zero version, which would
+    be lesser than any non-zero version.
+
+    For efficient list sorts use the ``key`` property, e.g.:
+    ``sorted(versions, key=lambda x: x.key)``
+    """
+
+    NULL = None # initialized below
+
+    def __init__(self, value=None):
+        if value is not None:
+            super(VersionString, self).__init__(value)
+        self.key = parse_version_string(self)
+
+    def __eq__(self, version):
+        if not isinstance(version, VersionString):
+            version = VersionString(version)
+        return self.key == version.key
+
+    def __lt__(self, version):
+        if not isinstance(version, VersionString):
+            version = VersionString(version)
+        return self.key < version.key
+
+    def __hash__(self):
+        return self.key.__hash__()
+
+
+def parse_version_string(version): # pylint: disable=too-many-branches
+    """
+    Parses a version string.
+
+    :param version: The version string
+    :returns: The primary tuple and qualifier float
+    :rtype: ((int), float)
+    """
+
+    if version is None:
+        return _NULL
+    version = unicode(version)
+
+    # Split to primary and qualifier on '-'
+    split = version.split('-', 1)
+    if len(split) == 2:
+        primary, qualifier = split
+    else:
+        primary = split[0]
+        qualifier = None
+
+    # Parse primary
+    split = primary.split('.')
+    primary = []
+    for element in split:
+        if _DIGITS_RE.match(element) is None:
+            # Invalid version string
+            return _NULL
+        try:
+            element = int(element)
+        except ValueError:
+            # Invalid version string
+            return _NULL
+        primary.append(element)
+
+    # Remove redundant zeros
+    for element in reversed(primary):
+        if element == 0:
+            primary.pop()
+        else:
+            break
+    primary = tuple(primary)
+
+    # Parse qualifier
+    if qualifier is not None:
+        if _DIGITS_RE.match(qualifier) is not None:
+            # Integer qualifier
+            try:
+                qualifier = float(int(qualifier))
+            except ValueError:
+                # Invalid version string
+                return _NULL
+        else:
+            # Prefixed integer qualifier
+            value = None
+            qualifier = qualifier.lower()
+            for prefix, factor in _PREFIXES.iteritems():
+                if qualifier.startswith(prefix):
+                    value = qualifier[len(prefix):]
+                    if _DIGITS_RE.match(value) is None:
+                        # Invalid version string
+                        return _NULL
+                    try:
+                        value = float(int(value)) * factor
+                    except ValueError:
+                        # Invalid version string
+                        return _NULL
+                    break
+            if value is None:
+                # Invalid version string
+                return _NULL
+            qualifier = value
+    else:
+        # Version strings with no qualifiers are higher
+        qualifier = _INF
+
+    return primary, qualifier
+
+
+VersionString.NULL = VersionString()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/eae44d0b/tests/utils/test_versions.py
----------------------------------------------------------------------
diff --git a/tests/utils/test_versions.py b/tests/utils/test_versions.py
new file mode 100644
index 0000000..222949c
--- /dev/null
+++ b/tests/utils/test_versions.py
@@ -0,0 +1,85 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from aria.utils.versions import (VersionString, parse_version_string)
+
+
+def test_version_string():
+    # No qualifiers
+    assert VersionString('20') == VersionString('20')
+    assert VersionString('20') == VersionString('20.0')
+    assert VersionString('20') == VersionString('20.0.0')
+    assert VersionString('20') < VersionString('20.0.1')
+
+    # With numeric qualifiers
+    assert VersionString('20.0.1-1') < VersionString('20.0.1-2')
+    assert VersionString('20.0.1-0') < VersionString('20.0.1')
+    assert VersionString('20.0.1-1') < VersionString('20.0.1')
+
+    # With prefixed qualifiers
+    assert VersionString('20.0.1-beta1') < VersionString('20.0.1-beta2')
+    assert VersionString('20.0.1-beta1') < VersionString('20.0.1-1')
+    assert VersionString('20.0.1-beta1') < VersionString('20.0.1')
+    assert VersionString('20.0.1-beta2') < VersionString('20.0.1-rc2')
+    assert VersionString('20.0.1-alpha2') < VersionString('20.0.1-beta1')
+    assert VersionString('20.0.1-dev2') < VersionString('20.0.1-alpha1')
+    assert VersionString('20.0.1-DEV2') < VersionString('20.0.1-ALPHA1')
+
+    # Coercive comparisons
+    assert VersionString('20.0.0') == VersionString(10 * 2)
+    assert VersionString('20.0.0') == VersionString(20.0)
+
+    # Non-VersionString comparisons
+    assert VersionString('20.0.0') == 20
+    assert VersionString('20.0.0') < '20.0.1'
+
+    # Nulls
+    assert VersionString() == VersionString()
+    assert VersionString() == VersionString.NULL
+    assert VersionString(None) == VersionString.NULL
+    assert VersionString.NULL == None # pylint: disable=singleton-comparison
+    assert VersionString.NULL == 0
+
+    # Invalid version strings
+    assert VersionString('maxim is maxim') == VersionString.NULL
+    assert VersionString('20.maxim.0') == VersionString.NULL
+    assert VersionString('20.0.0-maxim1') == VersionString.NULL
+    assert VersionString('20.0.1-1.1') == VersionString.NULL
+
+    # Sorts
+    v1 = VersionString('20.0.0')
+    v2 = VersionString('20.0.1-beta1')
+    v3 = VersionString('20.0.1')
+    v4 = VersionString('20.0.2')
+    assert [v1, v2, v3, v4] == sorted([v4, v3, v2, v1], key=lambda v: v.key)
+
+    # Sets
+    v1 = VersionString('20.0.0')
+    v2 = VersionString('20.0')
+    v3 = VersionString('20')
+    assert set([v1]) == set([v1, v2, v3])
+
+    # Dicts
+    the_dict = {v1: 'test'}
+    assert the_dict.get(v2) == 'test'
+
+def test_parse_version_string():
+    # One test of each type from the groups above should be enough
+    assert parse_version_string('20') < parse_version_string('20.0.1')
+    assert parse_version_string('20.0.1-1') < parse_version_string('20.0.1-2')
+    assert parse_version_string('20.0.1-beta1') < parse_version_string('20.0.1-beta2')
+    assert parse_version_string('20.0.0') == parse_version_string(10 * 2)
+    assert parse_version_string(None) == parse_version_string(0)
+    assert parse_version_string(None) == parse_version_string('maxim is maxim')


[2/3] incubator-ariatosca git commit: ARIA-139 Support attributes

Posted by em...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/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
deleted file mode 100644
index 405aa8f..0000000
--- a/extensions/aria_extension_tosca/simple_v1_0/functions.py
+++ /dev/null
@@ -1,536 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You 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.
-
-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.exceptions import InvalidValueError
-from aria.parser.validation import Issue
-from aria.modeling.exceptions import CannotEvaluateFunctionException
-from aria.modeling.functions import Function
-
-#
-# Intrinsic
-#
-
-@dsl_specification('4.3.1', 'tosca-simple-1.0')
-class Concat(Function):
-    """
-    The :code:`concat` function is used to concatenate two or more string values within a TOSCA
-    service template.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if not isinstance(argument, list):
-            raise InvalidValueError(
-                'function "concat" argument must be a list of string expressions: %s'
-                % safe_repr(argument),
-                locator=self.locator)
-
-        string_expressions = []
-        for index, an_argument in enumerate(argument):
-            string_expressions.append(parse_string_expression(context, presentation, 'concat',
-                                                              index, None, an_argument))
-        self.string_expressions = FrozenList(string_expressions)
-
-    @property
-    def as_raw(self):
-        string_expressions = []
-        for string_expression in self.string_expressions:
-            if hasattr(string_expression, 'as_raw'):
-                string_expression = as_raw(string_expression)
-            string_expressions.append(string_expression)
-        return {'concat': string_expressions}
-
-    def _evaluate(self, context, container):
-        value = StringIO()
-        for e in self.string_expressions:
-            if hasattr(e, '_evaluate'):
-                e = e._evaluate(context, container)
-            value.write(str(e))
-        return value.getvalue()
-
-@dsl_specification('4.3.2', 'tosca-simple-1.0')
-class Token(Function):
-    """
-    The :code:`token` function is used within a TOSCA service template on a string to parse out
-    (tokenize) substrings separated by one or more token characters within a larger string.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if (not isinstance(argument, list)) or (len(argument) != 3):
-            raise InvalidValueError('function "token" argument must be a list of 3 parameters: %s'
-                                    % safe_repr(argument),
-                                    locator=self.locator)
-
-        self.string_with_tokens = parse_string_expression(context, presentation, 'token', 0,
-                                                          'the string to tokenize', argument[0])
-        self.string_of_token_chars = parse_string_expression(context, presentation, 'token', 1,
-                                                             'the token separator characters',
-                                                             argument[1])
-        self.substring_index = parse_int(context, presentation, 'token', 2,
-                                         'the 0-based index of the token to return', argument[2])
-
-    @property
-    def as_raw(self):
-        string_with_tokens = self.string_with_tokens
-        if hasattr(string_with_tokens, 'as_raw'):
-            string_with_tokens = as_raw(string_with_tokens)
-        string_of_token_chars = self.string_with_tokens
-        if hasattr(string_of_token_chars, 'as_raw'):
-            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):
-        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
-
-#
-# Property
-#
-
-@dsl_specification('4.4.1', 'tosca-simple-1.0')
-class GetInput(Function):
-    """
-    The :code:`get_input` function is used to retrieve the values of properties declared within the
-    inputs section of a TOSCA Service Template.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        self.input_property_name = parse_string_expression(context, presentation, 'get_input',
-                                                           None, 'the input property name',
-                                                           argument)
-
-        if isinstance(self.input_property_name, basestring):
-            the_input = context.presentation.get_from_dict('service_template', 'topology_template',
-                                                           'inputs', self.input_property_name)
-            if the_input is None:
-                raise InvalidValueError(
-                    'function "get_input" argument is not a valid input name: %s'
-                    % safe_repr(argument),
-                    locator=self.locator)
-
-    @property
-    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:
-            raise CannotEvaluateFunctionException()
-        the_input = context.modeling.instance.inputs.get(
-            self.input_property_name,
-            context.modeling.template.inputs.get(self.input_property_name))
-        return as_raw(the_input.value) if the_input is not None else None
-
-@dsl_specification('4.4.2', 'tosca-simple-1.0')
-class GetProperty(Function):
-    """
-    The :code:`get_property` function is used to retrieve property values between modelable entities
-    defined in the same service template.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if (not isinstance(argument, list)) or (len(argument) < 2):
-            raise InvalidValueError(
-                'function "get_property" argument must be a list of at least 2 string expressions: '
-                '%s'
-                % safe_repr(argument),
-                locator=self.locator)
-
-        self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
-                                                                 'get_property', 0, argument[0])
-        # The first of these will be tried as a req-or-cap name:
-        self.nested_property_name_or_index = argument[1:]
-
-    @property
-    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,
-                                                    self.modelable_entity_name)
-        req_or_cap_name = self.nested_property_name_or_index[0]
-
-        for modelable_entity in modelable_entities:
-            properties = None
-            
-            if hasattr(modelable_entity, 'requirement_templates') \
-                and modelable_entity.requirement_templates \
-                and (req_or_cap_name in [v.name for v in modelable_entity.requirement_templates]):
-                for requirement_template in modelable_entity.requirement_templates:
-                    if requirement_template.name == req_or_cap_name:
-                        # First argument refers to a requirement
-                        # TODO: should follow to matched capability in other node...
-                        raise CannotEvaluateFunctionException()
-                        break
-                nested_property_name_or_index = self.nested_property_name_or_index[1:]
-            elif hasattr(modelable_entity, 'capability_templates') \
-                and modelable_entity.capability_templates \
-                and (req_or_cap_name in modelable_entity.capability_templates):
-                # First argument refers to a capability
-                properties = modelable_entity.capability_templates[req_or_cap_name].properties
-                nested_property_name_or_index = self.nested_property_name_or_index[1:]
-            else:
-                properties = modelable_entity.properties
-                nested_property_name_or_index = self.nested_property_name_or_index
-
-            if properties:
-                found = True
-                value = properties
-                for name in nested_property_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'):
-                            value = value._evaluate(context, modelable_entity)
-                    else:
-                        found = False
-                        break
-                if found:
-                    return as_raw(value)
-
-        raise InvalidValueError(
-            'function "get_property" could not find "%s" in modelable entity "%s"' \
-            % ('.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
-            locator=self.locator)
-
-#
-# Attribute
-#
-
-@dsl_specification('4.5.1', 'tosca-simple-1.0')
-class GetAttribute(Function):
-    """
-    The :code:`get_attribute` function is used to retrieve the values of named attributes declared
-    by the referenced node or relationship template name.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if (not isinstance(argument, list)) or (len(argument) < 2):
-            raise InvalidValueError(
-                'function "get_attribute" argument must be a list of at least 2 string expressions:'
-                ' %s'
-                % safe_repr(argument),
-                locator=self.locator)
-
-        self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
-                                                                 'get_attribute', 0, argument[0])
-        # The first of these will be tried as a req-or-cap name:
-        self.nested_property_name_or_index = argument[1:]
-
-    @property
-    def as_raw(self):
-        return {'get_attribute': [self.modelable_entity_name] + self.nested_property_name_or_index}
-
-    def _evaluate(self, context, container): # pylint: disable=no-self-use,unused-argument
-        raise CannotEvaluateFunctionException()
-
-#
-# Operation
-#
-
-@dsl_specification('4.6.1', 'tosca-simple-1.0')
-class GetOperationOutput(Function):
-    """
-    The :code:`get_operation_output` function is used to retrieve the values of variables exposed /
-    exported from an interface operation.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if (not isinstance(argument, list)) or (len(argument) != 4):
-            raise InvalidValueError(
-                'function "get_operation_output" argument must be a list of 4 parameters: %s'
-                % safe_repr(argument),
-                locator=self.locator)
-
-        self.modelable_entity_name = parse_string_expression(context, presentation,
-                                                             'get_operation_output', 0,
-                                                             'modelable entity name', argument[0])
-        self.interface_name = parse_string_expression(context, presentation, 'get_operation_output',
-                                                      1, 'the interface name', argument[1])
-        self.operation_name = parse_string_expression(context, presentation, 'get_operation_output',
-                                                      2, 'the operation name', argument[2])
-        self.output_variable_name = parse_string_expression(context, presentation,
-                                                            'get_operation_output', 3,
-                                                            'the output name', argument[3])
-
-    @property
-    def as_raw(self):
-        interface_name = self.interface_name
-        if hasattr(interface_name, 'as_raw'):
-            interface_name = as_raw(interface_name)
-        operation_name = self.operation_name
-        if hasattr(operation_name, 'as_raw'):
-            operation_name = as_raw(operation_name)
-        output_variable_name = self.output_variable_name
-        if hasattr(output_variable_name, 'as_raw'):
-            output_variable_name = as_raw(output_variable_name)
-        return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name,
-                                         output_variable_name]}
-
-#
-# Navigation
-#
-
-@dsl_specification('4.7.1', 'tosca-simple-1.0')
-class GetNodesOfType(Function):
-    """
-    The :code:`get_nodes_of_type` function can be used to retrieve a list of all known instances of
-    nodes of the declared Node Type.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        self.node_type_name = parse_string_expression(context, presentation, 'get_nodes_of_type',
-                                                      None, 'the node type name', argument)
-
-        if isinstance(self.node_type_name, basestring):
-            node_types = context.presentation.get('service_template', 'node_types')
-            if (node_types is None) or (self.node_type_name not in node_types):
-                raise InvalidValueError(
-                    'function "get_nodes_of_type" argument is not a valid node type name: %s'
-                    % safe_repr(argument),
-                    locator=self.locator)
-
-    @property
-    def as_raw(self):
-        node_type_name = self.node_type_name
-        if hasattr(node_type_name, 'as_raw'):
-            node_type_name = as_raw(node_type_name)
-        return {'get_nodes_of_type': node_type_name}
-
-    def _evaluate(self, context, container):
-        pass
-
-#
-# Artifact
-#
-
-@dsl_specification('4.8.1', 'tosca-simple-1.0')
-class GetArtifact(Function):
-    """
-    The :code:`get_artifact` function is used to retrieve artifact location between modelable
-    entities defined in the same service template.
-    """
-
-    def __init__(self, context, presentation, argument):
-        self.locator = presentation._locator
-
-        if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4):
-            raise InvalidValueError(
-                'function "get_artifact" argument must be a list of 2 to 4 parameters: %s'
-                % safe_repr(argument),
-                locator=self.locator)
-
-        self.modelable_entity_name = parse_string_expression(context, presentation, 'get_artifact',
-                                                             0, 'modelable entity name',
-                                                             argument[0])
-        self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1,
-                                                     'the artifact name', argument[1])
-        self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
-                                                'the location or "LOCAL_FILE"', argument[2])
-        self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
-                                 argument[3])
-
-    @property
-    def as_raw(self):
-        artifact_name = self.artifact_name
-        if hasattr(artifact_name, 'as_raw'):
-            artifact_name = as_raw(artifact_name)
-        location = self.location
-        if hasattr(location, 'as_raw'):
-            location = as_raw(location)
-        return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]}
-
-#
-# Utils
-#
-
-def get_function(context, presentation, value):
-    functions = context.presentation.presenter.functions
-    if isinstance(value, dict) and (len(value) == 1):
-        key = value.keys()[0]
-        if key in functions:
-            try:
-                return True, functions[key](context, presentation, value[key])
-            except InvalidValueError as e:
-                context.validation.report(issue=e.issue)
-                return True, None
-    return False, None
-
-def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
-    is_function, func = get_function(context, presentation, value)
-    if is_function:
-        return func
-    else:
-        value = str(value)
-    return value
-
-def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
-    if not isinstance(value, int):
-        try:
-            value = int(value)
-        except ValueError:
-            raise invalid_value(name, index, 'an integer', explanation, value,
-                                presentation._locator)
-    return value
-
-def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
-    if not isinstance(value, bool):
-        raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator)
-    return value
-
-def parse_modelable_entity_name(context, presentation, name, index, value):
-    value = parse_string_expression(context, presentation, name, index, 'the modelable entity name',
-                                    value)
-    if value == 'SELF':
-        the_self, _ = parse_self(presentation)
-        if the_self is None:
-            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
-                                                'a node template or a relationship template')
-    elif value == 'HOST':
-        _, self_variant = parse_self(presentation)
-        if self_variant != 'node_template':
-            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
-                                                'a node template')
-    elif (value == 'SOURCE') or (value == 'TARGET'):
-        _, self_variant = parse_self(presentation)
-        if self_variant != 'relationship_template':
-            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
-                                                'a relationship template')
-    elif isinstance(value, basestring):
-        node_templates = \
-            context.presentation.get('service_template', 'topology_template', 'node_templates') \
-            or {}
-        relationship_templates = \
-            context.presentation.get('service_template', 'topology_template',
-                                     'relationship_templates') \
-            or {}
-        if (value not in node_templates) and (value not in relationship_templates):
-            raise InvalidValueError(
-                'function "%s" parameter %d is not a valid modelable entity name: %s'
-                % (name, index + 1, safe_repr(value)),
-                locator=presentation._locator, level=Issue.BETWEEN_TYPES)
-    return value
-
-def parse_self(presentation):
-    from .templates import NodeTemplate, RelationshipTemplate
-    from .types import NodeType, RelationshipType
-
-    if presentation is None:
-        return None, None
-    elif isinstance(presentation, NodeTemplate) or isinstance(presentation, NodeType):
-        return presentation, 'node_template'
-    elif isinstance(presentation, RelationshipTemplate) \
-        or isinstance(presentation, RelationshipType):
-        return presentation, 'relationship_template'
-    else:
-        return parse_self(presentation._container)
-
-@dsl_specification('4.1', 'tosca-simple-1.0')
-def get_modelable_entities(context, container, 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)
-    elif modelable_entity_name == 'HOST':
-        return get_host(context, container)
-    elif modelable_entity_name == 'SOURCE':
-        return get_source(context, container)
-    elif modelable_entity_name == 'TARGET':
-        return get_target(context, container)
-    elif isinstance(modelable_entity_name, basestring):
-        node_templates = \
-            context.presentation.get('service_template', 'topology_template', 'node_templates') \
-            or {}
-        if modelable_entity_name in node_templates:
-            return [node_templates[modelable_entity_name]]
-        relationship_templates = \
-            context.presentation.get('service_template', 'topology_template',
-                                     'relationship_templates') \
-            or {}
-        if modelable_entity_name in relationship_templates:
-            return [relationship_templates[modelable_entity_name]]
-
-    raise InvalidValueError('function "get_property" could not find modelable entity "%s"'
-                            % modelable_entity_name,
-                            locator=locator)
-
-def get_self(context, container): # pylint: disable=unused-argument
-    """
-    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]
-
-def get_host(context, container): # 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).
-
-    Specifically, TOSCA orchestrators that encounter this keyword when evaluating the get_attribute
-    or :code:`get_property` functions SHALL search each node along the "HostedOn" relationship chain
-    starting at the immediate node that hosts the node where the function was evaluated (and then
-    that node's host node, and so forth) until a match is found or the "HostedOn" relationship chain
-    ends.
-    """
-
-    return []
-
-def get_source(context, container): # 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.
-    """
-
-    return []
-
-def get_target(context, container): # 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 invalid_modelable_entity_name(name, index, value, locator, contexts):
-    return InvalidValueError('function "%s" parameter %d can be "%s" only in %s'
-                             % (name, index + 1, value, contexts),
-                             locator=locator, level=Issue.FIELD)
-
-def invalid_value(name, index, the_type, explanation, value, locator):
-    return InvalidValueError(
-        'function "%s" %s is not %s%s: %s'
-        % (name, ('parameter %d' % (index + 1)) if index is not None else 'argument',
-           the_type, (', %s' % explanation) if explanation is not None else '', safe_repr(value)),
-        locator=locator, level=Issue.FIELD)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/misc.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py
index 42fc1ad..0fcd947 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/misc.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py
@@ -177,7 +177,7 @@ class Import(ExtensiblePresentation):
         """
 
 @has_fields
-@dsl_specification('3.5.2', 'tosca-simple-1.0')
+@dsl_specification('3.5.2-1', 'tosca-simple-1.0')
 class ConstraintClause(ExtensiblePresentation):
     """
     A constraint clause defines an operation along with one or more compatible values that can be

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/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 9576260..add0766 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -32,6 +32,8 @@ from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate,
                                   SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate,
                                   ArtifactTemplate, Metadata, Parameter, PluginSpecification)
 
+from .constraints import (Equal, GreaterThan, GreaterOrEqual, LessThan, LessOrEqual, InRange,
+                          ValidValues, Length, MinLength, MaxLength, Pattern)
 from ..data_types import coerce_value
 
 
@@ -164,6 +166,8 @@ def create_node_template_model(context, service_template, node_template):
 
     create_parameter_models_from_values(model.properties,
                                         node_template._get_property_values(context))
+    create_parameter_models_from_values(model.attributes,
+                                        node_template._get_attribute_default_values(context))
     create_interface_template_models(context, service_template, model.interface_templates,
                                      node_template._get_interfaces(context))
 
@@ -179,9 +183,9 @@ def create_node_template_model(context, service_template, node_template):
             model.capability_templates[capability_name] = \
                 create_capability_template_model(context, service_template, capability)
 
-    if model.target_node_template_constraints:
+    if node_template.node_filter:
         model.target_node_template_constraints = []
-        create_node_filter_constraint_lambdas(context, node_template.node_filter,
+        create_node_filter_constraints(context, node_template.node_filter,
                                               model.target_node_template_constraints)
 
     return model
@@ -271,9 +275,9 @@ def create_requirement_template_model(context, service_template, requirement):
 
     model = RequirementTemplate(**model)
 
-    if model.target_node_template_constraints:
+    if requirement.node_filter:
         model.target_node_template_constraints = []
-        create_node_filter_constraint_lambdas(context, requirement.node_filter,
+        create_node_filter_constraints(context, requirement.node_filter,
                                               model.target_node_template_constraints)
 
     relationship = requirement.relationship
@@ -557,17 +561,13 @@ def create_interface_template_models(context, service_template, interfaces, sour
                 interfaces[interface_name] = interface
 
 
-def create_node_filter_constraint_lambdas(context, node_filter, target_node_template_constraints):
-    if node_filter is None:
-        return
-
+def create_node_filter_constraints(context, node_filter, target_node_template_constraints):
     properties = node_filter.properties
     if properties is not None:
         for property_name, constraint_clause in properties:
-            func = create_constraint_clause_lambda(context, node_filter, constraint_clause,
-                                                   property_name, None)
-            if func is not None:
-                target_node_template_constraints.append(func)
+            constraint = create_constraint(context, node_filter, constraint_clause, property_name,
+                                           None)
+            target_node_template_constraints.append(constraint)
 
     capabilities = node_filter.capabilities
     if capabilities is not None:
@@ -575,129 +575,64 @@ def create_node_filter_constraint_lambdas(context, node_filter, target_node_temp
             properties = capability.properties
             if properties is not None:
                 for property_name, constraint_clause in properties:
-                    func = create_constraint_clause_lambda(context, node_filter, constraint_clause,
-                                                           property_name, capability_name)
-                    if func is not None:
-                        target_node_template_constraints.append(func)
+                    constraint = create_constraint(context, node_filter, constraint_clause,
+                                                   property_name, capability_name)
+                    target_node_template_constraints.append(constraint)
 
 
-def create_constraint_clause_lambda(context, node_filter, constraint_clause, property_name, # pylint: disable=too-many-return-statements
-                                    capability_name):
+def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements
     constraint_key = constraint_clause._raw.keys()[0]
-    the_type = constraint_clause._get_type(context)
 
-    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)
-        return constraint
-
-    def get_value(node_type):
-        if capability_name is not None:
-            capability = node_type.capability_templates.get(capability_name)
-            prop = capability.properties.get(property_name) if capability is not None else None
-            return prop.value if prop is not None else None
-        value = node_type.properties.get(property_name)
-        return value.value if value is not None else None
+    the_type = constraint_clause._get_type(context)
 
-    if constraint_key == 'equal':
-        def equal(node_type, container):
-            constraint = coerce_constraint(constraint_clause.equal, container)
-            value = get_value(node_type)
-            return value == constraint
+    def coerce_constraint(constraint):
+        if the_type is not None:
+            return coerce_value(context, node_filter, the_type, None, None, constraint,
+                                constraint_key) 
+        else:
+            return constraint
 
-        return equal
+    def coerce_constraints(constraints):
+        if the_type is not None:
+            return tuple(coerce_constraint(constraint) for constraint in constraints)
+        else:
+            return constraints
 
+    if constraint_key == 'equal':
+        return Equal(property_name, capability_name,
+                     coerce_constraint(constraint_clause.equal))
     elif constraint_key == 'greater_than':
-        def greater_than(node_type, container):
-            constraint = coerce_constraint(constraint_clause.greater_than, container)
-            value = get_value(node_type)
-            return value > constraint
-
-        return greater_than
-
+        return GreaterThan(property_name, capability_name,
+                           coerce_constraint(constraint_clause.greater_than))
     elif constraint_key == 'greater_or_equal':
-        def greater_or_equal(node_type, container):
-            constraint = coerce_constraint(constraint_clause.greater_or_equal, container)
-            value = get_value(node_type)
-            return value >= constraint
-
-        return greater_or_equal
-
+        return GreaterOrEqual(property_name, capability_name,
+                              coerce_constraint(constraint_clause.greater_or_equal))
     elif constraint_key == 'less_than':
-        def less_than(node_type, container):
-            constraint = coerce_constraint(constraint_clause.less_than, container)
-            value = get_value(node_type)
-            return value < constraint
-
-        return less_than
-
+        return LessThan(property_name, capability_name,
+                        coerce_constraint(constraint_clause.less_than))
     elif constraint_key == 'less_or_equal':
-        def less_or_equal(node_type, container):
-            constraint = coerce_constraint(constraint_clause.less_or_equal, container)
-            value = get_value(node_type)
-            return value <= constraint
-
-        return less_or_equal
-
+        return LessOrEqual(property_name, capability_name,
+                           coerce_constraint(constraint_clause.less_or_equal))
     elif constraint_key == 'in_range':
-        def in_range(node_type, container):
-            lower, upper = constraint_clause.in_range
-            lower, upper = coerce_constraint(lower, container), coerce_constraint(upper, container)
-            value = get_value(node_type)
-            if value < lower:
-                return False
-            if (upper != 'UNBOUNDED') and (value > upper):
-                return False
-            return True
-
-        return in_range
-
+        return InRange(property_name, capability_name,
+                       coerce_constraints(constraint_clause.in_range))
     elif constraint_key == 'valid_values':
-        def valid_values(node_type, container):
-            constraint = tuple(coerce_constraint(v, container)
-                               for v in constraint_clause.valid_values)
-            value = get_value(node_type)
-            return value in constraint
-
-        return valid_values
-
+        return ValidValues(property_name, capability_name,
+                           coerce_constraints(constraint_clause.valid_values))
     elif constraint_key == 'length':
-        def length(node_type, container): # pylint: disable=unused-argument
-            constraint = constraint_clause.length
-            value = get_value(node_type)
-            return len(value) == constraint
-
-        return length
-
+        return Length(property_name, capability_name,
+                      coerce_constraint(constraint_clause.length))
     elif constraint_key == 'min_length':
-        def min_length(node_type, container): # pylint: disable=unused-argument
-            constraint = constraint_clause.min_length
-            value = get_value(node_type)
-            return len(value) >= constraint
-
-        return min_length
-
+        return MinLength(property_name, capability_name,
+                         coerce_constraint(constraint_clause.min_length))
     elif constraint_key == 'max_length':
-        def max_length(node_type, container): # pylint: disable=unused-argument
-            constraint = constraint_clause.max_length
-            value = get_value(node_type)
-            return len(value) >= constraint
-
-        return max_length
-
+        return MaxLength(property_name, capability_name,
+                         coerce_constraint(constraint_clause.max_length))
     elif constraint_key == 'pattern':
-        def pattern(node_type, container): # pylint: disable=unused-argument
-            constraint = constraint_clause.pattern
-            # Note: the TOSCA 1.0 spec does not specify the regular expression grammar, so we will
-            # just use Python's
-            value = node_type.properties.get(property_name)
-            return re.match(constraint, str(value)) is not None
-
-        return pattern
-
-    return None
+        return Pattern(property_name, capability_name,
+                       coerce_constraint(constraint_clause.pattern))
+    else:
+        raise ValueError('malformed node_filter: {0}'.format(constraint_key))
 
 
 def split_prefix(string):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/modeling/constraints.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/constraints.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/constraints.py
new file mode 100644
index 0000000..46783fe
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/constraints.py
@@ -0,0 +1,137 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 re
+
+from aria.modeling.contraints import (NodeTemplateConstraint, NodeTemplateContainerHolder)
+from aria.modeling.functions import evaluate
+from aria.parser import dsl_specification
+
+
+@dsl_specification('3.5.2-2', 'tosca-simple-1.0')
+class EvaluatingNodeTemplateConstraint(NodeTemplateConstraint):
+    """
+    A version of :class:`NodeTemplateConstraint` with boilerplate initialization for TOSCA
+    constraints. 
+    """
+
+    def __init__(self, property_name, capability_name, constraint, plural=False):
+        self.property_name = property_name
+        self.capability_name = capability_name
+        self.constraint = constraint
+        self.plural = plural
+
+    def matches(self, source_node_template, target_node_template):
+        if self.capability_name is not None:
+            capability = target_node_template.capability_templates.get(self.capability_name)
+            value = capability.properties.get(self.property_name) \
+                if capability is not None else None
+        else:
+            value = target_node_template.properties.get(self.property_name)
+        value = value.value if value is not None else None
+
+        container_holder = NodeTemplateContainerHolder(source_node_template)
+        if self.plural:
+            constraints = []
+            for constraint in self.constraint:
+                evaluation = evaluate(constraint, container_holder)
+                if evaluation is not None:
+                    constraints.append(evaluation.value)
+                else:
+                    constraints.append(constraint)
+            constraint = constraints
+        else:
+            evaluation = evaluate(self.constraint, container_holder)
+            if evaluation is not None:
+                constraint = evaluation.value
+            else:
+                constraint = self.constraint
+
+        return self.matches_evaluated(value, constraint)
+
+    def matches_evaluated(self, value, constraint):
+        raise NotImplementedError
+
+
+class Equal(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return value == constraint
+
+
+class GreaterThan(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return value > constraint
+
+
+class GreaterOrEqual(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return value >= constraint
+
+
+class LessThan(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return value < constraint
+
+
+class LessOrEqual(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return value <= constraint
+
+
+class InRange(EvaluatingNodeTemplateConstraint):
+    def __init__(self, property_name, capability_name, constraint):
+        super(InRange, self).__init__(property_name, capability_name, constraint, plural=True)
+
+    def matches_evaluated(self, value, constraints):
+        lower, upper = constraints
+        if value < lower:
+            return False
+        if (upper != 'UNBOUNDED') and (value > upper):
+            return False
+        return True
+
+
+class ValidValues(EvaluatingNodeTemplateConstraint):
+    def __init__(self, property_name, capability_name, constraint):
+        super(ValidValues, self).__init__(property_name, capability_name, constraint, plural=True)
+
+    def matches_evaluated(self, value, constraints):
+        return value in constraints
+
+
+class Length(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return len(value) == constraint
+
+
+class MinLength(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return len(value) >= constraint
+
+
+class MaxLength(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        return len(value) <= constraint
+
+
+class Pattern(EvaluatingNodeTemplateConstraint):
+    def matches_evaluated(self, value, constraint):
+        # From TOSCA 1.0 3.5.2.1:
+        #
+        # "Note: Future drafts of this specification will detail the use of regular expressions and
+        # reference an appropriate standardized grammar."
+        #
+        # So we will just use Python's. 
+        return re.match(constraint, unicode(value)) is not None

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/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 99dcfea..fc86292 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
@@ -22,7 +22,7 @@ from aria.parser import dsl_specification
 from aria.parser.presentation import (get_locator, validate_primitive)
 from aria.parser.validation import Issue
 
-from ..functions import get_function
+from .functions import get_function
 from ..presentation.types import get_type_by_full_or_shorthand_name
 
 #
@@ -295,8 +295,12 @@ def apply_constraint_to_value(context, presentation, constraint_clause, value):
     elif constraint_key == 'pattern':
         constraint = constraint_clause.pattern
         try:
-            # Note: the TOSCA 1.0 spec does not specify the regular expression grammar, so we will
-            # just use Python's
+            # From TOSCA 1.0 3.5.2.1:
+            #
+            # "Note: Future drafts of this specification will detail the use of regular expressions
+            # and reference an appropriate standardized grammar."
+            #
+            # So we will just use Python's. 
             if re.match(constraint, str(value)) is None:
                 report('does not match regular expression', constraint)
                 return False
@@ -327,20 +331,20 @@ def get_data_type_value(context, presentation, field_name, type_name):
 
 PRIMITIVE_DATA_TYPES = {
     # YAML 1.2:
-    'tag:yaml.org,2002:str': str,
+    'tag:yaml.org,2002:str': unicode,
     'tag:yaml.org,2002:integer': int,
     'tag:yaml.org,2002:float': float,
     'tag:yaml.org,2002:bool': bool,
     'tag:yaml.org,2002:null': None.__class__,
 
     # TOSCA aliases:
-    'string': str,
+    'string': unicode,
     'integer': int,
     'float': float,
     'boolean': bool,
     'null': None.__class__}
 
-@dsl_specification('3.2.1', 'tosca-simple-1.0')
+@dsl_specification('3.2.1-1', 'tosca-simple-1.0')
 def get_primitive_data_type(type_name):
     """
     Many of the types we use in this profile are built-in types from the YAML 1.2 specification
@@ -371,6 +375,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

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
new file mode 100644
index 0000000..3d0f844
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
@@ -0,0 +1,675 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+from cStringIO import StringIO
+import re
+
+from aria.utils.collections import FrozenList
+from aria.utils.formatting import (as_raw, safe_repr, full_type_name)
+from aria.parser import dsl_specification
+from aria.parser.exceptions import InvalidValueError
+from aria.parser.validation import Issue
+from aria.modeling.exceptions import CannotEvaluateFunctionException
+from aria.modeling.models import (Node, NodeTemplate, Relationship, RelationshipTemplate)
+from aria.modeling.functions import (Function, Evaluation)
+
+
+#
+# Intrinsic
+#
+
+@dsl_specification('4.3.1', 'tosca-simple-1.0')
+class Concat(Function):
+    """
+    The :code:`concat` function is used to concatenate two or more string values within a TOSCA
+    service template.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if not isinstance(argument, list):
+            raise InvalidValueError(
+                'function "concat" argument must be a list of string expressions: {0}'
+                .format(safe_repr(argument)),
+                locator=self.locator)
+
+        string_expressions = []
+        for index, an_argument in enumerate(argument):
+            string_expressions.append(parse_string_expression(context, presentation, 'concat',
+                                                              index, None, an_argument))
+        self.string_expressions = FrozenList(string_expressions)
+
+    @property
+    def as_raw(self):
+        string_expressions = []
+        for string_expression in self.string_expressions:
+            if hasattr(string_expression, 'as_raw'):
+                string_expression = as_raw(string_expression)
+            string_expressions.append(string_expression)
+        return {'concat': string_expressions}
+
+    def __evaluate__(self, container_holder):
+        final = True
+        value = StringIO()
+        for e in self.string_expressions:
+            e, final = evaluate(e, final, container_holder)
+            if e is not None:
+                value.write(unicode(e))
+        value = value.getvalue()
+        return Evaluation(value, final)
+
+
+@dsl_specification('4.3.2', 'tosca-simple-1.0')
+class Token(Function):
+    """
+    The :code:`token` function is used within a TOSCA service template on a string to parse out
+    (tokenize) substrings separated by one or more token characters within a larger string.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if (not isinstance(argument, list)) or (len(argument) != 3):
+            raise InvalidValueError('function "token" argument must be a list of 3 parameters: {0}'
+                                    .format(safe_repr(argument)),
+                                    locator=self.locator)
+
+        self.string_with_tokens = parse_string_expression(context, presentation, 'token', 0,
+                                                          'the string to tokenize', argument[0])
+        self.string_of_token_chars = parse_string_expression(context, presentation, 'token', 1,
+                                                             'the token separator characters',
+                                                             argument[1])
+        self.substring_index = parse_int(context, presentation, 'token', 2,
+                                         'the 0-based index of the token to return', argument[2])
+
+    @property
+    def as_raw(self):
+        string_with_tokens = self.string_with_tokens
+        if hasattr(string_with_tokens, 'as_raw'):
+            string_with_tokens = as_raw(string_with_tokens)
+        string_of_token_chars = self.string_of_token_chars
+        if hasattr(string_of_token_chars, 'as_raw'):
+            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, container_holder):
+        final = True
+        string_with_tokens, final = evaluate(self.string_with_tokens, final, container_holder)
+        string_of_token_chars, final = evaluate(self.string_of_token_chars, final, container_holder)
+
+        if string_of_token_chars:
+            regex = '[' + ''.join(re.escape(c) for c in string_of_token_chars) + ']'
+            split = re.split(regex, string_with_tokens)
+            if self.substring_index < len(split):
+                return Evaluation(split[self.substring_index], final)
+
+        raise CannotEvaluateFunctionException()
+
+
+#
+# Property
+#
+
+@dsl_specification('4.4.1', 'tosca-simple-1.0')
+class GetInput(Function):
+    """
+    The :code:`get_input` function is used to retrieve the values of properties declared within the
+    inputs section of a TOSCA Service Template.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        self.input_property_name = parse_string_expression(context, presentation, 'get_input',
+                                                           None, 'the input property name',
+                                                           argument)
+
+        if isinstance(self.input_property_name, basestring):
+            the_input = context.presentation.get_from_dict('service_template', 'topology_template',
+                                                           'inputs', self.input_property_name)
+            if the_input is None:
+                raise InvalidValueError(
+                    'function "get_input" argument is not a valid input name: {0}'
+                    .format(safe_repr(argument)),
+                    locator=self.locator)
+
+    @property
+    def as_raw(self):
+        return {'get_input': as_raw(self.input_property_name)}
+
+    def __evaluate__(self, container_holder):
+        service = container_holder.service
+        if service is None:
+            raise CannotEvaluateFunctionException()
+            
+        value = service.inputs.get(self.input_property_name)
+        if value is not None:
+            value = value.value
+            return Evaluation(value, False) # We never return final evaluations!
+
+        raise InvalidValueError(
+            'function "get_input" argument is not a valid input name: {0}'
+            .format(safe_repr(self.input_property_name)),
+            locator=self.locator)
+
+
+@dsl_specification('4.4.2', 'tosca-simple-1.0')
+class GetProperty(Function):
+    """
+    The :code:`get_property` function is used to retrieve property values between modelable entities
+    defined in the same service template.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if (not isinstance(argument, list)) or (len(argument) < 2):
+            raise InvalidValueError(
+                'function "get_property" argument must be a list of at least 2 string expressions: '
+                '{0}'.format(safe_repr(argument)),
+                locator=self.locator)
+
+        self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
+                                                                 'get_property', 0, argument[0])
+        # The first of these will be tried as a req-or-cap name:
+        self.nested_property_name_or_index = argument[1:]
+
+    @property
+    def as_raw(self):
+        return {'get_property': [self.modelable_entity_name] + self.nested_property_name_or_index}
+
+    def __evaluate__(self, container_holder):
+        modelable_entities = get_modelable_entities(container_holder, 'get_property', self.locator,
+                                                    self.modelable_entity_name)
+        req_or_cap_name = self.nested_property_name_or_index[0]
+
+        for modelable_entity in modelable_entities:
+            properties = None
+            
+            if hasattr(modelable_entity, 'requirement_templates') \
+                and modelable_entity.requirement_templates \
+                and (req_or_cap_name in [v.name for v in modelable_entity.requirement_templates]):
+                for requirement_template in modelable_entity.requirement_templates:
+                    if requirement_template.name == req_or_cap_name:
+                        # First argument refers to a requirement
+                        # TODO: should follow to matched capability in other node...
+                        raise CannotEvaluateFunctionException()
+                        break
+                nested_property_name_or_index = self.nested_property_name_or_index[1:]
+            elif hasattr(modelable_entity, 'capability_templates') \
+                and modelable_entity.capability_templates \
+                and (req_or_cap_name in modelable_entity.capability_templates):
+                # First argument refers to a capability
+                properties = modelable_entity.capability_templates[req_or_cap_name].properties
+                nested_property_name_or_index = self.nested_property_name_or_index[1:]
+            else:
+                properties = modelable_entity.properties
+                nested_property_name_or_index = self.nested_property_name_or_index
+
+            evaluation = get_modelable_entity_parameter(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}"'
+            .format('.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
+            locator=self.locator)
+
+
+#
+# Attribute
+#
+
+@dsl_specification('4.5.1', 'tosca-simple-1.0')
+class GetAttribute(Function):
+    """
+    The :code:`get_attribute` function is used to retrieve the values of named attributes declared
+    by the referenced node or relationship template name.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if (not isinstance(argument, list)) or (len(argument) < 2):
+            raise InvalidValueError(
+                'function "get_attribute" argument must be a list of at least 2 string expressions:'
+                ' {0}'.format(safe_repr(argument)),
+                locator=self.locator)
+
+        self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
+                                                                 'get_attribute', 0, argument[0])
+        # The first of these will be tried as a req-or-cap name:
+        self.nested_attribute_name_or_index = argument[1:]
+
+    @property
+    def as_raw(self):
+        return {'get_attribute': [self.modelable_entity_name] + self.nested_attribute_name_or_index}
+
+    def __evaluate__(self, container_holder):
+        modelable_entities = get_modelable_entities(container_holder, 'get_attribute', 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
+            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
+#
+
+@dsl_specification('4.6.1', 'tosca-simple-1.0')
+class GetOperationOutput(Function):
+    """
+    The :code:`get_operation_output` function is used to retrieve the values of variables exposed /
+    exported from an interface operation.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if (not isinstance(argument, list)) or (len(argument) != 4):
+            raise InvalidValueError(
+                'function "get_operation_output" argument must be a list of 4 parameters: {0}'
+                .format(safe_repr(argument)),
+                locator=self.locator)
+
+        self.modelable_entity_name = parse_string_expression(context, presentation,
+                                                             'get_operation_output', 0,
+                                                             'modelable entity name', argument[0])
+        self.interface_name = parse_string_expression(context, presentation, 'get_operation_output',
+                                                      1, 'the interface name', argument[1])
+        self.operation_name = parse_string_expression(context, presentation, 'get_operation_output',
+                                                      2, 'the operation name', argument[2])
+        self.output_variable_name = parse_string_expression(context, presentation,
+                                                            'get_operation_output', 3,
+                                                            'the output name', argument[3])
+
+    @property
+    def as_raw(self):
+        interface_name = self.interface_name
+        if hasattr(interface_name, 'as_raw'):
+            interface_name = as_raw(interface_name)
+        operation_name = self.operation_name
+        if hasattr(operation_name, 'as_raw'):
+            operation_name = as_raw(operation_name)
+        output_variable_name = self.output_variable_name
+        if hasattr(output_variable_name, 'as_raw'):
+            output_variable_name = as_raw(output_variable_name)
+        return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name,
+                                         output_variable_name]}
+
+
+#
+# Navigation
+#
+
+@dsl_specification('4.7.1', 'tosca-simple-1.0')
+class GetNodesOfType(Function):
+    """
+    The :code:`get_nodes_of_type` function can be used to retrieve a list of all known instances of
+    nodes of the declared Node Type.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        self.node_type_name = parse_string_expression(context, presentation, 'get_nodes_of_type',
+                                                      None, 'the node type name', argument)
+
+        if isinstance(self.node_type_name, basestring):
+            node_types = context.presentation.get('service_template', 'node_types')
+            if (node_types is None) or (self.node_type_name not in node_types):
+                raise InvalidValueError(
+                    'function "get_nodes_of_type" argument is not a valid node type name: {0}'
+                    .format(safe_repr(argument)),
+                    locator=self.locator)
+
+    @property
+    def as_raw(self):
+        node_type_name = self.node_type_name
+        if hasattr(node_type_name, 'as_raw'):
+            node_type_name = as_raw(node_type_name)
+        return {'get_nodes_of_type': node_type_name}
+
+    def __evaluate__(self, container):
+        pass
+
+
+#
+# Artifact
+#
+
+@dsl_specification('4.8.1', 'tosca-simple-1.0')
+class GetArtifact(Function):
+    """
+    The :code:`get_artifact` function is used to retrieve artifact location between modelable
+    entities defined in the same service template.
+    """
+
+    def __init__(self, context, presentation, argument):
+        self.locator = presentation._locator
+
+        if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4):
+            raise InvalidValueError(
+                'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
+                .format(safe_repr(argument)),
+                locator=self.locator)
+
+        self.modelable_entity_name = parse_string_expression(context, presentation, 'get_artifact',
+                                                             0, 'modelable entity name',
+                                                             argument[0])
+        self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1,
+                                                     'the artifact name', argument[1])
+        self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
+                                                'the location or "LOCAL_FILE"', argument[2])
+        self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
+                                 argument[3])
+
+    @property
+    def as_raw(self):
+        artifact_name = self.artifact_name
+        if hasattr(artifact_name, 'as_raw'):
+            artifact_name = as_raw(artifact_name)
+        location = self.location
+        if hasattr(location, 'as_raw'):
+            location = as_raw(location)
+        return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]}
+
+
+#
+# Utils
+#
+
+def get_function(context, presentation, value):
+    functions = context.presentation.presenter.functions
+    if isinstance(value, dict) and (len(value) == 1):
+        key = value.keys()[0]
+        if key in functions:
+            try:
+                return True, functions[key](context, presentation, value[key])
+            except InvalidValueError as e:
+                context.validation.report(issue=e.issue)
+                return True, None
+    return False, None
+
+
+def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+    is_function, func = get_function(context, presentation, value)
+    if is_function:
+        return func
+    else:
+        value = str(value)
+    return value
+
+
+def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+    if not isinstance(value, int):
+        try:
+            value = int(value)
+        except ValueError:
+            raise invalid_value(name, index, 'an integer', explanation, value,
+                                presentation._locator)
+    return value
+
+
+def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+    if not isinstance(value, bool):
+        raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator)
+    return value
+
+
+def parse_modelable_entity_name(context, presentation, name, index, value):
+    value = parse_string_expression(context, presentation, name, index, 'the modelable entity name',
+                                    value)
+    if value == 'SELF':
+        the_self, _ = parse_self(presentation)
+        if the_self is None:
+            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
+                                                'a node template or a relationship template')
+    elif value == 'HOST':
+        _, self_variant = parse_self(presentation)
+        if self_variant != 'node_template':
+            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
+                                                'a node template')
+    elif (value == 'SOURCE') or (value == 'TARGET'):
+        _, self_variant = parse_self(presentation)
+        if self_variant != 'relationship_template':
+            raise invalid_modelable_entity_name(name, index, value, presentation._locator,
+                                                'a relationship template')
+    elif isinstance(value, basestring):
+        node_templates = \
+            context.presentation.get('service_template', 'topology_template', 'node_templates') \
+            or {}
+        relationship_templates = \
+            context.presentation.get('service_template', 'topology_template',
+                                     'relationship_templates') \
+            or {}
+        if (value not in node_templates) and (value not in relationship_templates):
+            raise InvalidValueError(
+                'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
+                .format(name, index + 1, safe_repr(value)),
+                locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+    return value
+
+
+def parse_self(presentation):
+    from ..types import (NodeType, RelationshipType)
+    from ..templates import (
+        NodeTemplate as NodeTemplatePresentation,
+        RelationshipTemplate as RelationshipTemplatePresentation
+    )
+
+    if presentation is None:
+        return None, None
+    elif isinstance(presentation, NodeTemplatePresentation) or isinstance(presentation, NodeType):
+        return presentation, 'node_template'
+    elif isinstance(presentation, RelationshipTemplatePresentation) \
+        or isinstance(presentation, RelationshipType):
+        return presentation, 'relationship_template'
+    else:
+        return parse_self(presentation._container)
+
+
+def evaluate(value, final, container_holder):
+    """
+    Calls ``__evaluate__`` and passes on ``final`` state.
+    """
+
+    if hasattr(value, '__evaluate__'):
+        value = value.__evaluate__(container_holder)
+        if not value.final:
+            final = False
+        return value.value, final
+    else:
+        return value, final
+
+
+@dsl_specification('4.1', 'tosca-simple-1.0')
+def get_modelable_entities(container_holder, name, 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(container_holder, name, locator)
+    elif modelable_entity_name == 'HOST':
+        return get_hosts(container_holder, name, locator)
+    elif modelable_entity_name == 'SOURCE':
+        return get_source(container_holder, name, locator)
+    elif modelable_entity_name == 'TARGET':
+        return get_target(container_holder, name, locator)
+    elif isinstance(modelable_entity_name, basestring):
+        modelable_entities = []
+
+        service = container_holder.service
+        if service is not None:
+            for node in service.nodes.itervalues():
+                if node.node_template.name == modelable_entity_name:
+                    modelable_entities.append(node)
+        else:
+            service_template = container_holder.service_template
+            if service_template is not None:
+                for node_template in service_template.node_templates.itervalues():
+                    if node_template.name == modelable_entity_name:
+                        modelable_entities.append(node_template)
+
+        if not modelable_entities:
+            raise CannotEvaluateFunctionException()
+
+        return modelable_entities
+
+    raise InvalidValueError('function "{0}" could not find modelable entity "{0}"'
+                            .format(name, modelable_entity_name),
+                            locator=locator)
+
+
+def get_self(container_holder, name, locator):
+    """
+    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.
+    """
+    
+    container = container_holder.container
+    if (not isinstance(container, Node)) and \
+        (not isinstance(container, NodeTemplate)) and \
+        (not isinstance(container, Relationship)) and \
+        (not isinstance(container, RelationshipTemplate)):
+        raise InvalidValueError('function "{0}" refers to "SELF" but it is not contained in '
+                                'a node or a relationship: {1}'.format(name,
+                                                                       full_type_name(container)),
+                                locator=locator)
+
+    return [container]
+
+
+def get_hosts(container_holder, name, locator):
+    """
+    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).
+
+    Specifically, TOSCA orchestrators that encounter this keyword when evaluating the get_attribute
+    or :code:`get_property` functions SHALL search each node along the "HostedOn" relationship chain
+    starting at the immediate node that hosts the node where the function was evaluated (and then
+    that node's host node, and so forth) until a match is found or the "HostedOn" relationship chain
+    ends.
+    """
+
+    container = container_holder.container
+    if (not isinstance(container, Node)) and (not isinstance(container, NodeTemplate)):
+        raise InvalidValueError('function "{0}" refers to "HOST" but it is not contained in '
+                                'a node: {1}'.format(name, full_type_name(container)),
+                                locator=locator)
+
+    if not isinstance(container, Node):
+        # NodeTemplate does not have "host"; we'll wait until instantiation
+        raise CannotEvaluateFunctionException()
+
+    host = container.host
+    if host is None:
+        # We might have a host later
+        raise CannotEvaluateFunctionException()
+
+    return [host]
+
+
+def get_source(container_holder, name, locator):
+    """
+    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.
+    """
+
+    container = container_holder.container
+    if (not isinstance(container, Relationship)) and \
+        (not isinstance(container, RelationshipTemplate)):
+        raise InvalidValueError('function "{0}" refers to "SOURCE" but it is not contained in '
+                                'a relationship: {1}'.format(name, full_type_name(container)),
+                                locator=locator)
+
+    if not isinstance(container, RelationshipTemplate):
+        # RelationshipTemplate does not have "source_node"; we'll wait until instantiation
+        raise CannotEvaluateFunctionException()
+
+    return [container.source_node]
+
+
+def get_target(container_holder, name, locator):
+    """
+    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.
+    """
+
+    container = container_holder.container
+    if (not isinstance(container, Relationship)) and \
+        (not isinstance(container, RelationshipTemplate)):
+        raise InvalidValueError('function "{0}" refers to "TARGET" but it is not contained in '
+                                'a relationship: {1}'.format(name, full_type_name(container)),
+                                locator=locator)
+
+    if not isinstance(container, RelationshipTemplate):
+        # RelationshipTemplate does not have "target_node"; we'll wait until instantiation
+        raise CannotEvaluateFunctionException()
+
+    return [container.target_node]
+
+
+def get_modelable_entity_parameter(modelable_entity, parameters, nested_parameter_name_or_index):
+    if not parameters:
+        return False, True, None
+
+    found = True
+    final = True
+    value = 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]
+            # We are not using "value.value" because we want to make sure to get "final"
+            value, final = evaluate(value._value, final, value)
+        else:
+            found = False
+            break
+
+    return Evaluation(value, final) if found else None
+
+
+def invalid_modelable_entity_name(name, index, value, locator, contexts):
+    return InvalidValueError('function "{0}" parameter {1:d} can be "{2}" only in {3}'
+                             .format(name, index + 1, value, contexts),
+                             locator=locator, level=Issue.FIELD)
+
+
+def invalid_value(name, index, the_type, explanation, value, locator):
+    return InvalidValueError(
+        'function "{0}" {1} is not {2}{3}: {4}'
+        .format(name,
+                'parameter {0:d}'.format(index + 1) if index is not None else 'argument',
+                the_type,
+                ', {0}'.format(explanation) if explanation is not None else '',
+                safe_repr(value)),
+        locator=locator, level=Issue.FIELD)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
index f61cb99..9c3ea42 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/properties.py
@@ -58,7 +58,8 @@ def get_inherited_property_definitions(context, presentation, field_name, for_pr
 # NodeTemplate, RelationshipTemplate, GroupTemplate, PolicyTemplate
 #
 
-def get_assigned_and_defined_property_values(context, presentation):
+def get_assigned_and_defined_property_values(context, presentation, field_name='property',
+                                             field_name_plural='properties'):
     """
     Returns the assigned property values while making sure they are defined in our type.
 
@@ -70,8 +71,9 @@ def get_assigned_and_defined_property_values(context, presentation):
     values = OrderedDict()
 
     the_type = presentation._get_type(context)
-    assignments = presentation.properties
-    definitions = the_type._get_properties(context) if the_type is not None else None
+    assignments = getattr(presentation, field_name_plural)
+    get_fn_name = '_get_{0}'.format(field_name_plural)
+    definitions = getattr(the_type, get_fn_name)(context) if the_type is not None else None
 
     # Fill in our assignments, but make sure they are defined
     if assignments:
@@ -80,14 +82,14 @@ def get_assigned_and_defined_property_values(context, presentation):
                 definition = definitions[name]
                 values[name] = coerce_property_value(context, value, definition, value.value)
             else:
-                context.validation.report('assignment to undefined property "%s" in "%s"'
-                                          % (name, presentation._fullname),
+                context.validation.report('assignment to undefined {0} "{1}" in "{2}"'
+                                          .format(field_name, name, presentation._fullname),
                                           locator=value._locator, level=Issue.BETWEEN_TYPES)
 
     # Fill in defaults from the definitions
     if definitions:
         for name, definition in definitions.iteritems():
-            if (values.get(name) is None) and (definition.default is not None):
+            if values.get(name) is None:
                 values[name] = coerce_property_value(context, presentation, definition,
                                                      definition.default)
 
@@ -181,7 +183,8 @@ def merge_property_definitions(context, presentation, property_definitions,
 def coerce_property_value(context, presentation, definition, value, aspect=None):
     the_type = definition._get_type(context) if definition is not None else None
     entry_schema = definition.entry_schema if definition is not None else None
-    constraints = definition._get_constraints(context) if definition is not None else None
+    constraints = definition._get_constraints(context) \
+        if ((definition is not None) and hasattr(definition, '_get_constraints')) else None
     value = coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect)
     if (the_type is not None) and hasattr(the_type, '_name'):
         type_name = the_type._name

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
index 6ff4384..e6a6fc7 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
@@ -304,8 +304,12 @@ def constraint_clause_pattern_validator(field, presentation, context):
     value = getattr(presentation, field.name)
     if value is not None:
         try:
-            # Note: the TOSCA 1.0 spec does not specify the regular expression grammar, so we will
-            # just use Python's
+            # From TOSCA 1.0 3.5.2.1:
+            #
+            # "Note: Future drafts of this specification will detail the use of regular expressions
+            # and reference an appropriate standardized grammar."
+            #
+            # So we will just use Python's. 
             re.compile(value)
         except re.error as e:
             context.validation.report(

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
index 96cc763..231a7d1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presenter.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
@@ -17,9 +17,9 @@ from aria.utils.collections import FrozenList, EMPTY_READ_ONLY_LIST
 from aria.utils.caching import cachedmethod
 from aria.parser.presentation import Presenter
 
-from .functions import (Concat, Token, GetInput, GetProperty, GetAttribute, GetOperationOutput,
-                        GetNodesOfType, GetArtifact)
 from .modeling import create_service_template_model
+from .modeling.functions import (Concat, Token, GetInput, GetProperty, GetAttribute,
+                                 GetOperationOutput, GetNodesOfType, GetArtifact)
 from .templates import ServiceTemplate
 
 class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/templates.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/templates.py b/extensions/aria_extension_tosca/simple_v1_0/templates.py
index 6860b72..c0f9f23 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/templates.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/templates.py
@@ -26,7 +26,7 @@ from .assignments import (PropertyAssignment, AttributeAssignment, RequirementAs
 from .definitions import ParameterDefinition
 from .filters import NodeFilter
 from .misc import (Description, MetaData, Repository, Import, SubstitutionMappings)
-from .modeling.properties import get_assigned_and_defined_property_values, get_parameter_values
+from .modeling.properties import (get_assigned_and_defined_property_values, get_parameter_values)
 from .modeling.interfaces import get_template_interfaces
 from .modeling.requirements import get_template_requirements
 from .modeling.capabilities import get_template_capabilities
@@ -160,6 +160,11 @@ class NodeTemplate(ExtensiblePresentation):
         return FrozenDict(get_assigned_and_defined_property_values(context, self))
 
     @cachedmethod
+    def _get_attribute_default_values(self, context):
+        return FrozenDict(get_assigned_and_defined_property_values(context, self,
+                                                                   'attribute', 'attributes'))
+
+    @cachedmethod
     def _get_requirements(self, context):
         return FrozenList(get_template_requirements(context, self))
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
----------------------------------------------------------------------
diff --git a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
index 349a166..8e80640 100644
--- a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
+++ b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
@@ -33,6 +33,7 @@ imports:
   - types/mongodb.yaml
   - types/nginx.yaml
   - aria-1.0
+
 dsl_definitions:
 
   default_openstack_credential: &DEFAULT_OPENSTACK_CREDENTIAL
@@ -94,8 +95,11 @@ topology_template:
           properties:
             unpack_credential:
               user: gigaspaces
-              token: { get_property: [ SELF, app_endpoint, protocol ] }
+              token: { get_attribute: [ SELF, tosca_id ] }
+              #token: { get_property: [ SELF, app_endpoint, protocol ] }
               #token: { get_property: [ HOST, flavor_name ] }
+              #token: { token: [ { get_property: [ HOST, flavor_name ] }, '.', 1 ] }
+              #token: { token: [ 'zero.one|two-three', '.|-', 3 ] }
       interfaces:
         Maintenance:
           enable: juju > charm.maintenance_on


[3/3] incubator-ariatosca git commit: ARIA-139 Support attributes

Posted by em...@apache.org.
ARIA-139 Support attributes

* Fully implement attribute support in parser
* New intrinsic function evaluation mechanism
* Implemented more intrinsic functions, including get_attribute
* Fix to one-on-one relationship back population
* Fixes to TOSCA use case examples
* Indirectly related: re-enabled node_filter mechanism and reworked
filter constraints in order to make them serializable


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/b940419b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/b940419b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/b940419b

Branch: refs/heads/ARIA-139-attributes
Commit: b940419b6a40db83d7c34d7f908c7b10d27863f5
Parents: eae44d0
Author: Tal Liron <ta...@gmail.com>
Authored: Wed Apr 19 20:07:33 2017 -0500
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue May 9 15:50:13 2017 -0500

----------------------------------------------------------------------
 aria/core.py                                    |   6 +-
 aria/modeling/contraints.py                     |  43 ++
 aria/modeling/functions.py                      | 104 ++-
 aria/modeling/mixins.py                         |   2 +-
 aria/modeling/relationship.py                   |  19 +-
 aria/modeling/service_common.py                 | 169 ++++-
 aria/modeling/service_instance.py               |  78 ++-
 aria/modeling/service_template.py               | 126 ++--
 aria/modeling/utils.py                          |  33 +-
 aria/parser/consumption/__init__.py             |  11 +-
 aria/parser/consumption/modeling.py             |   4 +-
 aria/utils/caching.py                           |  13 +-
 aria/utils/formatting.py                        |   4 +-
 .../block-storage-1/block-storage-1.yaml        |   2 +-
 .../block-storage-2/block-storage-2.yaml        |   2 +-
 .../block-storage-3/block-storage-3.yaml        |   2 +-
 .../block-storage-4/block-storage-4.yaml        |   2 +-
 .../block-storage-5/block-storage-5.yaml        |   6 +-
 .../block-storage-6/block-storage-6.yaml        |   4 +-
 .../use-cases/multi-tier-1/multi-tier-1.yaml    |  14 +-
 .../simple_v1_0/__init__.py                     |  12 +-
 .../simple_v1_0/functions.py                    | 536 ---------------
 .../aria_extension_tosca/simple_v1_0/misc.py    |   2 +-
 .../simple_v1_0/modeling/__init__.py            | 171 ++---
 .../simple_v1_0/modeling/constraints.py         | 137 ++++
 .../simple_v1_0/modeling/data_types.py          |  18 +-
 .../simple_v1_0/modeling/functions.py           | 675 +++++++++++++++++++
 .../simple_v1_0/modeling/properties.py          |  17 +-
 .../presentation/field_validators.py            |   8 +-
 .../simple_v1_0/presenter.py                    |   4 +-
 .../simple_v1_0/templates.py                    |   7 +-
 .../node-cellar/node-cellar.yaml                |   6 +-
 32 files changed, 1370 insertions(+), 867 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/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/b940419b/aria/modeling/contraints.py
----------------------------------------------------------------------
diff --git a/aria/modeling/contraints.py b/aria/modeling/contraints.py
new file mode 100644
index 0000000..1798b09
--- /dev/null
+++ b/aria/modeling/contraints.py
@@ -0,0 +1,43 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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.
+
+
+class NodeTemplateConstraint(object):
+    """
+    Used to constrain requirements for node templates.
+
+    Must be serializable.
+    """
+
+    def matches(self, source_node_template, target_node_template):
+        """
+        Returns true is the target matches the constraint for the source.
+        """
+        raise NotImplementedError
+
+
+class NodeTemplateContainerHolder(object):
+    """
+    Wrapper that allows using a :class:`aria.modeling.models.NodeTemplate` model directly as the
+    ``container_holder`` argument for :func:`evaluate`.
+    """
+
+    def __init__(self, node_template):
+        self.container = node_template
+        self.service = None
+
+    @property
+    def service_template(self):
+        return self.container.service_template

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/aria/modeling/functions.py
----------------------------------------------------------------------
diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py
index 02f4454..06fd19f 100644
--- a/aria/modeling/functions.py
+++ b/aria/modeling/functions.py
@@ -13,20 +13,118 @@
 # 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):
+    """
+    An evaluated :class:`Function` return value.
+    """
+
+    def __init__(self, value, final=False):
+        self.value = value
+        self.final = final
+
+
+def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches
+    """
+    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.
+
+    The ``container_holder`` argument should have three properties: ``container`` should return
+    the model that contains the value, ``service`` should return the containing
+    :class:`aria.modeling.models.Service` model or None, and ``service_template`` should return the
+    containing :class:`aria.modeling.models.ServiceTemplate` model or None.
+    """
+
+    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/b940419b/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/b940419b/aria/modeling/relationship.py
----------------------------------------------------------------------
diff --git a/aria/modeling/relationship.py b/aria/modeling/relationship.py
index 291d08c..40be5b2 100644
--- a/aria/modeling/relationship.py
+++ b/aria/modeling/relationship.py
@@ -146,13 +146,18 @@ def one_to_one(model_class,
                        false to disable
     :type back_populates: basestring|bool
     """
-    if back_populates is None:
-        back_populates = model_class.__tablename__
+    backref_kwargs = None
+    if back_populates is not NO_BACK_POP:
+        if back_populates is None:
+            back_populates = model_class.__tablename__
+        backref_kwargs = {'name': back_populates, 'uselist': False}
+        back_populates = None
 
     return _relationship(model_class,
                          other_table,
                          fk=fk,
                          back_populates=back_populates,
+                         backref_kwargs=backref_kwargs,
                          other_fk=other_fk)
 
 
@@ -190,6 +195,7 @@ def one_to_many(model_class,
     rel_kwargs.setdefault('cascade', 'all')
     if back_populates is None:
         back_populates = model_class.__tablename__
+
     return _relationship(
         model_class,
         child_table,
@@ -330,10 +336,11 @@ def _relationship(model_class,
 
     if backref_kwargs:
         assert back_populates is None
-        return relationship(lambda: _get_class_for_table(model_class, other_table_name),
-                            backref=backref(**backref_kwargs),
-                            **relationship_kwargs
-                           )
+        return relationship(
+            lambda: _get_class_for_table(model_class, other_table_name),
+            backref=backref(**backref_kwargs),
+            **relationship_kwargs
+        )
     else:
         if back_populates is not NO_BACK_POP:
             relationship_kwargs['back_populates'] = back_populates

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index 1188f34..e813465 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -15,6 +15,8 @@
 
 # pylint: disable=no-self-argument, no-member, abstract-method
 
+import datetime
+
 from sqlalchemy import (
     Column,
     Text,
@@ -22,20 +24,21 @@ from sqlalchemy import (
 )
 from sqlalchemy.ext.declarative import declared_attr
 
+from ..parser import dsl_specification
 from ..parser.consumption import ConsumptionContext
-from ..utils import collections, formatting, console
-from .mixins import InstanceModelMixin, TemplateModelMixin
+from ..utils import (collections, formatting, console, caching)
+from .mixins import (InstanceModelMixin, TemplateModelMixin)
 from . import (
     relationship,
-    utils
+    functions
 )
 
 
-class ParameterBase(TemplateModelMixin):
+class ParameterBase(TemplateModelMixin, caching.HasCachedMethods):
     """
-    Represents a typed value.
+    Represents a typed value. The value can contain nested intrinsic functions.
 
-    This model is used by both service template and service instance elements.
+    This model can be used as the ``container_holder`` argument for :func:`functions.evaluate`.
 
     :ivar name: Name
     :vartype name: basestring
@@ -50,8 +53,109 @@ class ParameterBase(TemplateModelMixin):
 
     name = Column(Text)
     type_name = Column(Text)
-    value = Column(PickleType)
     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
+
+    @property
+    @caching.cachedmethod
+    def container(self): # pylint: disable=too-many-return-statements,too-many-branches
+        """
+        The logical 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 abnormal, orphaned parameter.
+        """
+
+        from . import models
+
+        # Find first non-null relationship
+        value = None
+        for the_relationship in self.__mapper__.relationships:
+            v = getattr(self, the_relationship.key)
+            if v:
+                value = v[0]
+                break
+
+        # Convert to logical container
+
+        if isinstance(value, models.Operation):
+            value = value.interface
+        elif isinstance(value, models.OperationTemplate):
+            value = value.interface_template
+
+        if isinstance(value, models.Interface):
+            if value.node:
+                return value.node
+            elif value.group:
+                return value.group
+            elif value.relationship:
+                value = value.relationship
+        elif isinstance(value, models.InterfaceTemplate):
+            if value.node_template:
+                return value.node_template
+            elif value.group_template:
+                return value.group_template
+            elif value.relationship_template:
+                value = value.relationship_template
+
+        if isinstance(value, models.Relationship):
+            return value.source_node
+        elif isinstance(value, models.RelationshipTemplate):
+            return value.requirement_template.node_template
+        elif isinstance(value, models.Capability) or isinstance(value, models.Artifact):
+            return value.node
+        elif isinstance(value, models.CapabilityTemplate) \
+            or isinstance(value, models.ArtifactTemplate):
+            return value.node_template
+
+        if value is not None:
+            return value
+        else:
+            raise ValueError('orphaned parameter: 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):
@@ -65,47 +169,70 @@ 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:
-            self.value = utils.coerce_value(container, self.value,
-                                            report_issues)
+    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(self.value),
+                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(self.value)))
+                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
 
+    @dsl_specification('3.2.1-2', 'tosca-simple-1.0')
     @classmethod
     def wrap(cls, name, value, description=None):
         """
         Wraps an arbitrary value as a parameter. The type will be guessed via introspection.
 
+        For primitive types, we will prefer their TOSCA aliases. See the `TOSCA Simple Profile v1.0
+        cos01 specification <http://docs.oasis-open.org/tosca/TOSCA-Simple-Profile-YAML/v1.0/cos01
+        /TOSCA-Simple-Profile-YAML-v1.0-cos01.html#_Toc373867862>`__
+
         :param name: Parameter name
         :type name: basestring
         :param value: Parameter value
         :param description: Description (optional)
         :type description: basestring
         """
-        return cls(name=name,
-                   type_name=formatting.full_type_name(value)
-                   if value is not None else None,
-                   value=value,
-                   description=description)
+
+        from . import models
+        if value is None:
+            type_name = 'null'
+        elif isinstance(value, basestring):
+            type_name = 'string'
+        elif isinstance(value, int):
+            type_name = 'integer'
+        elif isinstance(value, float):
+            type_name = 'float'
+        elif isinstance(value, bool):
+            type_name = 'boolean'
+        elif isinstance(value, datetime.datetime):
+            type_name = 'timestamp'
+        else:
+            type_name = formatting.full_type_name(value)
+        return models.Parameter(name=name,
+                                type_name=type_name,
+                                value=value,
+                                description=description)
 
 
 class TypeBase(InstanceModelMixin):
@@ -188,7 +315,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):
@@ -237,7 +364,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/b940419b/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
index ad8e7ed..1efe1e1 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -251,16 +251,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()
@@ -513,6 +513,10 @@ class NodeBase(InstanceModelMixin):
     def properties(cls):
         return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name')
 
+    @declared_attr
+    def attributes(cls):
+        return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name')
+
     # endregion
 
     description = Column(Text)
@@ -646,6 +650,7 @@ class NodeBase(InstanceModelMixin):
             ('name', self.name),
             ('type_name', self.type.name),
             ('properties', formatting.as_raw_dict(self.properties)),
+            ('attributes', formatting.as_raw_dict(self.properties)),
             ('interfaces', formatting.as_raw_list(self.interfaces)),
             ('artifacts', formatting.as_raw_list(self.artifacts)),
             ('capabilities', formatting.as_raw_list(self.capabilities)),
@@ -664,17 +669,19 @@ class NodeBase(InstanceModelMixin):
         # TODO: validate that node template is of type?
 
         utils.validate_dict_values(self.properties)
+        utils.validate_dict_values(self.attributes)
         utils.validate_dict_values(self.interfaces)
         utils.validate_dict_values(self.artifacts)
         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.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()
@@ -683,6 +690,7 @@ class NodeBase(InstanceModelMixin):
             console.puts('Type: {0}'.format(context.style.type(self.type.name)))
             console.puts('Template: {0}'.format(context.style.node(self.node_template.name)))
             utils.dump_dict_values(self.properties, 'Properties')
+            utils.dump_dict_values(self.attributes, 'Attributes')
             utils.dump_interfaces(self.interfaces)
             utils.dump_dict_values(self.artifacts, 'Artifacts')
             utils.dump_dict_values(self.capabilities, 'Capabilities')
@@ -797,9 +805,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()
@@ -916,8 +924,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()
@@ -1017,8 +1025,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()
@@ -1121,7 +1129,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):
@@ -1311,9 +1319,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()
@@ -1451,8 +1459,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()
@@ -1598,9 +1606,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()
@@ -1765,8 +1773,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()
@@ -1905,8 +1913,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/b940419b/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index e3320fa..7a192a7 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -17,7 +17,6 @@
 
 from __future__ import absolute_import  # so we can import standard 'types'
 
-from types import FunctionType
 from datetime import datetime
 
 from sqlalchemy import (
@@ -25,7 +24,8 @@ from sqlalchemy import (
     Text,
     Integer,
     Boolean,
-    DateTime
+    DateTime,
+    PickleType
 )
 from sqlalchemy.ext.declarative import declared_attr
 from sqlalchemy.ext.associationproxy import association_proxy
@@ -347,16 +347,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()
@@ -427,7 +427,7 @@ class NodeTemplateBase(TemplateModelMixin):
     :ivar requirement_templates: Potential relationships with other nodes
     :vartype requirement_templates: [:class:`RequirementTemplate`]
     :ivar target_node_template_constraints: Constraints for filtering relationship targets
-    :vartype target_node_template_constraints: [:class:`FunctionType`]
+    :vartype target_node_template_constraints: [:class:`NodeTemplateConstraint`]
     :ivar service_template: Containing service template
     :vartype service_template: :class:`ServiceTemplate`
     :ivar group_templates: We are a member of these groups
@@ -504,6 +504,10 @@ class NodeTemplateBase(TemplateModelMixin):
         return relationship.many_to_many(cls, 'parameter', prefix='properties', dict_key='name')
 
     @declared_attr
+    def attributes(cls):
+        return relationship.many_to_many(cls, 'parameter', prefix='attributes', dict_key='name')
+
+    @declared_attr
     def interface_templates(cls):
         return relationship.one_to_many(cls, 'interface_template', dict_key='name')
 
@@ -525,12 +529,12 @@ class NodeTemplateBase(TemplateModelMixin):
     default_instances = Column(Integer, default=1)
     min_instances = Column(Integer, default=0)
     max_instances = Column(Integer, default=None)
-    target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
+    target_node_template_constraints = Column(PickleType)
 
-    def is_target_node_valid(self, target_node_template):
+    def is_target_node_template_valid(self, target_node_template):
         if self.target_node_template_constraints:
-            for node_type_constraint in self.target_node_template_constraints:
-                if not node_type_constraint(target_node_template, self):
+            for node_template_constraint in self.target_node_template_constraints:
+                if not node_template_constraint.matches(self, target_node_template):
                     return False
         return True
 
@@ -544,6 +548,7 @@ class NodeTemplateBase(TemplateModelMixin):
             ('min_instances', self.min_instances),
             ('max_instances', self.max_instances),
             ('properties', formatting.as_raw_dict(self.properties)),
+            ('attributes', formatting.as_raw_dict(self.properties)),
             ('interface_templates', formatting.as_raw_list(self.interface_templates)),
             ('artifact_templates', formatting.as_raw_list(self.artifact_templates)),
             ('capability_templates', formatting.as_raw_list(self.capability_templates)),
@@ -564,24 +569,34 @@ class NodeTemplateBase(TemplateModelMixin):
                            runtime_properties={},
                            node_template=self)
         utils.instantiate_dict(node, node.properties, self.properties)
+        utils.instantiate_dict(node, node.attributes, self.attributes)
         utils.instantiate_dict(node, node.interfaces, self.interface_templates)
         utils.instantiate_dict(node, node.artifacts, self.artifact_templates)
         utils.instantiate_dict(node, node.capabilities, self.capability_templates)
+
+        # Default attributes
+        if 'tosca_name' in node.attributes:
+            node.attributes['tosca_name'].value = self.name
+        if 'tosca_id' in node.attributes:
+            node.attributes['tosca_id'].value = name
+
         return node
 
     def validate(self):
         utils.validate_dict_values(self.properties)
+        utils.validate_dict_values(self.attributes)
         utils.validate_dict_values(self.interface_templates)
         utils.validate_dict_values(self.artifact_templates)
         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.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()
@@ -597,6 +612,7 @@ class NodeTemplateBase(TemplateModelMixin):
                 if self.max_instances is not None
                 else ' or more'))
             utils.dump_dict_values(self.properties, 'Properties')
+            utils.dump_dict_values(self.attributes, 'Attributes')
             utils.dump_interfaces(self.interface_templates)
             utils.dump_dict_values(self.artifact_templates, 'Artifact templates')
             utils.dump_dict_values(self.capability_templates, 'Capability templates')
@@ -720,9 +736,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()
@@ -851,8 +867,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()
@@ -945,8 +961,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()
@@ -1049,7 +1065,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):
@@ -1113,7 +1129,7 @@ class RequirementTemplateBase(TemplateModelMixin):
     :ivar target_capability_name: Name of capability in target node (optional)
     :vartype target_capability_name: basestring
     :ivar target_node_template_constraints: Constraints for filtering relationship targets
-    :vartype target_node_template_constraints: [:class:`FunctionType`]
+    :vartype target_node_template_constraints: [:class:`NodeTemplateConstraint`]
     :ivar relationship_template: Template for relationships (optional)
     :vartype relationship_template: :class:`RelationshipTemplate`
     :ivar node_template: Containing node template
@@ -1183,9 +1199,7 @@ class RequirementTemplateBase(TemplateModelMixin):
 
     @declared_attr
     def relationship_template(cls):
-        return relationship.one_to_one(cls,
-                                       'relationship_template',
-                                       back_populates=relationship.NO_BACK_POP)
+        return relationship.one_to_one(cls, 'relationship_template')
 
     # endregion
 
@@ -1215,18 +1229,18 @@ class RequirementTemplateBase(TemplateModelMixin):
     # endregion
 
     target_capability_name = Column(Text)
-    target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
+    target_node_template_constraints = Column(PickleType)
 
     def find_target(self, source_node_template):
         context = ConsumptionContext.get_thread_local()
 
         # We might already have a specific node template, so we'll just verify it
         if self.target_node_template is not None:
-            if not source_node_template.is_target_node_valid(self.target_node_template):
+            if not source_node_template.is_target_node_template_valid(self.target_node_template):
                 context.validation.report('requirement "{0}" of node template "{1}" is for node '
                                           'template "{2}" but it does not match constraints'.format(
                                               self.name,
-                                              self.target_node_template_name,
+                                              self.target_node_template.name,
                                               source_node_template.name),
                                           level=validation.Issue.BETWEEN_TYPES)
             if (self.target_capability_type is not None) \
@@ -1247,7 +1261,7 @@ class RequirementTemplateBase(TemplateModelMixin):
                 if self.target_node_type.get_descendant(target_node_template.type.name) is None:
                     continue
 
-                if not source_node_template.is_target_node_valid(target_node_template):
+                if not source_node_template.is_target_node_template_valid(target_node_template):
                     continue
 
                 target_node_capability = self.find_target_capability(source_node_template,
@@ -1284,9 +1298,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()
@@ -1417,9 +1431,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()
@@ -1543,8 +1557,8 @@ class CapabilityTemplateBase(TemplateModelMixin):
 
         # Apply requirement constraints
         if requirement.target_node_template_constraints:
-            for node_type_constraint in requirement.target_node_template_constraints:
-                if not node_type_constraint(target_node_template, source_node_template):
+            for node_template_constraint in requirement.target_node_template_constraints:
+                if not node_template_constraint.matches(source_node_template, target_node_template):
                     return False
 
         return True
@@ -1574,8 +1588,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()
@@ -1728,9 +1742,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()
@@ -1882,7 +1896,7 @@ class OperationTemplateBase(TemplateModelMixin):
                 plugin = None
                 implementation = None
         else:
-            # using the execution plugin
+            # Using the execution plugin
             plugin = None
             implementation = self.implementation
 
@@ -1903,8 +1917,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()
@@ -2051,8 +2065,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()
@@ -2128,7 +2142,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/b940419b/aria/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index 91d7b9c..c786965 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -19,9 +19,6 @@ from StringIO import StringIO
 
 from . import exceptions
 from ..parser.consumption import ConsumptionContext
-from ..parser.exceptions import InvalidValueError
-from ..parser.presentation import Value
-from ..utils.collections import OrderedDict
 from ..utils.console import puts
 from ..utils.type import validate_value_type
 
@@ -109,39 +106,17 @@ def _merge_and_validate_inputs(inputs, template_inputs):
     return merged_inputs
 
 
-def coerce_value(container, value, report_issues=False):
-    if isinstance(value, Value):
-        value = value.value
-
-    if isinstance(value, list):
-        return [coerce_value(container, v, report_issues) for v in value]
-    elif isinstance(value, dict):
-        return OrderedDict((k, coerce_value(container, v, report_issues))
-                           for k, v in value.iteritems())
-    elif hasattr(value, '_evaluate'):
-        context = ConsumptionContext.get_thread_local()
-        try:
-            value = value._evaluate(context, container)
-            value = coerce_value(container, value, report_issues)
-        except exceptions.CannotEvaluateFunctionException:
-            pass
-        except InvalidValueError as e:
-            if report_issues:
-                context.validation.report(e.issue)
-    return 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/b940419b/aria/parser/consumption/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/__init__.py b/aria/parser/consumption/__init__.py
index 8f6d2b6..76e73be 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'
+    'ValidateCapabilities',
+    'CoerceServiceInstanceValues'
 )

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/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/b940419b/aria/utils/caching.py
----------------------------------------------------------------------
diff --git a/aria/utils/caching.py b/aria/utils/caching.py
index 613ade6..c9e475a 100644
--- a/aria/utils/caching.py
+++ b/aria/utils/caching.py
@@ -65,22 +65,24 @@ class cachedmethod(object):  # pylint: disable=invalid-name
             return self.func(*args, **kwargs)
 
         instance = args[0]
-        cache = instance.get_method_cache()
+        if not hasattr(instance, '_method_cache'):
+            instance._method_cache = {}
+        method_cache = instance._method_cache
 
         key = (self.func, args[1:], frozenset(kwargs.items()))
 
         try:
             with self.lock:
-                return_value = cache[key]
+                return_value = method_cache[key]
                 self.hits += 1
         except KeyError:
             return_value = self.func(*args, **kwargs)
             with self.lock:
-                cache[key] = return_value
+                method_cache[key] = return_value
                 self.misses += 1
             # Another thread may override our cache entry here, so we need to read
             # it again to make sure all threads use the same return value
-            return_value = cache.get(key, return_value)
+            return_value = method_cache.get(key, return_value)
 
         return return_value
 
@@ -93,9 +95,6 @@ class HasCachedMethods(object):
     def __init__(self, method_cache=None):
         self._method_cache = method_cache or {}
 
-    def get_method_cache(self):
-        return self._method_cache
-
     @property
     def _method_cache_info(self):
         """

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/aria/utils/formatting.py
----------------------------------------------------------------------
diff --git a/aria/utils/formatting.py b/aria/utils/formatting.py
index b5e141d..75652a7 100644
--- a/aria/utils/formatting.py
+++ b/aria/utils/formatting.py
@@ -163,8 +163,8 @@ def as_raw(value):
             value = value()
     elif isinstance(value, list):
         value = list(value)
-        for i, _ in enumerate(value):
-            value[i] = as_raw(value[i])
+        for i, v in enumerate(value):
+            value[i] = as_raw(v)
     elif isinstance(value, dict):
         value = dict(value)
         for k, v in value.iteritems():

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-1/block-storage-1.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-1/block-storage-1.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-1/block-storage-1.yaml
index ff6dc92..b912fb2 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-1/block-storage-1.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-1/block-storage-1.yaml
@@ -65,4 +65,4 @@ topology_template:
       value: { get_attribute: [ my_server, private_address ] }
     volume_id:
       description: The volume id of the block storage instance.
-      value: { get_attribute: [ my_storage, volume_id ] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-2/block-storage-2.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-2/block-storage-2.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-2/block-storage-2.yaml
index 09c30a7..ac475cf 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-2/block-storage-2.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-2/block-storage-2.yaml
@@ -72,4 +72,4 @@ topology_template:
 
     volume_id:
       description: The volume id of the block storage instance.
-      value: { get_attribute: [ my_storage, volume_id ] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-3/block-storage-3.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-3/block-storage-3.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-3/block-storage-3.yaml
index 3018fe9..c3f183e 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-3/block-storage-3.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-3/block-storage-3.yaml
@@ -65,4 +65,4 @@ topology_template:
       value: { get_attribute: [ my_server, private_address ] }
     volume_id:
       description: The volume id of the block storage instance.
-      value: { get_attribute: [ my_storage, volume_id ] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-4/block-storage-4.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-4/block-storage-4.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-4/block-storage-4.yaml
index 0693ddd..e2bdb9f 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-4/block-storage-4.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-4/block-storage-4.yaml
@@ -93,4 +93,4 @@ topology_template:
       value: { get_attribute: [ my_web_app_tier_2, private_address ] }
     volume_id:
       description: The volume id of the block storage instance.
-      value: { get_attribute: [ my_storage, volume_id ] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-5/block-storage-5.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-5/block-storage-5.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-5/block-storage-5.yaml
index 5f5cf71..a0c2229 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-5/block-storage-5.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-5/block-storage-5.yaml
@@ -100,10 +100,10 @@ topology_template:
   outputs:
     private_ip_1:
       description: The private IP address of the application's first tier.
-      value: { get_attribute: [my_web_app_tier_1, private_address] }
+      value: { get_attribute: [ my_web_app_tier_1, private_address ] }
     private_ip_2:
       description: The private IP address of the application's second tier.
-      value: { get_attribute: [my_web_app_tier_2, private_address] }
+      value: { get_attribute: [ my_web_app_tier_2, private_address ] }
     volume_id:
       description: The volume id of the block storage instance.
-      value: { get_attribute: [my_storage, volume_id] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/block-storage-6/block-storage-6.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/block-storage-6/block-storage-6.yaml b/examples/tosca-simple-1.0/use-cases/block-storage-6/block-storage-6.yaml
index 808245b..534884a 100644
--- a/examples/tosca-simple-1.0/use-cases/block-storage-6/block-storage-6.yaml
+++ b/examples/tosca-simple-1.0/use-cases/block-storage-6/block-storage-6.yaml
@@ -96,7 +96,7 @@ topology_template:
       value: { get_attribute: [ my_server2, private_address ] }
     volume_id_1:
       description: The volume id of the first block storage instance.
-      value: { get_attribute: [my_storage, volume_id] }
+      value: { get_property: [ my_storage, volume_id ] } # ARIA NOTE: wrong in spec
     volume_id_2:
       description: The volume id of the second block storage instance.
-      value: { get_attribute: [ my_storage2, volume_id ] }
+      value: { get_property: [ my_storage2, volume_id ] } # ARIA NOTE: wrong in spec

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/examples/tosca-simple-1.0/use-cases/multi-tier-1/multi-tier-1.yaml
----------------------------------------------------------------------
diff --git a/examples/tosca-simple-1.0/use-cases/multi-tier-1/multi-tier-1.yaml b/examples/tosca-simple-1.0/use-cases/multi-tier-1/multi-tier-1.yaml
index 3485e49..50401ec 100644
--- a/examples/tosca-simple-1.0/use-cases/multi-tier-1/multi-tier-1.yaml
+++ b/examples/tosca-simple-1.0/use-cases/multi-tier-1/multi-tier-1.yaml
@@ -59,7 +59,7 @@ topology_template:
              implementation: scripts/nodejs/configure.sh
              inputs:
                github_url: { get_property: [ SELF, github_url ] }
-               mongodb_ip: { get_attribute: [mongo_server, private_address] }
+               mongodb_ip: { get_attribute: [ mongo_server, private_address ] }
            start: scripts/nodejs/start.sh
 
     nodejs:
@@ -90,7 +90,7 @@ topology_template:
           configure:
             implementation: scripts/mongodb/config.sh
             inputs:
-              mongodb_ip: { get_attribute: [mongo_server, ip_address] }
+              mongodb_ip: { get_attribute: [ mongo_server, private_address ] } # ARIA NOTE: wrong in spec
           start: scripts/mongodb/start.sh
 
     elasticsearch:
@@ -115,7 +115,7 @@ topology_template:
                   pre_configure_source:
                     implementation: python/logstash/configure_elasticsearch.py
                     inputs:
-                      elasticsearch_ip: { get_attribute: [elasticsearch_server, ip_address] }
+                      elasticsearch_ip: { get_attribute: [ elasticsearch_server, private_address ] } # ARIA NOTE: wrong in spec
       interfaces:
         Standard: # ARIA NOTE: wrong in spec
           create: scripts/lostash/create.sh
@@ -133,8 +133,8 @@ topology_template:
           configure:
             implementation: scripts/kibana/config.sh
             inputs:
-              elasticsearch_ip: { get_attribute: [ elasticsearch_server, ip_address ] }
-              kibana_ip: { get_attribute: [ kibana_server, ip_address ] }
+              elasticsearch_ip: { get_attribute: [ elasticsearch_server, private_address ] } # ARIA NOTE: wrong in spec
+              kibana_ip: { get_attribute: [ kibana_server, private_address ] } # ARIA NOTE: wrong in spec
           start: scripts/kibana/start.sh
 
     app_collectd:
@@ -155,7 +155,7 @@ topology_template:
           configure:
             implementation: python/collectd/config.py
             inputs:
-              logstash_ip: { get_attribute: [ logstash_server, ip_address ] }
+              logstash_ip: { get_attribute: [ logstash_server, private_address ] } # ARIA NOTE: wrong in spec
           start: scripts/collectd/start.sh
 
     app_rsyslog:
@@ -176,7 +176,7 @@ topology_template:
           configure:
             implementation: scripts/rsyslog/config.sh
             inputs:
-              logstash_ip: { get_attribute: [ logstash_server, ip_address ] }
+              logstash_ip: { get_attribute: [ logstash_server, private_address ] } # ARIA NOTE: wrong in spec
           start: scripts/rsyslog/start.sh
 
     app_server:

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b940419b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
index 29df362..d701a1d 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
@@ -30,8 +30,6 @@ from .types import (ArtifactType, DataType, CapabilityType, InterfaceType, Relat
                     NodeType, GroupType, PolicyType)
 from .data_types import (Timestamp, Version, Range, List, Map, ScalarSize, ScalarTime,
                          ScalarFrequency)
-from .functions import (Concat, Token, GetInput, GetProperty, GetAttribute, GetOperationOutput,
-                        GetNodesOfType, GetArtifact)
 
 MODULES = (
     'modeling',
@@ -89,12 +87,4 @@ __all__ = (
     'Map',
     'ScalarSize',
     'ScalarTime',
-    'ScalarFrequency',
-    'Concat',
-    'Token',
-    'GetInput',
-    'GetProperty',
-    'GetAttribute',
-    'GetOperationOutput',
-    'GetNodesOfType',
-    'GetArtifact')
+    'ScalarFrequency')
\ No newline at end of file