You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ariatosca.apache.org by mx...@apache.org on 2017/08/09 12:33:44 UTC

[1/3] incubator-ariatosca git commit: ARIA-174 Refactor instantiation phase [Forced Update!]

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-174-Refactor-instantiation-phase b7f7ff7a8 -> 4ad587422 (forced update)


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/topology.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/topology.py b/aria/orchestrator/topology/topology.py
new file mode 100644
index 0000000..8ee33d1
--- /dev/null
+++ b/aria/orchestrator/topology/topology.py
@@ -0,0 +1,223 @@
+# 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 ...parser.validation import issue
+from ...modeling import models
+from ...utils import console
+from . import (
+    template_handler,
+    instance_handler,
+    common
+)
+
+
+class Topology(issue.ReporterMixin):
+
+    _init_map = {
+        models.ServiceTemplate: models.Service,
+        models.ArtifactTemplate: models.Artifact,
+        models.CapabilityTemplate: models.Capability,
+        models.GroupTemplate: models.Group,
+        models.InterfaceTemplate: models.Interface,
+        models.NodeTemplate: models.Node,
+        models.PolicyTemplate: models.Policy,
+        models.SubstitutionTemplate: models.Substitution,
+        models.RelationshipTemplate: models.Relationship,
+        models.OperationTemplate: models.Operation,
+        models.SubstitutionTemplateMapping: models.SubstitutionMapping,
+
+        # Common
+        models.Metadata: models.Metadata,
+        models.Attribute: models.Attribute,
+        models.Property: models.Property,
+        models.Input: models.Input,
+        models.Output: models.Output,
+        models.Configuration: models.Configuration,
+        models.Argument: models.Argument,
+        models.Type: models.Type
+    }
+
+    def __init__(self, *args, **kwargs):
+        super(Topology, self).__init__(*args, **kwargs)
+        self._model_cls_to_handler = dict(self._init_handlers(instance_handler),
+                                          **self._init_handlers(template_handler))
+
+    @staticmethod
+    def _init_handlers(module_):
+        """
+        Register handlers from a handler module to the models
+
+        :param module_: The module to look for handlers
+        :return: a dict where the key is the models class, and the value is the handler class
+        associated with it from the provided module
+        """
+        handlers = {}
+        for attribute_name in dir(module_):
+            if attribute_name.startswith('_'):
+                continue
+            attribute = getattr(module_, attribute_name)
+            if isinstance(attribute, type) and issubclass(attribute, common.HandlerBase):
+                handlers[getattr(models, attribute_name)] = attribute
+        return handlers
+
+    def instantiate(self, model, **kwargs):
+        """
+        instantiate the provided model
+
+        :param model:
+        :param kwargs:
+        :return:
+        """
+        if isinstance(model, dict):
+            return dict((name, self.instantiate(value, **kwargs))
+                        for name, value in model.iteritems())
+        elif isinstance(model, list):
+            return list(self.instantiate(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            model_instance_cls = self._init_map[model.__class__]
+            return _handler(self, model).instantiate(model_instance_cls, **kwargs)
+
+    def validate(self, model, **kwargs):
+        if isinstance(model, dict):
+            return self.validate(model.values(), **kwargs)
+        elif isinstance(model, list):
+            return all(self.validate(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            return _handler(self, model).validate(**kwargs)
+
+    def dump(self, model, out_stream=None, title=None, **kwargs):
+        out_stream = out_stream or console.TopologyStylizer()
+
+        # if model is empty, no need to print out the section name
+        if model and title:
+            out_stream.write('{0}:'.format(title))
+
+        if isinstance(model, dict):
+            if str(out_stream):
+                with out_stream.indent():
+                    return self.dump(model.values(), out_stream=out_stream, **kwargs)
+            else:
+                return self.dump(model.values(), out_stream=out_stream, **kwargs)
+
+        elif isinstance(model, list):
+            for value in model:
+                self.dump(value, out_stream=out_stream, **kwargs)
+
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            _handler(self, model).dump(out_stream=out_stream, **kwargs)
+
+        return out_stream
+
+    def dump_graph(self, service):
+        out_stream = console.TopologyStylizer()
+        for node in service.nodes.itervalues():
+            if not node.inbound_relationships:
+                self._dump_graph_node(out_stream, node)
+        return out_stream
+
+    def _dump_graph_node(self, out_stream, node, capability=None):
+        out_stream.write(out_stream.node_style(node.name))
+        if capability is not None:
+            out_stream.write('{0} ({1})'.format(out_stream.property_style(capability.name),
+                                                out_stream.type_style(capability.type.name)))
+        if node.outbound_relationships:
+            with out_stream.indent():
+                for relationship_model in node.outbound_relationships:
+                    styled_relationship_name = out_stream.property_style(relationship_model.name)
+                    if relationship_model.type is not None:
+                        out_stream.write('-> {0} ({1})'.format(
+                            styled_relationship_name,
+                            out_stream.type_style(relationship_model.type.name)))
+                    else:
+                        out_stream.write('-> {0}'.format(styled_relationship_name))
+                    with out_stream.indent(3):
+                        self._dump_graph_node(out_stream,
+                                              relationship_model.target_node,
+                                              relationship_model.target_capability)
+
+    def coerce(self, model, **kwargs):
+        if isinstance(model, dict):
+            return self.coerce(model.values(), **kwargs)
+        elif isinstance(model, list):
+            return all(self.coerce(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            return _handler(self, model).coerce(**kwargs)
+
+    def dump_types(self, service_template, out_stream=None):
+        out_stream = out_stream or console.TopologyStylizer()
+        self.dump(service_template.node_types, out_stream, 'Node types')
+        self.dump(service_template.group_types, out_stream, 'Group types')
+        self.dump(service_template.capability_types, out_stream, 'Capability types')
+        self.dump(service_template.relationship_types, out_stream, 'Relationship types')
+        self.dump(service_template.policy_types, out_stream, 'Policy types')
+        self.dump(service_template.artifact_types, out_stream, 'Artifact types')
+        self.dump(service_template.interface_types, out_stream, 'Interface types')
+
+        return out_stream
+
+    def satisfy_requirements(self, model, **kwargs):
+        if isinstance(model, dict):
+            return self.satisfy_requirements(model.values(), **kwargs)
+        elif isinstance(model, list):
+            return all(self.satisfy_requirements(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            return _handler(self, model).satisfy_requirements(**kwargs)
+
+    def validate_capabilities(self, model, **kwargs):
+        if isinstance(model, dict):
+            return self.validate_capabilities(model.values(), **kwargs)
+        elif isinstance(model, list):
+            return all(self.validate_capabilities(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            return _handler(self, model).validate_capabilities(**kwargs)
+
+    def _find_host(self, node):
+        if node.type.role == 'host':
+            return node
+
+        def target_has_role(rel, role):
+            return (rel.target_capability is not None and
+                    rel.target_capability.type.role == role)
+
+        for outbound_relationship in node.outbound_relationships:
+            if target_has_role(outbound_relationship, 'host'):
+                host = self._find_host(outbound_relationship.target_node)
+                if host is not None:
+                    return host
+        for inbound_relationship in node.inbound_relationships:
+            if target_has_role(inbound_relationship, 'feature'):
+                host = self._find_host(inbound_relationship.source_node)
+                if host is not None:
+                    return host
+        return None
+
+    def assign_hosts(self, service):
+        for node in service.nodes.values():
+            node.host = self._find_host(node)
+
+    def configure_operations(self, model, **kwargs):
+        if isinstance(model, dict):
+            return self.configure_operations(model.values(), **kwargs)
+        elif isinstance(model, list):
+            return all(self.configure_operations(value, **kwargs) for value in model)
+        elif model is not None:
+            _handler = self._model_cls_to_handler[model.__class__]
+            return _handler(self, model).configure_operations(**kwargs)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/utils.py b/aria/orchestrator/topology/utils.py
new file mode 100644
index 0000000..ec74391
--- /dev/null
+++ b/aria/orchestrator/topology/utils.py
@@ -0,0 +1,48 @@
+# 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 copy import deepcopy
+
+
+def deepcopy_with_locators(value):
+    """
+    Like :func:`deepcopy`, but also copies over locators.
+    """
+
+    res = deepcopy(value)
+    copy_locators(res, value)
+    return res
+
+
+def copy_locators(target, source):
+    """
+    Copies over ``_locator`` for all elements, recursively.
+
+    Assumes that target and source have exactly the same list/dict structure.
+    """
+
+    locator = getattr(source, '_locator', None)
+    if locator is not None:
+        try:
+            setattr(target, '_locator', locator)
+        except AttributeError:
+            pass
+
+    if isinstance(target, list) and isinstance(source, list):
+        for i, _ in enumerate(target):
+            copy_locators(target[i], source[i])
+    elif isinstance(target, dict) and isinstance(source, dict):
+        for k, v in target.items():
+            copy_locators(v, source[k])

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/workflow_runner.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflow_runner.py b/aria/orchestrator/workflow_runner.py
index 0eac61c..4dbf29b 100644
--- a/aria/orchestrator/workflow_runner.py
+++ b/aria/orchestrator/workflow_runner.py
@@ -141,9 +141,8 @@ class WorkflowRunner(object):
                                                      supplied_inputs=inputs or {})
         modeling_utils.validate_required_inputs_are_supplied(declared_inputs=workflow_inputs,
                                                              supplied_inputs=inputs or {})
-        execution.inputs = modeling_utils.merge_parameter_values(inputs,
-                                                                 workflow_inputs,
-                                                                 model_cls=models.Input)
+        execution.inputs = modeling_utils.merge_parameter_values(
+            inputs, workflow_inputs, model_cls=models.Input)
         # TODO: these two following calls should execute atomically
         self._validate_no_active_executions(execution)
         self._model_storage.execution.put(execution)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index ec96d27..6ce4a00 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -137,14 +137,13 @@ class OperationTask(BaseTask):
         operation = self.actor.interfaces[self.interface_name].operations[self.operation_name]
         self.plugin = operation.plugin
         self.function = operation.function
-        self.arguments = modeling_utils.merge_parameter_values(arguments,
-                                                               operation.arguments,
-                                                               model_cls=models.Argument)
+        self.arguments = modeling_utils.merge_parameter_values(arguments, operation.arguments)
 
         actor = self.actor
         if hasattr(actor, '_wrapped'):
             # Unwrap instrumented model
             actor = actor._wrapped
+
         if isinstance(actor, models.Node):
             self._context_cls = context.operation.NodeOperationContext
         elif isinstance(actor, models.Relationship):

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/consumption/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/__init__.py b/aria/parser/consumption/__init__.py
index bd4b29c..f9caf5f 100644
--- a/aria/parser/consumption/__init__.py
+++ b/aria/parser/consumption/__init__.py
@@ -20,7 +20,6 @@ Consumption package.
    :nosignatures:
 
    aria.parser.consumption.ConsumptionContext
-   aria.parser.consumption.Style
 
 Consumers
 ---------
@@ -47,7 +46,6 @@ Consumers
 
 from .exceptions import ConsumerException
 from .context import ConsumptionContext
-from .style import Style
 from .consumer import (
     Consumer,
     ConsumerChain
@@ -70,7 +68,6 @@ from .inputs import Inputs
 __all__ = (
     'ConsumerException',
     'ConsumptionContext',
-    'Style',
     'Consumer',
     'ConsumerChain',
     'Read',

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/consumption/consumer.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/consumer.py b/aria/parser/consumption/consumer.py
index 4f4c614..878a161 100644
--- a/aria/parser/consumption/consumer.py
+++ b/aria/parser/consumption/consumer.py
@@ -27,6 +27,9 @@ class Consumer(object):
     """
 
     def __init__(self, context):
+        from ...orchestrator import topology
+
+        self.topology = topology.Topology()
         self.context = context
 
     def consume(self):
@@ -73,6 +76,10 @@ class ConsumerChain(Consumer):
                     handle_exception(consumer, e)
                 else:
                     raise e
+
+            if consumer.topology.has_issues:
+                self.context.validation.extend_issues(consumer.topology.issues)
+
             if self.context.validation.has_issues:
                 break
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/consumption/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/context.py b/aria/parser/consumption/context.py
index 6fa61f4..9164984 100644
--- a/aria/parser/consumption/context.py
+++ b/aria/parser/consumption/context.py
@@ -16,12 +16,12 @@
 import sys
 import threading
 
+from ...utils import console
 from ..validation import ValidationContext
 from ..loading import LoadingContext
 from ..reading import ReadingContext
 from ..presentation import PresentationContext
 from ..modeling import ModelingContext
-from .style import Style
 
 
 _thread_locals = threading.local()
@@ -58,12 +58,12 @@ class ConsumptionContext(object):
     def __init__(self, set_thread_local=True):
         self.args = []
         self.out = sys.stdout
-        self.style = Style()
         self.validation = ValidationContext()
         self.loading = LoadingContext()
         self.reading = ReadingContext()
         self.presentation = PresentationContext()
         self.modeling = ModelingContext()
+        self.style = console.TopologyStylizer()
 
         if set_thread_local:
             self.set_thread_local()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/consumption/modeling.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/modeling.py b/aria/parser/consumption/modeling.py
index 44027b9..221b308 100644
--- a/aria/parser/consumption/modeling.py
+++ b/aria/parser/consumption/modeling.py
@@ -13,8 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from ...utils.formatting import json_dumps, yaml_dumps
 from .consumer import Consumer, ConsumerChain
+from ...utils.formatting import json_dumps, yaml_dumps
 
 
 class DeriveServiceTemplate(Consumer):
@@ -42,7 +42,7 @@ class CoerceServiceTemplateValues(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.template.coerce_values(True)
+        self.topology.coerce(self.context.modeling.template, report_issues=True)
 
 
 class ValidateServiceTemplate(Consumer):
@@ -51,7 +51,7 @@ class ValidateServiceTemplate(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.template.validate()
+        self.topology.validate(self.context.modeling.template)
 
 
 class ServiceTemplate(ConsumerChain):
@@ -74,7 +74,7 @@ class ServiceTemplate(ConsumerChain):
             raw = self.context.modeling.template_as_raw
             self.context.write(json_dumps(raw, indent=indent))
         else:
-            self.context.modeling.template.dump()
+            self.context.write(self.topology.dump(self.context.modeling.template))
 
 
 class Types(Consumer):
@@ -92,7 +92,7 @@ class Types(Consumer):
             raw = self.context.modeling.types_as_raw
             self.context.write(json_dumps(raw, indent=indent))
         else:
-            self.context.modeling.template.dump_types()
+            self.topology.dump_types(self.context, self.context.modeling.template)
 
 
 class InstantiateServiceInstance(Consumer):
@@ -105,9 +105,10 @@ class InstantiateServiceInstance(Consumer):
             self.context.validation.report('InstantiateServiceInstance consumer: missing service '
                                            'template')
             return
-
-        self.context.modeling.template.instantiate(None, None,
-                                                   inputs=dict(self.context.modeling.inputs))
+        self.context.modeling.instance = self.topology.instantiate(
+            self.context.modeling.template,
+            inputs=dict(self.context.modeling.inputs)
+        )
 
 
 class CoerceServiceInstanceValues(Consumer):
@@ -116,7 +117,7 @@ class CoerceServiceInstanceValues(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.coerce_values(True)
+        self.topology.coerce(self.context.modeling.instance, report_issues=True)
 
 
 class ValidateServiceInstance(Consumer):
@@ -125,7 +126,7 @@ class ValidateServiceInstance(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.validate()
+        self.topology.validate(self.context.modeling.instance)
 
 
 class SatisfyRequirements(Consumer):
@@ -134,7 +135,7 @@ class SatisfyRequirements(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.satisfy_requirements()
+        self.topology.satisfy_requirements(self.context.modeling.instance)
 
 
 class ValidateCapabilities(Consumer):
@@ -143,7 +144,7 @@ class ValidateCapabilities(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.validate_capabilities()
+        self.topology.validate_capabilities(self.context.modeling.instance)
 
 
 class FindHosts(Consumer):
@@ -152,7 +153,7 @@ class FindHosts(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.find_hosts()
+        self.topology.assign_hosts(self.context.modeling.instance)
 
 
 class ConfigureOperations(Consumer):
@@ -161,7 +162,7 @@ class ConfigureOperations(Consumer):
     """
 
     def consume(self):
-        self.context.modeling.instance.configure_operations()
+        self.topology.configure_operations(self.context.modeling.instance)
 
 
 class ServiceInstance(ConsumerChain):
@@ -193,4 +194,5 @@ class ServiceInstance(ConsumerChain):
             raw = self.context.modeling.instance_as_raw
             self.context.write(json_dumps(raw, indent=indent))
         else:
-            self.context.modeling.instance.dump()
+            str_rep = self.topology.dump(self.context.modeling.instance)
+            self.context.write(str_rep)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/consumption/style.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/style.py b/aria/parser/consumption/style.py
deleted file mode 100644
index 1b52cd3..0000000
--- a/aria/parser/consumption/style.py
+++ /dev/null
@@ -1,54 +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 ...utils.console import Colored, indent
-from ...utils.formatting import safe_repr
-
-
-class Style(object):
-    def __init__(self, indentation=2):
-        self.indentation = indentation
-
-    @property
-    def indent(self):
-        return indent(self.indentation)
-
-    @staticmethod
-    def section(value):
-        return Colored.cyan(value, bold=True)
-
-    @staticmethod
-    def type(value):
-        return Colored.blue(value, bold=True)
-
-    @staticmethod
-    def node(value):
-        return Colored.red(value, bold=True)
-
-    @staticmethod
-    def property(value):
-        return Colored.magenta(value, bold=True)
-
-    @staticmethod
-    def literal(value):
-        return Colored.magenta(safe_repr(value))
-
-    @staticmethod
-    def meta(value):
-        return Colored.green(value)
-
-    @staticmethod
-    def required(value):
-        return Colored.white(value)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/modeling/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/context.py b/aria/parser/modeling/context.py
index 3d75617..d8a1f7a 100644
--- a/aria/parser/modeling/context.py
+++ b/aria/parser/modeling/context.py
@@ -19,6 +19,10 @@ from ...utils.collections import StrictDict, prune
 from ...utils.uuid import generate_uuid
 
 
+# See: http://www.faqs.org/rfcs/rfc1035.html
+ID_MAX_LENGTH = 63
+
+
 class IdType(object):
     LOCAL_SERIAL = 0
     """
@@ -61,7 +65,7 @@ class ModelingContext(object):
         #self.id_type = IdType.LOCAL_SERIAL
         #self.id_type = IdType.LOCAL_RANDOM
         self.id_type = IdType.UNIVERSAL_RANDOM
-        self.id_max_length = 63 # See: http://www.faqs.org/rfcs/rfc1035.html
+        self.id_max_length = ID_MAX_LENGTH
         self.inputs = StrictDict(key_class=basestring)
 
         self._serial_id_counter = itertools.count(1)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/presentation/fields.py
----------------------------------------------------------------------
diff --git a/aria/parser/presentation/fields.py b/aria/parser/presentation/fields.py
index 959bad1..5c08d4a 100644
--- a/aria/parser/presentation/fields.py
+++ b/aria/parser/presentation/fields.py
@@ -571,7 +571,7 @@ class Field(object):
     def _dump_primitive(self, context, value):
         if hasattr(value, 'as_raw'):
             value = as_raw(value)
-        puts('%s: %s' % (self.name, context.style.literal(value)))
+        puts('%s: %s' % (self.name, context.style.literal_style(value)))
 
     # primitive list
 
@@ -606,11 +606,11 @@ class Field(object):
 
     def _dump_primitive_list(self, context, value):
         puts('%s:' % self.name)
-        with context.style.indent:
+        with context.style.indent():
             for primitive in value:
                 if hasattr(primitive, 'as_raw'):
                     primitive = as_raw(primitive)
-                puts(context.style.literal(primitive))
+                puts(context.style.literal_style(primitive))
 
     # primitive dict
 
@@ -635,11 +635,11 @@ class Field(object):
 
     def _dump_primitive_dict(self, context, value):
         puts('%s:' % self.name)
-        with context.style.indent:
+        with context.style.indent():
             for v in value.itervalues():
                 if hasattr(v, 'as_raw'):
                     v = as_raw(v)
-                puts(context.style.literal(v))
+                puts(context.style.literal_style(v))
 
     # object
 
@@ -654,7 +654,7 @@ class Field(object):
 
     def _dump_object(self, context, value):
         puts('%s:' % self.name)
-        with context.style.indent:
+        with context.style.indent():
             if hasattr(value, '_dump'):
                 value._dump(context)
 
@@ -669,7 +669,7 @@ class Field(object):
 
     def _dump_object_list(self, context, value):
         puts('%s:' % self.name)
-        with context.style.indent:
+        with context.style.indent():
             for v in value:
                 if hasattr(v, '_dump'):
                     v._dump(context)
@@ -685,7 +685,7 @@ class Field(object):
 
     def _dump_object_dict(self, context, value):
         puts('%s:' % self.name)
-        with context.style.indent:
+        with context.style.indent():
             for v in value.itervalues():
                 if hasattr(v, '_dump'):
                     v._dump(context)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/presentation/presentation.py
----------------------------------------------------------------------
diff --git a/aria/parser/presentation/presentation.py b/aria/parser/presentation/presentation.py
index fb71a1e..3f9f86d 100644
--- a/aria/parser/presentation/presentation.py
+++ b/aria/parser/presentation/presentation.py
@@ -37,13 +37,13 @@ class Value(object):
 
     def _dump(self, context):
         if self.type is not None:
-            puts(context.style.type(self.type))
+            puts(context.style.type_style(self.type))
         if self.value is not None:
-            puts(context.style.literal(self.value))
+            puts(context.style.literal_style(self.value))
         if self.description is not None:
-            puts(context.style.meta(self.description))
+            puts(context.style.meta_style(self.description))
         if self.required is not None:
-            puts(context.style.required(self.required))
+            puts(context.style.required_style(self.required))
 
 
 class PresentationBase(HasCachedMethods):
@@ -142,7 +142,7 @@ class PresentationBase(HasCachedMethods):
 
         if self._name:
             puts(context.style.node(self._name))
-            with context.style.indent:
+            with context.style.indent():
                 self._dump_content(context)
         else:
             self._dump_content(context)
@@ -162,7 +162,7 @@ class PresentationBase(HasCachedMethods):
             for field_name in self._iter_field_names():  # pylint: disable=no-member
                 self._dump_field(context, field_name)
         else:
-            puts(context.style.literal(self._raw))
+            puts(context.style.literal_style(self._raw))
 
     def _dump_field(self, context, field_name):
         """
@@ -242,7 +242,7 @@ class AsIsPresentation(PresentationBase):
     def _dump(self, context):
         if hasattr(self._raw, '_dump'):
             puts(context.style.node(self._name))
-            with context.style.indent:
+            with context.style.indent():
                 self._raw._dump(context)
         else:
             super(AsIsPresentation, self)._dump(context)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/reading/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/reading/__init__.py b/aria/parser/reading/__init__.py
index 065ca56..c110585 100644
--- a/aria/parser/reading/__init__.py
+++ b/aria/parser/reading/__init__.py
@@ -24,8 +24,6 @@ Reading package.
    JinjaReader
    JsonReader
    Locator
-   deepcopy_with_locators
-   copy_locators
    RawReader
    Reader
    ReaderSource
@@ -36,7 +34,7 @@ Reading package.
 from .raw import RawReader
 from .reader import Reader
 from .yaml import YamlReader
-from .locator import (Locator, deepcopy_with_locators, copy_locators)
+from .locator import Locator
 from .json import JsonReader
 from .jinja import JinjaReader
 from .context import ReadingContext
@@ -57,8 +55,6 @@ __all__ = (
     'ReadingContext',
     'RawReader',
     'Locator',
-    'deepcopy_with_locators',
-    'copy_locators',
     'YamlReader',
     'JsonReader',
     'JinjaReader')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/reading/locator.py
----------------------------------------------------------------------
diff --git a/aria/parser/reading/locator.py b/aria/parser/reading/locator.py
index 965164d..57b4d50 100644
--- a/aria/parser/reading/locator.py
+++ b/aria/parser/reading/locator.py
@@ -10,9 +10,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from copy import deepcopy
-
-
 from ...utils.console import puts, Colored, indent
 
 
@@ -120,35 +117,3 @@ class Locator(object):
     def __str__(self):
         # Should be in same format as Issue.locator_as_str
         return '"%s":%d:%d' % (self.location, self.line, self.column)
-
-
-def deepcopy_with_locators(value):
-    """
-    Like :func:`deepcopy`, but also copies over locators.
-    """
-
-    res = deepcopy(value)
-    copy_locators(res, value)
-    return res
-
-
-def copy_locators(target, source):
-    """
-    Copies over ``_locator`` for all elements, recursively.
-
-    Assumes that target and source have exactly the same list/dict structure.
-    """
-
-    locator = getattr(source, '_locator', None)
-    if locator is not None:
-        try:
-            setattr(target, '_locator', locator)
-        except AttributeError:
-            pass
-
-    if isinstance(target, list) and isinstance(source, list):
-        for i, _ in enumerate(target):
-            copy_locators(target[i], source[i])
-    elif isinstance(target, dict) and isinstance(source, dict):
-        for k, v in target.items():
-            copy_locators(v, source[k])

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/validation/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/context.py b/aria/parser/validation/context.py
index ef641bd..da9eef6 100644
--- a/aria/parser/validation/context.py
+++ b/aria/parser/validation/context.py
@@ -13,15 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from .issue import Issue
-from ...utils.threading import LockedList
-from ...utils.collections import FrozenList
-from ...utils.exceptions import print_exception
-from ...utils.console import puts, Colored, indent
-from ...utils.formatting import as_raw
+from . import issue
 
 
-class ValidationContext(object):
+class ValidationContext(issue.ReporterMixin):
     """
     Validation context.
 
@@ -35,53 +30,7 @@ class ValidationContext(object):
     :vartype max_level: int
     """
 
-    def __init__(self):
+    def __init__(self, *args, **kwargs):
+        super(ValidationContext, self).__init__(*args, **kwargs)
         self.allow_unknown_fields = False
         self.allow_primitive_coersion = False
-        self.max_level = Issue.ALL
-
-        self._issues = LockedList()
-
-    def report(self, message=None, exception=None, location=None, line=None,
-               column=None, locator=None, snippet=None, level=Issue.PLATFORM, issue=None):
-        if issue is None:
-            issue = Issue(message, exception, location, line, column, locator, snippet, level)
-
-        # Avoid duplicate issues
-        with self._issues:
-            for i in self._issues:
-                if str(i) == str(issue):
-                    return
-
-            self._issues.append(issue)
-
-    @property
-    def has_issues(self):
-        return len(self._issues) > 0
-
-    @property
-    def issues(self):
-        issues = [i for i in self._issues if i.level <= self.max_level]
-        issues.sort(key=lambda i: (i.level, i.location, i.line, i.column, i.message))
-        return FrozenList(issues)
-
-    @property
-    def issues_as_raw(self):
-        return [as_raw(i) for i in self.issues]
-
-    def dump_issues(self):
-        issues = self.issues
-        if issues:
-            puts(Colored.blue('Validation issues:', bold=True))
-            with indent(2):
-                for issue in issues:
-                    puts(Colored.blue(issue.heading_as_str))
-                    details = issue.details_as_str
-                    if details:
-                        with indent(3):
-                            puts(details)
-                    if issue.exception is not None:
-                        with indent(3):
-                            print_exception(issue.exception)
-            return True
-        return False

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/parser/validation/issue.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/issue.py b/aria/parser/validation/issue.py
index db8065d..42fc580 100644
--- a/aria/parser/validation/issue.py
+++ b/aria/parser/validation/issue.py
@@ -15,8 +15,14 @@
 
 from __future__ import absolute_import  # so we can import standard 'collections'
 
-from ...utils.collections import OrderedDict
-from ...utils.type import full_type_name
+from ...utils import (
+    collections,
+    type,
+    threading,
+    exceptions,
+    console,
+    formatting
+)
 
 
 class Issue(object):
@@ -82,14 +88,14 @@ class Issue(object):
 
     @property
     def as_raw(self):
-        return OrderedDict((
+        return collections.OrderedDict((
             ('level', self.level),
             ('message', self.message),
             ('location', self.location),
             ('line', self.line),
             ('column', self.column),
             ('snippet', self.snippet),
-            ('exception', full_type_name(self.exception) if self.exception else None)))
+            ('exception', type.full_type_name(self.exception) if self.exception else None)))
 
     @property
     def locator_as_str(self):
@@ -124,3 +130,61 @@ class Issue(object):
         if details:
             heading_str += ', ' + details
         return heading_str
+
+
+class ReporterMixin(object):
+
+    Issue = Issue
+
+    def __init__(self, *args, **kwargs):
+        super(ReporterMixin, self).__init__(*args, **kwargs)
+        self._issues = threading.LockedList()
+        self.max_level = self.Issue.ALL
+
+    def report(self, message=None, exception=None, location=None, line=None,
+               column=None, locator=None, snippet=None, level=Issue.PLATFORM, issue=None):
+        if issue is None:
+            issue = self.Issue(message, exception, location, line, column, locator, snippet, level)
+
+        # Avoid duplicate issues
+        with self._issues:
+            for i in self._issues:
+                if str(i) == str(issue):
+                    return
+
+            self._issues.append(issue)
+
+    @property
+    def has_issues(self):
+        return len(self._issues) > 0
+
+    @property
+    def issues(self):
+        issues = [i for i in self._issues if i.level <= self.max_level]
+        issues.sort(key=lambda i: (i.level, i.location, i.line, i.column, i.message))
+        return collections.FrozenList(issues)
+
+    @property
+    def issues_as_raw(self):
+        return [formatting.as_raw(i) for i in self.issues]
+
+    def extend_issues(self, *issues):
+        with self._issues:
+            self._issues.extend(*issues)
+
+    def dump_issues(self):
+        issues = self.issues
+        if issues:
+            console.puts(console.Colored.blue('Validation issues:', bold=True))
+            with console.indent(2):
+                for issue in issues:
+                    console.puts(console.Colored.blue(issue.heading_as_str))
+                    details = issue.details_as_str
+                    if details:
+                        with console.indent(3):
+                            console.puts(details)
+                    if issue.exception is not None:
+                        with console.indent(3):
+                            exceptions.print_exception(issue.exception)
+            return True
+        return False

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/utils/__init__.py
----------------------------------------------------------------------
diff --git a/aria/utils/__init__.py b/aria/utils/__init__.py
index 2a957a9..43dd882 100644
--- a/aria/utils/__init__.py
+++ b/aria/utils/__init__.py
@@ -16,3 +16,50 @@
 """
 General-purpose utilities package.
 """
+
+from . import (
+    archive,
+    argparse,
+    caching,
+    collections,
+    console,
+    exceptions,
+    file,
+    formatting,
+    http,
+    imports,
+    openclose,
+    plugin,
+    process,
+    specification,
+    threading,
+    type,
+    uris,
+    uuid,
+    validation,
+    versions
+)
+
+
+__all__ = (
+    'archive',
+    'argparse',
+    'caching',
+    'collections',
+    'console',
+    'exceptions',
+    'file',
+    'formatting',
+    'http',
+    'imports',
+    'openclose',
+    'plugin',
+    'process',
+    'specification',
+    'threading',
+    'type',
+    'uris',
+    'uuid',
+    'validation',
+    'versions'
+)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/utils/console.py
----------------------------------------------------------------------
diff --git a/aria/utils/console.py b/aria/utils/console.py
index 2f6f622..81e8cf8 100644
--- a/aria/utils/console.py
+++ b/aria/utils/console.py
@@ -19,19 +19,64 @@ Abstraction API above terminal color libraries.
 
 import os
 import sys
+from StringIO import StringIO
 
 from contextlib import contextmanager
 
-from .formatting import safe_str
 from ..cli import color
+from . import formatting
 
 
 _indent_string = ''
 
 
+class TopologyStylizer(object):
+    def __init__(self, indentation=0):
+        self._str = StringIO()
+        self._indentation = indentation
+
+    def write(self, string):
+        self._str.write(' ' * self._indentation)
+        self._str.write(string)
+        self._str.write(os.linesep)
+
+    @contextmanager
+    def indent(self, indentation=2):
+        self._indentation += indentation
+        yield
+        self._indentation -= indentation
+
+    @staticmethod
+    def type_style(value):
+        return Colored.blue(value, bold=True)
+
+    @staticmethod
+    def node_style(value):
+        return Colored.red(value, bold=True)
+
+    @staticmethod
+    def property_style(value):
+        return Colored.magenta(value, bold=True)
+
+    @staticmethod
+    def literal_style(value):
+        return Colored.magenta(formatting.safe_repr(value))
+
+    @staticmethod
+    def required_style(value):
+        return Colored.white(value)
+
+    @staticmethod
+    def meta_style(value):
+        return Colored.green(value)
+
+    def __str__(self):
+        return self._str.getvalue()
+
+
 def puts(string='', newline=True, stream=sys.stdout):
     stream.write(_indent_string)
-    stream.write(safe_str(string))
+    stream.write(formatting.safe_str(string))
     if newline:
         stream.write(os.linesep)
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/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 23beb3c..a65ff41 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/misc.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py
@@ -52,7 +52,7 @@ class Description(AsIsPresentation):
 
     def _dump(self, context):
         value = as_raw(self.value)
-        puts(context.style.meta(value))
+        puts(context.style.meta_style(value))
 
 
 @allow_unknown_fields

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/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 2620150..1f90d29 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -497,8 +497,11 @@ def create_plugin_specification_model(context, policy):
 
 
 def create_workflow_operation_template_model(context, service_template, policy):
-    model = OperationTemplate(name=policy._name,
-                              service_template=service_template)
+    model = OperationTemplate(name=policy._name)
+    # since we use backpopulates, these fields are populated upon commit, we get a weird(temporary)
+    # behavior where in previous code service_template.workflow_templates is a dict which has None
+    # as key for the value of model.
+    service_template.workflow_templates[model.name] = model
 
     if policy.description:
         model.description = policy.description.value
@@ -606,7 +609,7 @@ def create_parameter_model_from_value(template_parameter, template_parameter_nam
 def create_parameter_models_from_assignments(properties, source_properties, model_cls):
     if source_properties:
         for property_name, prop in source_properties.iteritems():
-            properties[property_name] = model_cls(name=property_name, # pylint: disable=unexpected-keyword-arg
+            properties[property_name] = model_cls(name=property_name,                               # pylint: disable=unexpected-keyword-arg
                                                   type_name=prop.value.type,
                                                   value=prop.value.value,
                                                   description=prop.value.description)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/tests/parser/service_templates.py
----------------------------------------------------------------------
diff --git a/tests/parser/service_templates.py b/tests/parser/service_templates.py
index 55cd4ad..9e8fcae 100644
--- a/tests/parser/service_templates.py
+++ b/tests/parser/service_templates.py
@@ -47,6 +47,7 @@ def consume_use_case(use_case_name, consumer_class_name='instance', cache=True):
     assert not context.validation.has_issues
     return context, dumper
 
+
 def consume_types_use_case(use_case_name, consumer_class_name='instance', cache=True):
     cachedmethod.ENABLED = cache
     uri = get_service_template_uri('tosca-simple-1.0', 'types', use_case_name,
@@ -61,12 +62,23 @@ def consume_types_use_case(use_case_name, consumer_class_name='instance', cache=
     assert not context.validation.has_issues
     return context, dumper
 
+
 def consume_node_cellar(consumer_class_name='instance', cache=True):
+    consume_test_case(
+        get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml'),
+        consumer_class_name=consumer_class_name,
+        inputs_uri=get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'inputs.yaml'),
+        cache=cache
+
+    )
+
+
+def consume_test_case(uri, inputs_uri=None, consumer_class_name='instance', cache=True):
     cachedmethod.ENABLED = cache
-    uri = get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml')
+    uri = get_service_template_uri(uri)
     context = create_context(uri)
-    context.args.append('--inputs=' + get_service_template_uri('tosca-simple-1.0', 'node-cellar',
-                                                               'inputs.yaml'))
+    if inputs_uri:
+        context.args.append('--inputs=' + get_service_template_uri(inputs_uri))
     consumer, dumper = create_consumer(context, consumer_class_name)
     consumer.consume()
     context.validation.dump_issues()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/tests/parser/test_reqs_caps.py
----------------------------------------------------------------------
diff --git a/tests/parser/test_reqs_caps.py b/tests/parser/test_reqs_caps.py
new file mode 100644
index 0000000..e92aec4
--- /dev/null
+++ b/tests/parser/test_reqs_caps.py
@@ -0,0 +1,29 @@
+# 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 .service_templates import consume_test_case
+from ..helpers import get_service_template_uri
+
+
+def test_satisfy_capability_type():
+    consume_reqs_caps_template1('instance')
+
+
+def consume_reqs_caps_template1(consumer_class_name, cache=True):
+    consume_test_case(
+        get_service_template_uri('tosca-simple-1.0', 'reqs_caps', 'reqs_caps1.yaml'),
+        consumer_class_name=consumer_class_name,
+        cache=cache
+    )

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
----------------------------------------------------------------------
diff --git a/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml b/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
new file mode 100644
index 0000000..466a78e
--- /dev/null
+++ b/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
@@ -0,0 +1,40 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+capability_types:
+  Socket:
+    derived_from: tosca.capabilities.Root
+
+node_types:
+  Socket:
+    derived_from: tosca.nodes.Root
+    capabilities:
+      socket: Socket
+
+  Plug:
+    derived_from: tosca.nodes.Root
+    requirements:
+      - plug:
+          capability: Socket
+
+topology_template:
+  node_templates:
+    socket:
+      type: Socket
+
+    plug:
+      type: Plug
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index 8ca1480..8a4d613 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -23,8 +23,6 @@ from sqlalchemy import (
     MetaData
 )
 
-from aria.modeling import models
-
 
 class TestFileSystem(object):
 


[3/3] incubator-ariatosca git commit: ARIA-174 Refactor instantiation phase

Posted by mx...@apache.org.
ARIA-174 Refactor instantiation phase

The main issue this PR addressed was the consolidation
of instantiation mechanism into a single package - `topology`.

The code itself got additional refactory:
1. `dump` no longer prints onto the console but rather return a string representation to be printed).
2. The service related consumers use the new `topology` package.

The following issues were fixed as part of this PR:
* Req-Cap mechanism is now trying to satisfy requirements for a specific capability type.
* Issue with resuming workflows - accessing a non existing attribute.


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

Branch: refs/heads/ARIA-174-Refactor-instantiation-phase
Commit: 4ad587422f915be64aeb696534eb813fff872aa1
Parents: a43bc6b
Author: max-orlov <ma...@gigaspaces.com>
Authored: Thu Jul 13 16:49:15 2017 +0300
Committer: max-orlov <ma...@gigaspaces.com>
Committed: Wed Aug 9 15:33:30 2017 +0300

----------------------------------------------------------------------
 aria/cli/commands/executions.py                 |   2 +-
 aria/cli/commands/service_templates.py          |   7 +-
 aria/cli/commands/services.py                   |   6 +-
 aria/core.py                                    |  36 +-
 aria/modeling/functions.py                      |   3 +-
 aria/modeling/mixins.py                         |  42 +-
 aria/modeling/service_common.py                 |  27 -
 aria/modeling/service_instance.py               | 535 +-------------
 aria/modeling/service_template.py               | 740 +------------------
 aria/modeling/utils.py                          |  85 +--
 .../execution_plugin/instantiation.py           | 113 ++-
 aria/orchestrator/topology/__init__.py          |  16 +
 aria/orchestrator/topology/common.py            |  69 ++
 aria/orchestrator/topology/instance_handler.py  | 671 +++++++++++++++++
 aria/orchestrator/topology/template_handler.py  | 606 +++++++++++++++
 aria/orchestrator/topology/topology.py          | 223 ++++++
 aria/orchestrator/topology/utils.py             |  48 ++
 aria/orchestrator/workflow_runner.py            |   5 +-
 aria/orchestrator/workflows/api/task.py         |   5 +-
 aria/parser/consumption/__init__.py             |   3 -
 aria/parser/consumption/consumer.py             |   7 +
 aria/parser/consumption/context.py              |   4 +-
 aria/parser/consumption/modeling.py             |  32 +-
 aria/parser/consumption/style.py                |  54 --
 aria/parser/modeling/context.py                 |   6 +-
 aria/parser/presentation/fields.py              |  16 +-
 aria/parser/presentation/presentation.py        |  14 +-
 aria/parser/reading/__init__.py                 |   6 +-
 aria/parser/reading/locator.py                  |  35 -
 aria/parser/validation/context.py               |  59 +-
 aria/parser/validation/issue.py                 |  72 +-
 aria/utils/__init__.py                          |  47 ++
 aria/utils/console.py                           |  49 +-
 .../aria_extension_tosca/simple_v1_0/misc.py    |   2 +-
 .../simple_v1_0/modeling/__init__.py            |   9 +-
 tests/parser/service_templates.py               |  18 +-
 tests/parser/test_reqs_caps.py                  |  29 +
 .../tosca-simple-1.0/reqs_caps/reqs_caps1.yaml  |  40 +
 tests/storage/__init__.py                       |   2 -
 39 files changed, 2069 insertions(+), 1674 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/cli/commands/executions.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/executions.py b/aria/cli/commands/executions.py
index 4783442..f130d95 100644
--- a/aria/cli/commands/executions.py
+++ b/aria/cli/commands/executions.py
@@ -181,7 +181,7 @@ def resume(execution_id,
     executor = DryExecutor() if dry else None  # use WorkflowRunner's default executor
 
     execution = model_storage.execution.get(execution_id)
-    if execution.status != execution.status.CANCELLED:
+    if execution.status != execution.CANCELLED:
         logger.info("Can't resume execution {execution.id} - "
                     "execution is in status {execution.status}. "
                     "Can only resume executions in status {valid_status}"

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/cli/commands/service_templates.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/service_templates.py b/aria/cli/commands/service_templates.py
index f567aa8..5a7039c 100644
--- a/aria/cli/commands/service_templates.py
+++ b/aria/cli/commands/service_templates.py
@@ -28,7 +28,7 @@ from ...core import Core
 from ...storage import exceptions as storage_exceptions
 from ...parser import consumption
 from ...utils import (formatting, collections, console)
-
+from ... orchestrator import topology
 
 DESCRIPTION_FIELD_LENGTH_LIMIT = 20
 SERVICE_TEMPLATE_COLUMNS = \
@@ -73,10 +73,9 @@ def show(service_template_name, model_storage, mode_full, mode_types, format_jso
         elif format_yaml:
             console.puts(formatting.yaml_dumps(collections.prune(service_template.as_raw)))
         else:
-            service_template.dump()
+            console.puts(topology.Topology().dump(service_template))
     elif mode_types:
-        consumption.ConsumptionContext()
-        service_template.dump_types()
+        console.puts(topology.Topology().dump_types(service_template=service_template))
     else:
         logger.info('Showing service template {0}...'.format(service_template_name))
         service_template_dict = service_template.to_dict()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/cli/commands/services.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands/services.py b/aria/cli/commands/services.py
index a99f5b3..6752899 100644
--- a/aria/cli/commands/services.py
+++ b/aria/cli/commands/services.py
@@ -30,6 +30,7 @@ from ...modeling import exceptions as modeling_exceptions
 from ...storage import exceptions as storage_exceptions
 from ...parser import consumption
 from ...utils import (formatting, collections, console)
+from ...orchestrator import topology
 
 
 DESCRIPTION_FIELD_LENGTH_LIMIT = 20
@@ -73,10 +74,9 @@ def show(service_name, model_storage, mode_full, mode_graph, format_json, format
         elif format_yaml:
             console.puts(formatting.yaml_dumps(collections.prune(service.as_raw)))
         else:
-            service.dump()
+            console.puts(topology.Topology().dump(service))
     elif mode_graph:
-        consumption.ConsumptionContext()
-        service.dump_graph()
+        console.puts(topology.Topology().dump_graph(service))
     else:
         logger.info('Showing service {0}...'.format(service_name))
         service_dict = service.to_dict()

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/core.py
----------------------------------------------------------------------
diff --git a/aria/core.py b/aria/core.py
index e214b1a..e3b3b36 100644
--- a/aria/core.py
+++ b/aria/core.py
@@ -20,6 +20,7 @@ ARIA core module.
 from . import exceptions
 from .parser import consumption
 from .parser.loading.location import UriLocation
+from .orchestrator import topology
 
 
 class Core(object):
@@ -67,31 +68,26 @@ class Core(object):
         self.resource_storage.service_template.delete(entry_id=str(service_template.id))
 
     def create_service(self, service_template_id, inputs, service_name=None):
-
         service_template = self.model_storage.service_template.get(service_template_id)
 
-        # creating an empty ConsumptionContext, initiating a threadlocal context
-        context = consumption.ConsumptionContext()
-
         storage_session = self.model_storage._all_api_kwargs['session']
         # setting no autoflush for the duration of instantiation - this helps avoid dependency
         # constraints as they're being set up
         with storage_session.no_autoflush:
-            service = service_template.instantiate(None, self.model_storage, inputs=inputs)
-
-            consumption.ConsumerChain(
-                context,
-                (
-                    consumption.CoerceServiceInstanceValues,
-                    consumption.ValidateServiceInstance,
-                    consumption.SatisfyRequirements,
-                    consumption.CoerceServiceInstanceValues,
-                    consumption.ValidateCapabilities,
-                    consumption.FindHosts,
-                    consumption.ConfigureOperations,
-                    consumption.CoerceServiceInstanceValues
-                )).consume()
-            if context.validation.dump_issues():
+            topology_ = topology.Topology()
+            service = topology_.instantiate(
+                service_template, inputs=inputs, plugins=self.model_storage.plugin.list())
+            topology_.coerce(service, report_issues=True)
+
+            topology_.validate(service)
+            topology_.satisfy_requirements(service)
+            topology_.coerce(service, report_issues=True)
+
+            topology_.validate_capabilities(service)
+            topology_.assign_hosts(service)
+            topology_.configure_operations(service)
+            topology_.coerce(service, report_issues=True)
+            if topology_.dump_issues():
                 raise exceptions.InstantiationError('Failed to instantiate service template `{0}`'
                                                     .format(service_template.name))
 
@@ -122,6 +118,8 @@ class Core(object):
     def _parse_service_template(service_template_path):
         context = consumption.ConsumptionContext()
         context.presentation.location = UriLocation(service_template_path)
+        # Most of the parser uses the topology package in order to manipulate the models.
+        # However, here we use the Consumer mechanism, but this should change in the future.
         consumption.ConsumerChain(
             context,
             (

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/functions.py
----------------------------------------------------------------------
diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py
index 6544adf..31c3839 100644
--- a/aria/modeling/functions.py
+++ b/aria/modeling/functions.py
@@ -16,9 +16,8 @@
 """
 Mechanism for evaluating intrinsic functions.
 """
-
-from ..parser.consumption import ConsumptionContext
 from ..parser.exceptions import InvalidValueError
+from ..parser.consumption import ConsumptionContext
 from ..utils.collections import OrderedDict
 from . import exceptions
 

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index 4acbe6e..d58c25a 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -25,8 +25,7 @@ from sqlalchemy import (
     PickleType
 )
 
-from ..parser.consumption import ConsumptionContext
-from ..utils import console, collections, caching, formatting
+from ..utils import collections, caching
 from ..utils.type import canonical_type_name, full_type_name
 from . import utils, functions
 
@@ -132,26 +131,17 @@ class InstanceModelMixin(ModelMixin):
     def as_raw(self):
         raise NotImplementedError
 
-    def validate(self):
-        pass
-
     def coerce_values(self, report_issues):
         pass
 
-    def dump(self):
-        pass
-
 
-class TemplateModelMixin(InstanceModelMixin):
+class TemplateModelMixin(InstanceModelMixin):                                                       # pylint: disable=abstract-method
     """
     Mix-in for service template models.
 
     All model models can be instantiated into service instance models.
     """
 
-    def instantiate(self, container):
-        raise NotImplementedError
-
 
 class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods):                                 #pylint: disable=abstract-method
     """
@@ -306,34 +296,6 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods):
             ('value', self.value),
             ('description', self.description)))
 
-    def instantiate(self, container):
-        return self.__class__(name=self.name,  # pylint: disable=unexpected-keyword-arg
-                              type_name=self.type_name,
-                              _value=self._value,
-                              description=self.description)
-
-    def coerce_values(self, report_issues):
-        value = self._value
-        if value is not None:
-            evaluation = functions.evaluate(value, self, report_issues)
-            if (evaluation is not None) and evaluation.final:
-                # A final evaluation can safely replace the existing value
-                self._value = evaluation.value
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        if self.type_name is not None:
-            console.puts('{0}: {1} ({2})'.format(
-                context.style.property(self.name),
-                context.style.literal(formatting.as_raw(self.value)),
-                context.style.type(self.type_name)))
-        else:
-            console.puts('{0}: {1}'.format(
-                context.style.property(self.name),
-                context.style.literal(formatting.as_raw(self.value))))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-
     @property
     def unwrapped(self):
         return self.name, self.value

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index 4ce9dae..478e530 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -26,11 +26,9 @@ from sqlalchemy import (
 )
 from sqlalchemy.ext.declarative import declared_attr
 
-from ..parser.consumption import ConsumptionContext
 from ..utils import (
     collections,
     formatting,
-    console,
 )
 from .mixins import InstanceModelMixin, TemplateModelMixin, ParameterMixin
 from . import relationship
@@ -563,17 +561,6 @@ class TypeBase(InstanceModelMixin):
         self._append_raw_children(types)
         return types
 
-    def coerce_values(self, report_issues):
-        pass
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        if self.name:
-            console.puts(context.style.type(self.name))
-        with context.style.indent:
-            for child in self.children:
-                child.dump()
-
     def _append_raw_children(self, types):
         for child in self.children:
             raw_child = formatting.as_raw(child)
@@ -612,17 +599,3 @@ class MetadataBase(TemplateModelMixin):
         return collections.OrderedDict((
             ('name', self.name),
             ('value', self.value)))
-
-    def coerce_values(self, report_issues):
-        pass
-
-    def instantiate(self, container):
-        from . import models
-        return models.Metadata(name=self.name,
-                               value=self.value)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts('{0}: {1}'.format(
-            context.style.property(self.name),
-            context.style.literal(self.value)))

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
index ae6a0a0..daeb0a4 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -30,19 +30,15 @@ from sqlalchemy import DateTime
 from sqlalchemy.ext.declarative import declared_attr
 from sqlalchemy.ext.orderinglist import ordering_list
 
+from . import (
+    relationship,
+    types as modeling_types
+)
 from .mixins import InstanceModelMixin
-from ..orchestrator import execution_plugin
-from ..parser import validation
-from ..parser.consumption import ConsumptionContext
+
 from ..utils import (
     collections,
     formatting,
-    console
-)
-from . import (
-    relationship,
-    utils,
-    types as modeling_types
 )
 
 
@@ -232,50 +228,6 @@ class ServiceBase(InstanceModelMixin):
     :type: :class:`~datetime.datetime`
     """)
 
-    def satisfy_requirements(self):
-        satisfied = True
-        for node in self.nodes.itervalues():
-            if not node.satisfy_requirements():
-                satisfied = False
-        return satisfied
-
-    def validate_capabilities(self):
-        satisfied = True
-        for node in self.nodes.itervalues():
-            if not node.validate_capabilities():
-                satisfied = False
-        return satisfied
-
-    def find_hosts(self):
-        for node in self.nodes.itervalues():
-            node.find_host()
-
-    def configure_operations(self):
-        for node in self.nodes.itervalues():
-            node.configure_operations()
-        for group in self.groups.itervalues():
-            group.configure_operations()
-        for operation in self.workflows.itervalues():
-            operation.configure()
-
-    def is_node_a_target(self, target_node):
-        for node in self.nodes.itervalues():
-            if self._is_node_a_target(node, target_node):
-                return True
-        return False
-
-    def _is_node_a_target(self, source_node, target_node):
-        if source_node.outbound_relationships:
-            for relationship_model in source_node.outbound_relationships:
-                if relationship_model.target_node.name == target_node.name:
-                    return True
-                else:
-                    node = relationship_model.target_node
-                    if node is not None:
-                        if self._is_node_a_target(node, target_node):
-                            return True
-        return False
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -289,70 +241,6 @@ class ServiceBase(InstanceModelMixin):
             ('outputs', formatting.as_raw_dict(self.outputs)),
             ('workflows', formatting.as_raw_list(self.workflows))))
 
-    def validate(self):
-        utils.validate_dict_values(self.meta_data)
-        utils.validate_dict_values(self.nodes)
-        utils.validate_dict_values(self.groups)
-        utils.validate_dict_values(self.policies)
-        if self.substitution is not None:
-            self.substitution.validate()
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.outputs)
-        utils.validate_dict_values(self.workflows)
-
-    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(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()
-        if self.description is not None:
-            console.puts(context.style.meta(self.description))
-        utils.dump_dict_values(self.meta_data, 'Metadata')
-        for node in self.nodes.itervalues():
-            node.dump()
-        for group in self.groups.itervalues():
-            group.dump()
-        for policy in self.policies.itervalues():
-            policy.dump()
-        if self.substitution is not None:
-            self.substitution.dump()
-        utils.dump_dict_values(self.inputs, 'Inputs')
-        utils.dump_dict_values(self.outputs, 'Outputs')
-        utils.dump_dict_values(self.workflows, 'Workflows')
-
-    def dump_graph(self):
-        for node in self.nodes.itervalues():
-            if not self.is_node_a_target(node):
-                self._dump_graph_node(node)
-
-    def _dump_graph_node(self, node, capability=None):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(node.name))
-        if capability is not None:
-            console.puts('{0} ({1})'.format(context.style.property(capability.name),
-                                            context.style.type(capability.type.name)))
-        if node.outbound_relationships:
-            with context.style.indent:
-                for relationship_model in node.outbound_relationships:
-                    relationship_name = context.style.property(relationship_model.name)
-                    if relationship_model.type is not None:
-                        console.puts('-> {0} ({1})'.format(relationship_name,
-                                                           context.style.type(
-                                                               relationship_model.type.name)))
-                    else:
-                        console.puts('-> {0}'.format(relationship_name))
-                    with console.indent(3):
-                        self._dump_graph_node(relationship_model.target_node,
-                                              relationship_model.target_capability)
-
 
 class NodeBase(InstanceModelMixin):
     """
@@ -616,118 +504,6 @@ class NodeBase(InstanceModelMixin):
             return attribute.value if attribute else None
         return None
 
-    def satisfy_requirements(self):
-        node_template = self.node_template
-        satisfied = True
-        for requirement_template in node_template.requirement_templates:
-            # Find target template
-            target_node_template, target_node_capability = \
-                requirement_template.find_target(node_template)
-            if target_node_template is not None:
-                satisfied = self._satisfy_capability(target_node_capability,
-                                                     target_node_template,
-                                                     requirement_template)
-            else:
-                context = ConsumptionContext.get_thread_local()
-                context.validation.report('requirement "{0}" of node "{1}" has no target node '
-                                          'template'.format(requirement_template.name, self.name),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                satisfied = False
-        return satisfied
-
-    def _satisfy_capability(self, target_node_capability, target_node_template,
-                            requirement_template):
-        from . import models
-        context = ConsumptionContext.get_thread_local()
-        # Find target nodes
-        target_nodes = target_node_template.nodes
-        if target_nodes:
-            target_node = None
-            target_capability = None
-
-            if target_node_capability is not None:
-                # Relate to the first target node that has capacity
-                for node in target_nodes:
-                    a_target_capability = node.capabilities.get(target_node_capability.name)
-                    if a_target_capability.relate():
-                        target_node = node
-                        target_capability = a_target_capability
-                        break
-            else:
-                # Use first target node
-                target_node = target_nodes[0]
-
-            if target_node is not None:
-                if requirement_template.relationship_template is not None:
-                    relationship_model = \
-                        requirement_template.relationship_template.instantiate(self)
-                else:
-                    relationship_model = models.Relationship()
-                relationship_model.name = requirement_template.name
-                relationship_model.requirement_template = requirement_template
-                relationship_model.target_node = target_node
-                relationship_model.target_capability = target_capability
-                self.outbound_relationships.append(relationship_model)
-                return True
-            else:
-                context.validation.report('requirement "{0}" of node "{1}" targets node '
-                                          'template "{2}" but its instantiated nodes do not '
-                                          'have enough capacity'.format(
-                                              requirement_template.name,
-                                              self.name,
-                                              target_node_template.name),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                return False
-        else:
-            context.validation.report('requirement "{0}" of node "{1}" targets node template '
-                                      '"{2}" but it has no instantiated nodes'.format(
-                                          requirement_template.name,
-                                          self.name,
-                                          target_node_template.name),
-                                      level=validation.Issue.BETWEEN_INSTANCES)
-            return False
-
-    def validate_capabilities(self):
-        context = ConsumptionContext.get_thread_local()
-        satisfied = False
-        for capability in self.capabilities.itervalues():
-            if not capability.has_enough_relationships:
-                context.validation.report('capability "{0}" of node "{1}" requires at least {2:d} '
-                                          'relationships but has {3:d}'.format(
-                                              capability.name,
-                                              self.name,
-                                              capability.min_occurrences,
-                                              capability.occurrences),
-                                          level=validation.Issue.BETWEEN_INSTANCES)
-                satisfied = False
-        return satisfied
-
-    def find_host(self):
-        def _find_host(node):
-            if node.type.role == 'host':
-                return node
-            for the_relationship in node.outbound_relationships:
-                if (the_relationship.target_capability is not None) and \
-                    the_relationship.target_capability.type.role == 'host':
-                    host = _find_host(the_relationship.target_node)
-                    if host is not None:
-                        return host
-            for the_relationship in node.inbound_relationships:
-                if (the_relationship.target_capability is not None) and \
-                    the_relationship.target_capability.type.role == 'feature':
-                    host = _find_host(the_relationship.source_node)
-                    if host is not None:
-                        return host
-            return None
-
-        self.host = _find_host(self)
-
-    def configure_operations(self):
-        for interface in self.interfaces.itervalues():
-            interface.configure_operations()
-        for the_relationship in self.outbound_relationships:
-            the_relationship.configure_operations()
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -740,46 +516,6 @@ class NodeBase(InstanceModelMixin):
             ('capabilities', formatting.as_raw_list(self.capabilities)),
             ('relationships', formatting.as_raw_list(self.outbound_relationships))))
 
-    def validate(self):
-        context = ConsumptionContext.get_thread_local()
-        if len(self.name) > context.modeling.id_max_length:
-            context.validation.report('"{0}" has an ID longer than the limit of {1:d} characters: '
-                                      '{2:d}'.format(
-                                          self.name,
-                                          context.modeling.id_max_length,
-                                          len(self.name)),
-                                      level=validation.Issue.BETWEEN_INSTANCES)
-
-        # 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, 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()
-        console.puts('Node: {0}'.format(context.style.node(self.name)))
-        with context.style.indent:
-            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')
-            utils.dump_list_values(self.outbound_relationships, 'Relationships')
-
 
 class GroupBase(InstanceModelMixin):
     """
@@ -885,10 +621,6 @@ class GroupBase(InstanceModelMixin):
     :type: :obj:`basestring`
     """)
 
-    def configure_operations(self):
-        for interface in self.interfaces.itervalues():
-            interface.configure_operations()
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -896,27 +628,6 @@ class GroupBase(InstanceModelMixin):
             ('properties', formatting.as_raw_dict(self.properties)),
             ('interfaces', formatting.as_raw_list(self.interfaces))))
 
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-        utils.validate_dict_values(self.interfaces)
-
-    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()
-        console.puts('Group: {0}'.format(context.style.node(self.name)))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.properties, 'Properties')
-            utils.dump_interfaces(self.interfaces)
-            if self.nodes:
-                console.puts('Member nodes:')
-                with context.style.indent:
-                    for node in self.nodes:
-                        console.puts(context.style.node(node.name))
-
 
 class PolicyBase(InstanceModelMixin):
     """
@@ -1030,29 +741,6 @@ class PolicyBase(InstanceModelMixin):
             ('type_name', self.type.name),
             ('properties', formatting.as_raw_dict(self.properties))))
 
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts('Policy: {0}'.format(context.style.node(self.name)))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.properties, 'Properties')
-            if self.nodes:
-                console.puts('Target nodes:')
-                with context.style.indent:
-                    for node in self.nodes:
-                        console.puts(context.style.node(node.name))
-            if self.groups:
-                console.puts('Target groups:')
-                with context.style.indent:
-                    for group in self.groups:
-                        console.puts(context.style.node(group.name))
-
 
 class SubstitutionBase(InstanceModelMixin):
     """
@@ -1130,19 +818,6 @@ class SubstitutionBase(InstanceModelMixin):
             ('node_type_name', self.node_type.name),
             ('mappings', formatting.as_raw_dict(self.mappings))))
 
-    def validate(self):
-        utils.validate_dict_values(self.mappings)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.mappings, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts('Substitution:')
-        with context.style.indent:
-            console.puts('Node type: {0}'.format(context.style.type(self.node_type.name)))
-            utils.dump_dict_values(self.mappings, 'Mappings')
-
 
 class SubstitutionMappingBase(InstanceModelMixin):
     """
@@ -1238,31 +913,6 @@ class SubstitutionMappingBase(InstanceModelMixin):
         return collections.OrderedDict((
             ('name', self.name),))
 
-    def coerce_values(self, report_issues):
-        pass
-
-    def validate(self):
-        context = ConsumptionContext.get_thread_local()
-        if (self.capability is None) and (self.requirement_template is None):
-            context.validation.report('mapping "{0}" refers to neither capability nor a requirement'
-                                      ' in node: {1}'.format(
-                                          self.name,
-                                          formatting.safe_repr(self.node.name)),
-                                      level=validation.Issue.BETWEEN_TYPES)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        if self.capability is not None:
-            console.puts('{0} -> {1}.{2}'.format(
-                context.style.node(self.name),
-                context.style.node(self.capability.node.name),
-                context.style.node(self.capability.name)))
-        else:
-            console.puts('{0} -> {1}.{2}'.format(
-                context.style.node(self.name),
-                context.style.node(self.node.name),
-                context.style.node(self.requirement_template.name)))
-
 
 class RelationshipBase(InstanceModelMixin):
     """
@@ -1436,10 +1086,6 @@ class RelationshipBase(InstanceModelMixin):
     :type: :obj:`int`
     """)
 
-    def configure_operations(self):
-        for interface in self.interfaces.itervalues():
-            interface.configure_operations()
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -1452,33 +1098,6 @@ class RelationshipBase(InstanceModelMixin):
             ('properties', formatting.as_raw_dict(self.properties)),
             ('interfaces', formatting.as_raw_list(self.interfaces))))
 
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-        utils.validate_dict_values(self.interfaces)
-
-    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()
-        if self.name:
-            console.puts('{0} ->'.format(context.style.node(self.name)))
-        else:
-            console.puts('->')
-        with context.style.indent:
-            console.puts('Node: {0}'.format(context.style.node(self.target_node.name)))
-            if self.target_capability:
-                console.puts('Capability: {0}'.format(context.style.node(
-                    self.target_capability.name)))
-            if self.type is not None:
-                console.puts('Relationship type: {0}'.format(context.style.type(self.type.name)))
-            if (self.relationship_template is not None) and self.relationship_template.name:
-                console.puts('Relationship template: {0}'.format(
-                    context.style.node(self.relationship_template.name)))
-            utils.dump_dict_values(self.properties, 'Properties')
-            utils.dump_interfaces(self.interfaces, 'Interfaces')
-
 
 class CapabilityBase(InstanceModelMixin):
     """
@@ -1595,25 +1214,6 @@ class CapabilityBase(InstanceModelMixin):
             ('type_name', self.type.name),
             ('properties', formatting.as_raw_dict(self.properties))))
 
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            console.puts('Occurrences: {0:d} ({1:d}{2})'.format(
-                self.occurrences,
-                self.min_occurrences or 0,
-                ' to {0:d}'.format(self.max_occurrences)
-                if self.max_occurrences is not None
-                else ' or more'))
-            utils.dump_dict_values(self.properties, 'Properties')
-
 
 class InterfaceBase(InstanceModelMixin):
     """
@@ -1738,10 +1338,6 @@ class InterfaceBase(InstanceModelMixin):
     :type: :obj:`basestring`
     """)
 
-    def configure_operations(self):
-        for operation in self.operations.itervalues():
-            operation.configure()
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -1751,24 +1347,6 @@ class InterfaceBase(InstanceModelMixin):
             ('inputs', formatting.as_raw_dict(self.inputs)),
             ('operations', formatting.as_raw_list(self.operations))))
 
-    def validate(self):
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.operations)
-
-    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()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Interface type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.inputs, 'Inputs')
-            utils.dump_dict_values(self.operations, 'Operations')
-
 
 class OperationBase(InstanceModelMixin):
     """
@@ -1944,45 +1522,6 @@ class OperationBase(InstanceModelMixin):
     :type: :obj:`float`
     """)
 
-    def configure(self):
-        if (self.implementation is None) and (self.function is None):
-            return
-
-        if (self.interface is not None) and (self.plugin is None) and (self.function is None):
-            # ("interface" is None for workflow operations, which do not currently use "plugin")
-            # The default (None) plugin is the execution plugin
-            execution_plugin.instantiation.configure_operation(self)
-        else:
-            # In the future plugins may be able to add their own "configure_operation" hook that
-            # can validate the configuration and otherwise create specially derived arguments. For
-            # now, we just send all configuration parameters as arguments without validation.
-            utils.instantiate_dict(self, self.arguments,
-                                   utils.dict_as_arguments(self.configurations))
-
-        if self.interface is not None:
-            # Send all interface inputs as extra arguments
-            # ("interface" is None for workflow operations)
-            # Note that they will override existing arguments of the same names
-            utils.instantiate_dict(self, self.arguments,
-                                   utils.dict_as_arguments(self.interface.inputs))
-
-        # Send all inputs as extra arguments
-        # Note that they will override existing arguments of the same names
-        utils.instantiate_dict(self, self.arguments, utils.dict_as_arguments(self.inputs))
-
-        # Check for reserved arguments
-        from ..orchestrator.decorators import OPERATION_DECORATOR_RESERVED_ARGUMENTS
-        used_reserved_names = \
-            OPERATION_DECORATOR_RESERVED_ARGUMENTS.intersection(self.arguments.keys())
-        if used_reserved_names:
-            context = ConsumptionContext.get_thread_local()
-            context.validation.report('using reserved arguments in operation "{0}": {1}'
-                                      .format(
-                                          self.name,
-                                          formatting.string_list_as_string(used_reserved_names)),
-                                      level=validation.Issue.EXTERNAL)
-
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -1992,46 +1531,6 @@ class OperationBase(InstanceModelMixin):
             ('dependencies', self.dependencies),
             ('inputs', formatting.as_raw_dict(self.inputs))))
 
-    def validate(self):
-        # TODO must be associated with either interface or service
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.configurations)
-        utils.validate_dict_values(self.arguments)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.inputs, report_issues)
-        utils.coerce_dict_values(self.configurations, report_issues)
-        utils.coerce_dict_values(self.arguments, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            if self.implementation is not None:
-                console.puts('Implementation: {0}'.format(
-                    context.style.literal(self.implementation)))
-            if self.dependencies:
-                console.puts(
-                    'Dependencies: {0}'.format(
-                        ', '.join((str(context.style.literal(v)) for v in self.dependencies))))
-            utils.dump_dict_values(self.inputs, 'Inputs')
-            if self.executor is not None:
-                console.puts('Executor: {0}'.format(context.style.literal(self.executor)))
-            if self.max_attempts is not None:
-                console.puts('Max attempts: {0}'.format(context.style.literal(self.max_attempts)))
-            if self.retry_interval is not None:
-                console.puts('Retry interval: {0}'.format(
-                    context.style.literal(self.retry_interval)))
-            if self.plugin is not None:
-                console.puts('Plugin: {0}'.format(
-                    context.style.literal(self.plugin.name)))
-            utils.dump_dict_values(self.configurations, 'Configuration')
-            if self.function is not None:
-                console.puts('Function: {0}'.format(context.style.literal(self.function)))
-            utils.dump_dict_values(self.arguments, 'Arguments')
-
 
 class ArtifactBase(InstanceModelMixin):
     """
@@ -2150,27 +1649,3 @@ class ArtifactBase(InstanceModelMixin):
             ('repository_url', self.repository_url),
             ('repository_credential', formatting.as_agnostic(self.repository_credential)),
             ('properties', formatting.as_raw_dict(self.properties))))
-
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Artifact type: {0}'.format(context.style.type(self.type.name)))
-            console.puts('Source path: {0}'.format(context.style.literal(self.source_path)))
-            if self.target_path is not None:
-                console.puts('Target path: {0}'.format(context.style.literal(self.target_path)))
-            if self.repository_url is not None:
-                console.puts('Repository URL: {0}'.format(
-                    context.style.literal(self.repository_url)))
-            if self.repository_credential:
-                console.puts('Repository credential: {0}'.format(
-                    context.style.literal(self.repository_credential)))
-            utils.dump_dict_values(self.properties, 'Properties')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index 2246340..d988d21 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -21,8 +21,6 @@ ARIA modeling service template module
 
 from __future__ import absolute_import  # so we can import standard 'types'
 
-from datetime import datetime
-
 from sqlalchemy import (
     Column,
     Text,
@@ -33,15 +31,10 @@ from sqlalchemy import (
 )
 from sqlalchemy.ext.declarative import declared_attr
 
-from ..parser import validation
-from ..parser.consumption import ConsumptionContext
-from ..parser.reading import deepcopy_with_locators
-from ..utils import (collections, formatting, console)
-from ..utils.versions import VersionString
+from ..utils import (collections, formatting)
 from .mixins import TemplateModelMixin
 from . import (
     relationship,
-    utils,
     types as modeling_types
 )
 
@@ -331,130 +324,6 @@ class ServiceTemplateBase(TemplateModelMixin):
             ('interface_types', formatting.as_raw(self.interface_types)),
             ('artifact_types', formatting.as_raw(self.artifact_types))))
 
-    def instantiate(self, container, model_storage, inputs=None):  # pylint: disable=arguments-differ
-        from . import models
-        now = datetime.now()
-        service = models.Service(created_at=now,
-                                 updated_at=now,
-                                 description=deepcopy_with_locators(self.description),
-                                 service_template=self)
-
-        # TODO: we want to remove this use of the context
-        context = ConsumptionContext.get_thread_local()
-        context.modeling.instance = service
-
-        utils.validate_no_undeclared_inputs(declared_inputs=self.inputs,
-                                            supplied_inputs=inputs or {})
-        utils.validate_required_inputs_are_supplied(declared_inputs=self.inputs,
-                                                    supplied_inputs=inputs or {})
-
-        service.inputs = utils.merge_parameter_values(inputs, self.inputs, model_cls=models.Input)
-        # TODO: now that we have inputs, we should scan properties and inputs and evaluate functions
-
-        for plugin_specification in self.plugin_specifications.itervalues():
-            if plugin_specification.enabled:
-                if plugin_specification.resolve(model_storage):
-                    plugin = plugin_specification.plugin
-                    service.plugins[plugin.name] = plugin
-                else:
-                    context = ConsumptionContext.get_thread_local()
-                    context.validation.report('specified plugin not found: {0}'.format(
-                        plugin_specification.name), level=validation.Issue.EXTERNAL)
-
-        utils.instantiate_dict(self, service.meta_data, self.meta_data)
-
-        for node_template in self.node_templates.itervalues():
-            for _ in range(node_template.scaling['default_instances']):
-                node = node_template.instantiate(container)
-                service.nodes[node.name] = node
-
-        utils.instantiate_dict(self, service.groups, self.group_templates)
-        utils.instantiate_dict(self, service.policies, self.policy_templates)
-        utils.instantiate_dict(self, service.workflows, self.workflow_templates)
-
-        if self.substitution_template is not None:
-            service.substitution = self.substitution_template.instantiate(container)
-
-        utils.instantiate_dict(self, service.outputs, self.outputs)
-
-        return service
-
-    def validate(self):
-        utils.validate_dict_values(self.meta_data)
-        utils.validate_dict_values(self.node_templates)
-        utils.validate_dict_values(self.group_templates)
-        utils.validate_dict_values(self.policy_templates)
-        if self.substitution_template is not None:
-            self.substitution_template.validate()
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.outputs)
-        utils.validate_dict_values(self.workflow_templates)
-        if self.node_types is not None:
-            self.node_types.validate()
-        if self.group_types is not None:
-            self.group_types.validate()
-        if self.policy_types is not None:
-            self.policy_types.validate()
-        if self.relationship_types is not None:
-            self.relationship_types.validate()
-        if self.capability_types is not None:
-            self.capability_types.validate()
-        if self.interface_types is not None:
-            self.interface_types.validate()
-        if self.artifact_types is not None:
-            self.artifact_types.validate()
-
-    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(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()
-        if self.description is not None:
-            console.puts(context.style.meta(self.description))
-        utils.dump_dict_values(self.meta_data, 'Metadata')
-        for node_template in self.node_templates.itervalues():
-            node_template.dump()
-        for group_template in self.group_templates.itervalues():
-            group_template.dump()
-        for policy_template in self.policy_templates.itervalues():
-            policy_template.dump()
-        if self.substitution_template is not None:
-            self.substitution_template.dump()
-        utils.dump_dict_values(self.inputs, 'Inputs')
-        utils.dump_dict_values(self.outputs, 'Outputs')
-        utils.dump_dict_values(self.workflow_templates, 'Workflow templates')
-
-    def dump_types(self):
-        if self.node_types.children:
-            console.puts('Node types:')
-            self.node_types.dump()
-        if self.group_types.children:
-            console.puts('Group types:')
-            self.group_types.dump()
-        if self.capability_types.children:
-            console.puts('Capability types:')
-            self.capability_types.dump()
-        if self.relationship_types.children:
-            console.puts('Relationship types:')
-            self.relationship_types.dump()
-        if self.policy_types.children:
-            console.puts('Policy types:')
-            self.policy_types.dump()
-        if self.artifact_types.children:
-            console.puts('Artifact types:')
-            self.artifact_types.dump()
-        if self.interface_types.children:
-            console.puts('Interface types:')
-            self.interface_types.dump()
-
 
 class NodeTemplateBase(TemplateModelMixin):
     """
@@ -625,114 +494,6 @@ class NodeTemplateBase(TemplateModelMixin):
             ('capability_templates', formatting.as_raw_list(self.capability_templates)),
             ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
 
-    def instantiate(self, container):
-        from . import models
-        node = models.Node(name=self._next_name,
-                           type=self.type,
-                           description=deepcopy_with_locators(self.description),
-                           state=models.Node.INITIAL,
-                           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) \
-            and (node.attributes['tosca_name'].type_name == 'string'):
-            node.attributes['tosca_name'].value = self.name
-        if 'tosca_id' in node.attributes \
-            and (node.attributes['tosca_id'].type_name == 'string'):
-            node.attributes['tosca_id'].value = node.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, 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()
-        console.puts('Node template: {0}'.format(context.style.node(self.name)))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            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')
-            utils.dump_list_values(self.requirement_templates, 'Requirement templates')
-
-    @property
-    def scaling(self):
-        scaling = {}
-
-        def extract_property(properties, name):
-            if name in scaling:
-                return
-            prop = properties.get(name)
-            if (prop is not None) and (prop.type_name == 'integer') and (prop.value is not None):
-                scaling[name] = prop.value
-
-        def extract_properties(properties):
-            extract_property(properties, 'min_instances')
-            extract_property(properties, 'max_instances')
-            extract_property(properties, 'default_instances')
-
-        def default_property(name, value):
-            if name not in scaling:
-                scaling[name] = value
-
-        # From our scaling capabilities
-        for capability_template in self.capability_templates.itervalues():
-            if capability_template.type.role == 'scaling':
-                extract_properties(capability_template.properties)
-
-        # From service scaling policies
-        for policy_template in self.service_template.policy_templates.itervalues():
-            if policy_template.type.role == 'scaling':
-                if policy_template.is_for_node_template(self.name):
-                    extract_properties(policy_template.properties)
-
-        # Defaults
-        default_property('min_instances', 0)
-        default_property('max_instances', 1)
-        default_property('default_instances', 1)
-
-        # Validate
-        # pylint: disable=too-many-boolean-expressions
-        if ((scaling['min_instances'] < 0) or
-                (scaling['max_instances'] < 0) or
-                (scaling['default_instances'] < 0) or
-                (scaling['max_instances'] < scaling['min_instances']) or
-                (scaling['default_instances'] < scaling['min_instances']) or
-                (scaling['default_instances'] > scaling['max_instances'])):
-            context = ConsumptionContext.get_thread_local()
-            context.validation.report('invalid scaling parameters for node template "{0}": '
-                                      'min={1}, max={2}, default={3}'.format(
-                                          self.name,
-                                          scaling['min_instances'],
-                                          scaling['max_instances'],
-                                          scaling['default_instances']),
-                                      level=validation.Issue.BETWEEN_TYPES)
-
-        return scaling
-
     def is_target_node_template_valid(self, target_node_template):
         """
         Checks if ``target_node_template`` matches all our ``target_node_template_constraints``.
@@ -769,6 +530,40 @@ class NodeTemplateBase(TemplateModelMixin):
 
         return '{name}_{index}'.format(name=self.name, index=self._next_index)
 
+    @property
+    def scaling(self):
+        scaling = {}
+
+        def extract_property(properties, name):
+            if name in scaling:
+                return
+            prop = properties.get(name)
+            if (prop is not None) and (prop.type_name == 'integer') and (prop.value is not None):
+                scaling[name] = prop.value
+
+        def extract_properties(properties):
+            extract_property(properties, 'min_instances')
+            extract_property(properties, 'max_instances')
+            extract_property(properties, 'default_instances')
+
+        # From our scaling capabilities
+        for capability_template in self.capability_templates.itervalues():
+            if capability_template.type.role == 'scaling':
+                extract_properties(capability_template.properties)
+
+        # From service scaling policies
+        for policy_template in self.service_template.policy_templates.itervalues():
+            if policy_template.type.role == 'scaling':
+                if policy_template.is_for_node_template(self.name):
+                    extract_properties(policy_template.properties)
+
+        # Defaults
+        scaling.setdefault('min_instances', 0)
+        scaling.setdefault('max_instances', 1)
+        scaling.setdefault('default_instances', 1)
+
+        return scaling
+
 
 class GroupTemplateBase(TemplateModelMixin):
     """
@@ -876,40 +671,6 @@ class GroupTemplateBase(TemplateModelMixin):
             ('properties', formatting.as_raw_dict(self.properties)),
             ('interface_templates', formatting.as_raw_list(self.interface_templates))))
 
-    def instantiate(self, container):
-        from . import models
-        group = models.Group(name=self.name,
-                             type=self.type,
-                             description=deepcopy_with_locators(self.description),
-                             group_template=self)
-        utils.instantiate_dict(self, group.properties, self.properties)
-        utils.instantiate_dict(self, group.interfaces, self.interface_templates)
-        if self.node_templates:
-            for node_template in self.node_templates:
-                group.nodes += node_template.nodes
-        return group
-
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-        utils.validate_dict_values(self.interface_templates)
-
-    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()
-        console.puts('Group template: {0}'.format(context.style.node(self.name)))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.properties, 'Properties')
-            utils.dump_interfaces(self.interface_templates)
-            if self.node_templates:
-                console.puts('Member node templates: {0}'.format(', '.join(
-                    (str(context.style.node(v.name)) for v in self.node_templates))))
-
     def contains_node_template(self, name):
         for node_template in self.node_templates:
             if node_template.name == name:
@@ -1022,42 +783,6 @@ class PolicyTemplateBase(TemplateModelMixin):
             ('type_name', self.type.name),
             ('properties', formatting.as_raw_dict(self.properties))))
 
-    def instantiate(self, container):
-        from . import models
-        policy = models.Policy(name=self.name,
-                               type=self.type,
-                               description=deepcopy_with_locators(self.description),
-                               policy_template=self)
-        utils.instantiate_dict(self, policy.properties, self.properties)
-        if self.node_templates:
-            for node_template in self.node_templates:
-                policy.nodes += node_template.nodes
-        if self.group_templates:
-            for group_template in self.group_templates:
-                policy.groups += group_template.groups
-        return policy
-
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts('Policy template: {0}'.format(context.style.node(self.name)))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.properties, 'Properties')
-            if self.node_templates:
-                console.puts('Target node templates: {0}'.format(', '.join(
-                    (str(context.style.node(v.name)) for v in self.node_templates))))
-            if self.group_templates:
-                console.puts('Target group templates: {0}'.format(', '.join(
-                    (str(context.style.node(v.name)) for v in self.group_templates))))
-
     def is_for_node_template(self, name):
         for node_template in self.node_templates:
             if node_template.name == name:
@@ -1134,26 +859,6 @@ class SubstitutionTemplateBase(TemplateModelMixin):
             ('node_type_name', self.node_type.name),
             ('mappings', formatting.as_raw_dict(self.mappings))))
 
-    def instantiate(self, container):
-        from . import models
-        substitution = models.Substitution(node_type=self.node_type,
-                                           substitution_template=self)
-        utils.instantiate_dict(container, substitution.mappings, self.mappings)
-        return substitution
-
-    def validate(self):
-        utils.validate_dict_values(self.mappings)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.mappings, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts('Substitution template:')
-        with context.style.indent:
-            console.puts('Node type: {0}'.format(context.style.type(self.node_type.name)))
-            utils.dump_dict_values(self.mappings, 'Mappings')
-
 
 class SubstitutionTemplateMappingBase(TemplateModelMixin):
     """
@@ -1233,59 +938,6 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
         return collections.OrderedDict((
             ('name', self.name),))
 
-    def coerce_values(self, report_issues):
-        pass
-
-    def instantiate(self, container):
-        from . import models
-        context = ConsumptionContext.get_thread_local()
-        if self.capability_template is not None:
-            node_template = self.capability_template.node_template
-        else:
-            node_template = self.requirement_template.node_template
-        nodes = node_template.nodes
-        if len(nodes) == 0:
-            context.validation.report(
-                'mapping "{0}" refers to node template "{1}" but there are no '
-                'node instances'.format(self.mapped_name, self.node_template.name),
-                level=validation.Issue.BETWEEN_INSTANCES)
-            return None
-        # The TOSCA spec does not provide a way to choose the node,
-        # so we will just pick the first one
-        node = nodes[0]
-        capability = None
-        if self.capability_template:
-            for a_capability in node.capabilities.itervalues():
-                if a_capability.capability_template.name == self.capability_template.name:
-                    capability = a_capability
-        return models.SubstitutionMapping(name=self.name,
-                                          capability=capability,
-                                          requirement_template=self.requirement_template,
-                                          node=node)
-
-
-    def validate(self):
-        context = ConsumptionContext.get_thread_local()
-        if (self.capability_template is None) and (self.requirement_template is None):
-            context.validation.report('mapping "{0}" refers to neither capability nor a requirement'
-                                      ' in node template: {1}'.format(
-                                          self.name,
-                                          formatting.safe_repr(self.node_template.name)),
-                                      level=validation.Issue.BETWEEN_TYPES)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        if self.capability_template is not None:
-            node_template = self.capability_template.node_template
-        else:
-            node_template = self.requirement_template.node_template
-        console.puts('{0} -> {1}.{2}'.format(
-            context.style.node(self.name),
-            context.style.node(node_template.name),
-            context.style.node(self.capability_template.name
-                               if self.capability_template
-                               else self.requirement_template.name)))
-
 
 class RequirementTemplateBase(TemplateModelMixin):
     """
@@ -1424,56 +1076,6 @@ class RequirementTemplateBase(TemplateModelMixin):
     :type: [:class:`NodeTemplateConstraint`]
     """)
 
-    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_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,
-                                              source_node_template.name),
-                                          level=validation.Issue.BETWEEN_TYPES)
-            if (self.target_capability_type is not None) \
-                or (self.target_capability_name is not None):
-                target_node_capability = self.find_target_capability(source_node_template,
-                                                                     self.target_node_template)
-                if target_node_capability is None:
-                    return None, None
-            else:
-                target_node_capability = None
-
-            return self.target_node_template, target_node_capability
-
-        # Find first node that matches the type
-        elif self.target_node_type is not None:
-            for target_node_template in \
-                    self.node_template.service_template.node_templates.itervalues():
-                if self.target_node_type.get_descendant(target_node_template.type.name) is None:
-                    continue
-
-                if not source_node_template.is_target_node_template_valid(target_node_template):
-                    continue
-
-                target_node_capability = self.find_target_capability(source_node_template,
-                                                                     target_node_template)
-                if target_node_capability is None:
-                    continue
-
-                return target_node_template, target_node_capability
-
-        return None, None
-
-    def find_target_capability(self, source_node_template, target_node_template):
-        for capability_template in target_node_template.capability_templates.itervalues():
-            if capability_template.satisfies_requirement(source_node_template,
-                                                         self,
-                                                         target_node_template):
-                return capability_template
-        return None
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -1487,43 +1089,6 @@ class RequirementTemplateBase(TemplateModelMixin):
             ('target_capability_name', self.target_capability_name),
             ('relationship_template', formatting.as_raw(self.relationship_template))))
 
-    def validate(self):
-        if self.relationship_template:
-            self.relationship_template.validate()
-
-    def coerce_values(self, report_issues):
-        if self.relationship_template is not None:
-            self.relationship_template.coerce_values(report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        if self.name:
-            console.puts(context.style.node(self.name))
-        else:
-            console.puts('Requirement:')
-        with context.style.indent:
-            if self.target_node_type is not None:
-                console.puts('Target node type: {0}'.format(
-                    context.style.type(self.target_node_type.name)))
-            elif self.target_node_template is not None:
-                console.puts('Target node template: {0}'.format(
-                    context.style.node(self.target_node_template.name)))
-            if self.target_capability_type is not None:
-                console.puts('Target capability type: {0}'.format(
-                    context.style.type(self.target_capability_type.name)))
-            elif self.target_capability_name is not None:
-                console.puts('Target capability name: {0}'.format(
-                    context.style.node(self.target_capability_name)))
-            if self.target_node_template_constraints:
-                console.puts('Target node template constraints:')
-                with context.style.indent:
-                    for constraint in self.target_node_template_constraints:
-                        console.puts(context.style.literal(constraint))
-            if self.relationship_template:
-                console.puts('Relationship:')
-                with context.style.indent:
-                    self.relationship_template.dump()
-
 
 class RelationshipTemplateBase(TemplateModelMixin):
     """
@@ -1606,37 +1171,6 @@ class RelationshipTemplateBase(TemplateModelMixin):
             ('properties', formatting.as_raw_dict(self.properties)),
             ('interface_templates', formatting.as_raw_list(self.interface_templates))))
 
-    def instantiate(self, container):
-        from . import models
-        relationship_model = models.Relationship(name=self.name,
-                                                 type=self.type,
-                                                 relationship_template=self)
-        utils.instantiate_dict(container, relationship_model.properties, self.properties)
-        utils.instantiate_dict(container, relationship_model.interfaces, self.interface_templates)
-        return relationship_model
-
-    def validate(self):
-        # TODO: either type or name must be set
-        utils.validate_dict_values(self.properties)
-        utils.validate_dict_values(self.interface_templates)
-
-    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()
-        if self.type is not None:
-            console.puts('Relationship type: {0}'.format(context.style.type(self.type.name)))
-        else:
-            console.puts('Relationship template: {0}'.format(
-                context.style.node(self.name)))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            utils.dump_dict_values(self.properties, 'Properties')
-            utils.dump_interfaces(self.interface_templates, 'Interface templates')
-
 
 class CapabilityTemplateBase(TemplateModelMixin):
     """
@@ -1739,29 +1273,6 @@ class CapabilityTemplateBase(TemplateModelMixin):
     :type: :obj:`int`
     """)
 
-    def satisfies_requirement(self,
-                              source_node_template,
-                              requirement,
-                              target_node_template):
-        # Do we match the required capability type?
-        if requirement.target_capability_type and \
-            requirement.target_capability_type.get_descendant(self.type.name) is None:
-            return False
-
-        # Are we in valid_source_node_types?
-        if self.valid_source_node_types:
-            for valid_source_node_type in self.valid_source_node_types:
-                if valid_source_node_type.get_descendant(source_node_template.type.name) is None:
-                    return False
-
-        # Apply requirement constraints
-        if requirement.target_node_template_constraints:
-            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
-
     @property
     def as_raw(self):
         return collections.OrderedDict((
@@ -1773,42 +1284,6 @@ class CapabilityTemplateBase(TemplateModelMixin):
             ('valid_source_node_types', [v.name for v in self.valid_source_node_types]),
             ('properties', formatting.as_raw_dict(self.properties))))
 
-    def instantiate(self, container):
-        from . import models
-        capability = models.Capability(name=self.name,
-                                       type=self.type,
-                                       min_occurrences=self.min_occurrences,
-                                       max_occurrences=self.max_occurrences,
-                                       occurrences=0,
-                                       capability_template=self)
-        utils.instantiate_dict(container, capability.properties, self.properties)
-        return capability
-
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Type: {0}'.format(context.style.type(self.type.name)))
-            console.puts(
-                'Occurrences: {0:d}{1}'.format(
-                    self.min_occurrences or 0,
-                    ' to {0:d}'.format(self.max_occurrences)
-                    if self.max_occurrences is not None
-                    else ' or more'))
-            if self.valid_source_node_types:
-                console.puts('Valid source node types: {0}'.format(
-                    ', '.join((str(context.style.type(v.name))
-                               for v in self.valid_source_node_types))))
-            utils.dump_dict_values(self.properties, 'Properties')
-
 
 class InterfaceTemplateBase(TemplateModelMixin):
     """
@@ -1938,34 +1413,6 @@ class InterfaceTemplateBase(TemplateModelMixin):
             # TODO fix self.properties reference
             ('operation_templates', formatting.as_raw_list(self.operation_templates))))
 
-    def instantiate(self, container):
-        from . import models
-        interface = models.Interface(name=self.name,
-                                     type=self.type,
-                                     description=deepcopy_with_locators(self.description),
-                                     interface_template=self)
-        utils.instantiate_dict(container, interface.inputs, self.inputs)
-        utils.instantiate_dict(container, interface.operations, self.operation_templates)
-        return interface
-
-    def validate(self):
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.operation_templates)
-
-    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()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Interface type: {0}'.format(context.style.type(self.type.name)))
-            utils.dump_dict_values(self.inputs, 'Inputs')
-            utils.dump_dict_values(self.operation_templates, 'Operation templates')
-
 
 class OperationTemplateBase(TemplateModelMixin):
     """
@@ -2124,65 +1571,6 @@ class OperationTemplateBase(TemplateModelMixin):
             ('dependencies', self.dependencies),
             ('inputs', formatting.as_raw_dict(self.inputs))))
 
-    def instantiate(self, container):
-        from . import models
-
-        plugin = self.plugin_specification.plugin \
-            if (self.plugin_specification is not None) and self.plugin_specification.enabled \
-            else None
-
-        operation = models.Operation(name=self.name,
-                                     description=deepcopy_with_locators(self.description),
-                                     relationship_edge=self.relationship_edge,
-                                     implementation=self.implementation,
-                                     dependencies=self.dependencies,
-                                     executor=self.executor,
-                                     plugin=plugin,
-                                     function=self.function,
-                                     max_attempts=self.max_attempts,
-                                     retry_interval=self.retry_interval,
-                                     operation_template=self)
-
-        utils.instantiate_dict(container, operation.inputs, self.inputs)
-        utils.instantiate_dict(container, operation.configurations, self.configurations)
-
-        return operation
-
-    def validate(self):
-        utils.validate_dict_values(self.inputs)
-        utils.validate_dict_values(self.configurations)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.inputs, report_issues)
-        utils.coerce_dict_values(self.configurations, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            if self.implementation is not None:
-                console.puts('Implementation: {0}'.format(
-                    context.style.literal(self.implementation)))
-            if self.dependencies:
-                console.puts('Dependencies: {0}'.format(
-                    ', '.join((str(context.style.literal(v)) for v in self.dependencies))))
-            utils.dump_dict_values(self.inputs, 'Inputs')
-            if self.executor is not None:
-                console.puts('Executor: {0}'.format(context.style.literal(self.executor)))
-            if self.max_attempts is not None:
-                console.puts('Max attempts: {0}'.format(context.style.literal(self.max_attempts)))
-            if self.retry_interval is not None:
-                console.puts('Retry interval: {0}'.format(
-                    context.style.literal(self.retry_interval)))
-            if self.plugin_specification is not None:
-                console.puts('Plugin specification: {0}'.format(
-                    context.style.literal(self.plugin_specification.name)))
-            utils.dump_dict_values(self.configurations, 'Configuration')
-            if self.function is not None:
-                console.puts('Function: {0}'.format(context.style.literal(self.function)))
-
 
 class ArtifactTemplateBase(TemplateModelMixin):
     """
@@ -2295,43 +1683,6 @@ class ArtifactTemplateBase(TemplateModelMixin):
             ('repository_credential', formatting.as_agnostic(self.repository_credential)),
             ('properties', formatting.as_raw_dict(self.properties))))
 
-    def instantiate(self, container):
-        from . import models
-        artifact = models.Artifact(name=self.name,
-                                   type=self.type,
-                                   description=deepcopy_with_locators(self.description),
-                                   source_path=self.source_path,
-                                   target_path=self.target_path,
-                                   repository_url=self.repository_url,
-                                   repository_credential=self.repository_credential,
-                                   artifact_template=self)
-        utils.instantiate_dict(container, artifact.properties, self.properties)
-        return artifact
-
-    def validate(self):
-        utils.validate_dict_values(self.properties)
-
-    def coerce_values(self, report_issues):
-        utils.coerce_dict_values(self.properties, report_issues)
-
-    def dump(self):
-        context = ConsumptionContext.get_thread_local()
-        console.puts(context.style.node(self.name))
-        if self.description:
-            console.puts(context.style.meta(self.description))
-        with context.style.indent:
-            console.puts('Artifact type: {0}'.format(context.style.type(self.type.name)))
-            console.puts('Source path: {0}'.format(context.style.literal(self.source_path)))
-            if self.target_path is not None:
-                console.puts('Target path: {0}'.format(context.style.literal(self.target_path)))
-            if self.repository_url is not None:
-                console.puts('Repository URL: {0}'.format(
-                    context.style.literal(self.repository_url)))
-            if self.repository_credential:
-                console.puts('Repository credential: {0}'.format(
-                    context.style.literal(self.repository_credential)))
-            utils.dump_dict_values(self.properties, 'Properties')
-
 
 class PluginSpecificationBase(TemplateModelMixin):
     """
@@ -2399,24 +1750,3 @@ class PluginSpecificationBase(TemplateModelMixin):
             ('name', self.name),
             ('version', self.version),
             ('enabled', self.enabled)))
-
-    def coerce_values(self, report_issues):
-        pass
-
-    def resolve(self, model_storage):
-        # TODO: we are planning a separate "instantiation" module where this will be called or
-        # moved to.
-        plugins = model_storage.plugin.list()
-        matching_plugins = []
-        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
-            key = lambda plugin: VersionString(plugin.package_version).key
-            self.plugin = sorted(matching_plugins, key=key)[-1]
-        return self.plugin is not None


[2/3] incubator-ariatosca git commit: ARIA-174 Refactor instantiation phase

Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index 274eb88..305020b 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -22,8 +22,6 @@ from json import JSONEncoder
 from StringIO import StringIO
 
 from . import exceptions
-from ..parser.consumption import ConsumptionContext
-from ..utils.console import puts
 from ..utils.type import validate_value_type
 from ..utils.collections import OrderedDict
 from ..utils.formatting import string_list_as_string
@@ -84,7 +82,7 @@ def validate_required_inputs_are_supplied(declared_inputs, supplied_inputs):
             .format(string_list_as_string(missing_required_inputs)))
 
 
-def merge_parameter_values(provided_values, declared_parameters, model_cls):
+def merge_parameter_values(provided_values, declared_parameters, model_cls=None):
     """
     Merges parameter values according to those declared by a type.
 
@@ -109,6 +107,7 @@ def merge_parameter_values(provided_values, declared_parameters, model_cls):
     provided_values = provided_values or {}
     provided_values_of_wrong_type = OrderedDict()
     model_parameters = OrderedDict()
+    model_cls = model_cls or _get_class_from_sql_relationship(declared_parameters)
 
     for declared_parameter_name, declared_parameter in declared_parameters.iteritems():
         if declared_parameter_name in provided_values:
@@ -125,14 +124,14 @@ def merge_parameter_values(provided_values, declared_parameters, model_cls):
                 # TODO This error shouldn't be raised (or caught), but right now we lack support
                 # for custom data_types, which will raise this error. Skipping their validation.
                 pass
-            model_parameters[declared_parameter_name] = model_cls( # pylint: disable=unexpected-keyword-arg
+            model_parameters[declared_parameter_name] = model_cls(                                  # pylint: disable=unexpected-keyword-arg
                 name=declared_parameter_name,
                 type_name=type_name,
                 description=declared_parameter.description,
                 value=value)
         else:
             # Copy default value from declaration
-            model_parameters[declared_parameter_name] = declared_parameter.instantiate(None)
+            model_parameters[declared_parameter_name] = model_cls(**declared_parameter.as_raw)
 
     if provided_values_of_wrong_type:
         error_message = StringIO()
@@ -144,76 +143,6 @@ def merge_parameter_values(provided_values, declared_parameters, model_cls):
     return model_parameters
 
 
-def coerce_dict_values(the_dict, report_issues=False):
-    if not the_dict:
-        return
-    coerce_list_values(the_dict.itervalues(), report_issues)
-
-
-def coerce_list_values(the_list, report_issues=False):
-    if not the_list:
-        return
-    for value in the_list:
-        value.coerce_values(report_issues)
-
-
-def validate_dict_values(the_dict):
-    if not the_dict:
-        return
-    validate_list_values(the_dict.itervalues())
-
-
-def validate_list_values(the_list):
-    if not the_list:
-        return
-    for value in the_list:
-        value.validate()
-
-
-def instantiate_dict(container, the_dict, from_dict):
-    if not from_dict:
-        return
-    for name, value in from_dict.iteritems():
-        value = value.instantiate(container)
-        if value is not None:
-            the_dict[name] = value
-
-
-def instantiate_list(container, the_list, from_list):
-    if not from_list:
-        return
-    for value in from_list:
-        value = value.instantiate(container)
-        if value is not None:
-            the_list.append(value)
-
-
-def dump_list_values(the_list, name):
-    if not the_list:
-        return
-    puts('%s:' % name)
-    context = ConsumptionContext.get_thread_local()
-    with context.style.indent:
-        for value in the_list:
-            value.dump()
-
-
-def dump_dict_values(the_dict, name):
-    if not the_dict:
-        return
-    dump_list_values(the_dict.itervalues(), name)
-
-
-def dump_interfaces(interfaces, name='Interfaces'):
-    if not interfaces:
-        return
-    puts('%s:' % name)
-    context = ConsumptionContext.get_thread_local()
-    with context.style.indent:
-        for interface in interfaces.itervalues():
-            interface.dump()
-
-
 def parameters_as_values(the_dict):
     return dict((k, v.value) for k, v in the_dict.iteritems())
 
@@ -244,3 +173,9 @@ def fix_doc(cls):
     cls.__doc__ = cls.__bases__[-1].__doc__
 
     return cls
+
+
+def _get_class_from_sql_relationship(field):
+    class_ = field._sa_adapter.owner_state.class_
+    prop_name = field._sa_adapter.attr.key
+    return getattr(class_, prop_name).property.mapper.class_

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/execution_plugin/instantiation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/execution_plugin/instantiation.py b/aria/orchestrator/execution_plugin/instantiation.py
index f55aa50..8b52015 100644
--- a/aria/orchestrator/execution_plugin/instantiation.py
+++ b/aria/orchestrator/execution_plugin/instantiation.py
@@ -18,16 +18,11 @@ Instantiation of :class:`~aria.modeling.models.Operation` models.
 """
 
 # TODO: this module will eventually be moved to a new "aria.instantiation" package
-
-from ...utils.type import full_type_name
-from ...utils.formatting import safe_repr
-from ...utils.collections import OrderedDict
-from ...parser import validation
-from ...parser.consumption import ConsumptionContext
 from ...modeling.functions import Function
+from ... import utils
 
 
-def configure_operation(operation):
+def configure_operation(operation, reporter):
     host = None
     interface = operation.interface
     if interface.node is not None:
@@ -38,11 +33,11 @@ def configure_operation(operation):
         else: # either False or None (None meaning that edge was not specified)
             host = interface.relationship.source_node.host
 
-    _configure_common(operation)
+    _configure_common(operation, reporter)
     if host is None:
         _configure_local(operation)
     else:
-        _configure_remote(operation)
+        _configure_remote(operation, reporter)
 
     # Any remaining un-handled configuration parameters will become extra arguments, available as
     # kwargs in either "run_script_locally" or "run_script_with_ssh"
@@ -51,7 +46,7 @@ def configure_operation(operation):
             operation.arguments[key] = value.instantiate(None)
 
 
-def _configure_common(operation):
+def _configure_common(operation, reporter):
     """
     Local and remote operations.
     """
@@ -59,7 +54,7 @@ def _configure_common(operation):
     from ...modeling.models import Argument
     operation.arguments['script_path'] = Argument.wrap('script_path', operation.implementation,
                                                        'Relative path to the executable file.')
-    operation.arguments['process'] = Argument.wrap('process', _get_process(operation),
+    operation.arguments['process'] = Argument.wrap('process', _get_process(operation, reporter),
                                                    'Sub-process configuration.')
 
 
@@ -73,7 +68,7 @@ def _configure_local(operation):
                                           operations.run_script_locally.__name__)
 
 
-def _configure_remote(operation):
+def _configure_remote(operation, reporter):
     """
     Remote SSH operation via Fabric.
     """
@@ -81,7 +76,7 @@ def _configure_remote(operation):
     from ...modeling.models import Argument
     from . import operations
 
-    ssh = _get_ssh(operation)
+    ssh = _get_ssh(operation, reporter)
 
     # Defaults
     # TODO: find a way to configure these generally in the service template
@@ -110,20 +105,17 @@ def _configure_remote(operation):
 
     # Make sure we have a user
     if fabric_env.get('user') is None:
-        context = ConsumptionContext.get_thread_local()
-        context.validation.report('must configure "ssh.user" for "{0}"'
-                                  .format(operation.implementation),
-                                  level=validation.Issue.BETWEEN_TYPES)
+        reporter.report('must configure "ssh.user" for "{0}"'.format(operation.implementation),
+                        level=reporter.Issue.BETWEEN_TYPES)
 
     # Make sure we have an authentication value
     if (fabric_env.get('password') is None) and \
         (fabric_env.get('key') is None) and \
         (fabric_env.get('key_filename') is None):
-        context = ConsumptionContext.get_thread_local()
-        context.validation.report('must configure "ssh.password", "ssh.key", or "ssh.key_filename" '
-                                  'for "{0}"'
-                                  .format(operation.implementation),
-                                  level=validation.Issue.BETWEEN_TYPES)
+        reporter.report(
+            'must configure "ssh.password", "ssh.key", or "ssh.key_filename" for "{0}"'
+            .format(operation.implementation),
+            level=reporter.Issue.BETWEEN_TYPES)
 
     operation.arguments['fabric_env'] = Argument.wrap('fabric_env', fabric_env,
                                                       'Fabric configuration.')
@@ -132,97 +124,94 @@ def _configure_remote(operation):
                                           operations.run_script_with_ssh.__name__)
 
 
-def _get_process(operation):
+def _get_process(operation, reporter):
     value = (operation.configurations.get('process')._value
              if 'process' in operation.configurations
              else None)
     if value is None:
         return {}
-    _validate_type(value, dict, 'process')
-    value = OrderedDict(value)
+    _validate_type(value, dict, 'process', reporter)
+    value = utils.collections.OrderedDict(value)
     for k, v in value.iteritems():
         if k == 'eval_python':
-            value[k] = _coerce_bool(v, 'process.eval_python')
+            value[k] = _coerce_bool(v, 'process.eval_python', reporter)
         elif k == 'cwd':
-            _validate_type(v, basestring, 'process.cwd')
+            _validate_type(v, basestring, 'process.cwd', reporter)
         elif k == 'command_prefix':
-            _validate_type(v, basestring, 'process.command_prefix')
+            _validate_type(v, basestring, 'process.command_prefix', reporter)
         elif k == 'args':
-            value[k] = _dict_to_list_of_strings(v, 'process.args')
+            value[k] = _dict_to_list_of_strings(v, 'process.args', reporter)
         elif k == 'env':
-            _validate_type(v, dict, 'process.env')
+            _validate_type(v, dict, 'process.env', reporter)
         else:
-            context = ConsumptionContext.get_thread_local()
-            context.validation.report('unsupported configuration parameter: "process.{0}"'
-                                      .format(k),
-                                      level=validation.Issue.BETWEEN_TYPES)
+            reporter.report('unsupported configuration parameter: "process.{0}"'.format(k),
+                            level=reporter.Issue.BETWEEN_TYPES)
     return value
 
 
-def _get_ssh(operation):
+def _get_ssh(operation, reporter):
     value = (operation.configurations.get('ssh')._value
              if 'ssh' in operation.configurations
              else None)
     if value is None:
         return {}
-    _validate_type(value, dict, 'ssh')
-    value = OrderedDict(value)
+    _validate_type(value, dict, 'ssh', reporter)
+    value = utils.collections.OrderedDict(value)
     for k, v in value.iteritems():
         if k == 'use_sudo':
-            value[k] = _coerce_bool(v, 'ssh.use_sudo')
+            value[k] = _coerce_bool(v, 'ssh.use_sudo', reporter)
         elif k == 'hide_output':
-            value[k] = _dict_to_list_of_strings(v, 'ssh.hide_output')
+            value[k] = _dict_to_list_of_strings(v, 'ssh.hide_output', reporter)
         elif k == 'warn_only':
-            value[k] = _coerce_bool(v, 'ssh.warn_only')
+            value[k] = _coerce_bool(v, 'ssh.warn_only', reporter)
         elif k == 'user':
-            _validate_type(v, basestring, 'ssh.user')
+            _validate_type(v, basestring, 'ssh.user', reporter)
         elif k == 'password':
-            _validate_type(v, basestring, 'ssh.password')
+            _validate_type(v, basestring, 'ssh.password', reporter)
         elif k == 'key':
-            _validate_type(v, basestring, 'ssh.key')
+            _validate_type(v, basestring, 'ssh.key', reporter)
         elif k == 'key_filename':
-            _validate_type(v, basestring, 'ssh.key_filename')
+            _validate_type(v, basestring, 'ssh.key_filename', reporter)
         elif k == 'address':
-            _validate_type(v, basestring, 'ssh.address')
+            _validate_type(v, basestring, 'ssh.address', reporter)
         else:
-            context = ConsumptionContext.get_thread_local()
-            context.validation.report('unsupported configuration parameter: "ssh.{0}"'.format(k),
-                                      level=validation.Issue.BETWEEN_TYPES)
+            reporter.report('unsupported configuration parameter: "ssh.{0}"'.format(k),
+                            level=reporter.Issue.BETWEEN_TYPES)
     return value
 
 
-def _validate_type(value, the_type, name):
+def _validate_type(value, the_type, name, reporter):
     if isinstance(value, Function):
         return
     if not isinstance(value, the_type):
-        context = ConsumptionContext.get_thread_local()
-        context.validation.report('"{0}" configuration is not a {1}: {2}'
-                                  .format(name, full_type_name(the_type), safe_repr(value)),
-                                  level=validation.Issue.BETWEEN_TYPES)
+        reporter.report(
+            '"{0}" configuration is not a {1}: {2}'.format(
+                name, utils.type.full_type_name(the_type), utils.formatting.safe_repr(value)),
+            level=reporter.Issue.BETWEEN_TYPES)
 
 
-def _coerce_bool(value, name):
+def _coerce_bool(value, name, reporter):
     if value is None:
         return None
     if isinstance(value, bool):
         return value
-    _validate_type(value, basestring, name)
+    _validate_type(value, basestring, name, reporter)
     if value == 'true':
         return True
     elif value == 'false':
         return False
     else:
-        context = ConsumptionContext.get_thread_local()
-        context.validation.report('"{0}" configuration is not "true" or "false": {1}'
-                                  .format(name, safe_repr(value)),
-                                  level=validation.Issue.BETWEEN_TYPES)
+        reporter.report(
+            '"{0}" configuration is not "true" or "false": {1}'.format(
+                name, utils.formatting.safe_repr(value)),
+            level=reporter.Issue.BETWEEN_TYPES)
 
 
-def _dict_to_list_of_strings(the_dict, name):
-    _validate_type(the_dict, dict, name)
+def _dict_to_list_of_strings(the_dict, name, reporter):
+    _validate_type(the_dict, dict, name, reporter)
     value = []
     for k in sorted(the_dict):
         v = the_dict[k]
-        _validate_type(v, basestring, '{0}.{1}'.format(name, k))
+        _validate_type(v, basestring, '{0}.{1}'.format(name, k), reporter)
         value.append(v)
     return value

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/__init__.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/__init__.py b/aria/orchestrator/topology/__init__.py
new file mode 100644
index 0000000..099a950
--- /dev/null
+++ b/aria/orchestrator/topology/__init__.py
@@ -0,0 +1,16 @@
+# 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 .topology import Topology

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/common.py b/aria/orchestrator/topology/common.py
new file mode 100644
index 0000000..5124557
--- /dev/null
+++ b/aria/orchestrator/topology/common.py
@@ -0,0 +1,69 @@
+# 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 HandlerBase(object):
+    def __init__(self, topology, model):
+        self._topology = topology
+        self._model = model
+
+    def coerce(self, **kwargs):
+        raise NotImplementedError
+
+    def _coerce(self, *models, **kwargs):
+        for template in models:
+            self._topology.coerce(template, **kwargs)
+
+    def validate(self, **kwargs):
+        raise NotImplementedError
+
+    def _validate(self, *models, **kwargs):
+        for template in models:
+            self._topology.validate(template, **kwargs)
+
+    def dump(self, out_stream):
+        raise NotImplementedError
+
+
+class TemplateHandlerBase(HandlerBase):
+    """
+    Base handler for template based models
+    """
+
+    def instantiate(self, instance_cls, **kwargs):
+        raise NotImplementedError
+
+
+class InstanceHandlerBase(HandlerBase):
+    """
+    Base handler for instance based models
+
+    """
+    def validate(self, **kwargs):
+        raise NotImplementedError
+
+    def coerce(self, **kwargs):
+        raise NotImplementedError
+
+    def dump(self, out_stream):
+        raise NotImplementedError
+
+
+class ActorHandlerBase(HandlerBase):
+    """
+    Base handler for any model which has (or contains a field which references) an operation
+    """
+    def configure_operations(self):
+        raise NotImplementedError

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/instance_handler.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/instance_handler.py b/aria/orchestrator/topology/instance_handler.py
new file mode 100644
index 0000000..51f26c6
--- /dev/null
+++ b/aria/orchestrator/topology/instance_handler.py
@@ -0,0 +1,671 @@
+# 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 ... parser.modeling import context
+from ... modeling import models, functions
+from ... utils import formatting
+from .. import execution_plugin
+from .. import decorators
+from . import common
+
+
+class Artifact(common.InstanceHandlerBase):
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+    def dump(self, out_stream):
+        with out_stream.indent():
+            out_stream.write(out_stream.node_style(self._model.name))
+            out_stream.write(out_stream.meta_style(self._model.description))
+            with out_stream.indent():
+                out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+                    self._model.type.name)))
+                out_stream.write('Source path: {0}'.format(
+                    out_stream.literal_style(self._model.source_path)))
+                if self._model.target_path is not None:
+                    out_stream.write('Target path: {0}'.format(
+                        out_stream.literal_style(self._model.target_path)))
+                if self._model.repository_url is not None:
+                    out_stream.write('Repository URL: {0}'.format(
+                        out_stream.literal_style(self._model.repository_url)))
+                if self._model.repository_credential:
+                    out_stream.write('Repository credential: {0}'.format(
+                        out_stream.literal_style(self._model.repository_credential)))
+                self._topology.dump(self._model.properties, out_stream, title='Properties')
+
+
+class Capability(common.InstanceHandlerBase):
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write('Occurrences: {0:d} ({1:d}{2})'.format(
+                self._model.occurrences,
+                self._model.min_occurrences or 0,
+                ' to {0:d}'.format(self._model.max_occurrences)
+                if self._model.max_occurrences is not None
+                else ' or more'))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+
+
+class Group(common.ActorHandlerBase):
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties, self._model.interfaces, **kwargs)
+
+    def validate(self, **kwargs):
+        self._validate(self._model.properties,
+                       self._model.interfaces,
+                       **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write('Group: {0}'.format(out_stream.node_style(self._model.name)))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
+            if self._model.nodes:
+                out_stream.write('Member nodes:')
+                with out_stream.indent():
+                    for node in self._model.nodes:
+                        out_stream.write(out_stream.node_style(node.name))
+
+    def configure_operations(self):
+        for interface in self._model.interfaces.values():
+            self._topology.configure_operations(interface)
+
+
+class Interface(common.ActorHandlerBase):
+    def coerce(self, **kwargs):
+        self._coerce(self._model.inputs, self._model.operations, **kwargs)
+
+    def validate(self, **kwargs):
+        self._validate(self._model.inputs,
+                       self._model.operations,
+                       **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Interface type: {0}'.format(
+                out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+            self._topology.dump(self._model.operations, out_stream, title='Operations')
+
+    def configure_operations(self):
+        for operation in self._model.operations.values():
+            self._topology.configure_operations(operation)
+
+
+class Node(common.ActorHandlerBase):
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties,
+                     self._model.attributes,
+                     self._model.interfaces,
+                     self._model.artifacts,
+                     self._model.capabilities,
+                     self._model.outbound_relationships,
+                     **kwargs)
+
+    def validate(self, **kwargs):
+        if len(self._model.name) > context.ID_MAX_LENGTH:
+            self._topology.report(
+                '"{0}" has an ID longer than the limit of {1:d} characters: {2:d}'.format(
+                    self._model.name, context.ID_MAX_LENGTH, len(self._model.name)),
+                level=self._topology.Issue.BETWEEN_INSTANCES)
+
+        self._validate(self._model.properties,
+                       self._model.attributes,
+                       self._model.interfaces,
+                       self._model.artifacts,
+                       self._model.capabilities,
+                       self._model.outbound_relationships)
+
+    def dump(self, out_stream):
+        out_stream.write('Node: {0}'.format(out_stream.node_style(self._model.name)))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write('Template: {0}'.format(
+                out_stream.node_style(self._model.node_template.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.attributes, out_stream, title='Attributes')
+            self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
+            self._topology.dump(self._model.artifacts, out_stream, title='Artifacts')
+            self._topology.dump(self._model.capabilities, out_stream, title='Capabilities')
+            self._topology.dump(self._model.outbound_relationships, out_stream,
+                                title='Relationships')
+
+    def configure_operations(self):
+        for interface in self._model.interfaces.values():
+            self._topology.configure_operations(interface)
+        for relationship in self._model.outbound_relationships:
+            self._topology.configure_operations(relationship)
+
+    def validate_capabilities(self):
+        satisfied = False
+        for capability in self._model.capabilities.itervalues():
+            if not capability.has_enough_relationships:
+                self._topology.report(
+                    'capability "{0}" of node "{1}" requires at least {2:d} '
+                    'relationships but has {3:d}'.format(capability.name,
+                                                         self._model.name,
+                                                         capability.min_occurrences,
+                                                         capability.occurrences),
+                    level=self._topology.Issue.BETWEEN_INSTANCES)
+                satisfied = False
+        return satisfied
+
+    def satisfy_requirements(self):
+        satisfied = True
+        for requirement_template in self._model.node_template.requirement_templates:
+
+            # Since we try and satisfy requirements, which are node template bound, and use that
+            # information in the creation of the relationship, Some requirements may have been
+            # satisfied by a previous run on that node template.
+            # The entire mechanism of satisfying requirements needs to be refactored.
+            if any(rel.requirement_template == requirement_template
+                   for rel in self._model.outbound_relationships):
+                continue
+
+            # Find target template
+            target_node_template, target_node_capability = self._find_target(requirement_template)
+            if target_node_template is not None:
+                satisfied = self._satisfy_capability(
+                    target_node_capability, target_node_template, requirement_template)
+            else:
+                self._topology.report('requirement "{0}" of node "{1}" has no target node template'.
+                                      format(requirement_template.name, self._model.name),
+                                      level=self._topology.Issue.BETWEEN_INSTANCES)
+                satisfied = False
+        return satisfied
+
+    def _satisfy_capability(self, target_node_capability, target_node_template,
+                            requirement_template):
+        # Find target nodes
+        target_nodes = target_node_template.nodes
+        if target_nodes:
+            target_node = None
+            target_capability = None
+
+            if target_node_capability is not None:
+                # Relate to the first target node that has capacity
+                for node in target_nodes:
+                    a_target_capability = node.capabilities.get(target_node_capability.name)
+                    if a_target_capability.relate():
+                        target_node = node
+                        target_capability = a_target_capability
+                        break
+            else:
+                # Use first target node
+                target_node = target_nodes[0]
+
+            if target_node is not None:
+                if requirement_template.relationship_template is not None:
+                    relationship_model = self._topology.instantiate(
+                        requirement_template.relationship_template)
+                else:
+                    relationship_model = models.Relationship()
+                relationship_model.name = requirement_template.name
+                relationship_model.requirement_template = requirement_template
+                relationship_model.target_node = target_node
+                relationship_model.target_capability = target_capability
+                self._model.outbound_relationships.append(relationship_model)
+                return True
+            else:
+                self._topology.report(
+                    'requirement "{0}" of node "{1}" targets node '
+                    'template "{2}" but its instantiated nodes do not '
+                    'have enough capacity'.format(
+                        requirement_template.name, self._model.name, target_node_template.name),
+                    level=self._topology.Issue.BETWEEN_INSTANCES)
+                return False
+        else:
+            self._topology.report(
+                'requirement "{0}" of node "{1}" targets node template '
+                '"{2}" but it has no instantiated nodes'.format(
+                    requirement_template.name, self._model.name, target_node_template.name),
+                level=self._topology.Issue.BETWEEN_INSTANCES)
+            return False
+
+    def _find_target(self, requirement_template):
+        # We might already have a specific node template from the requirement template, so
+        # we'll just verify it
+        if requirement_template.target_node_template is not None:
+            if not self._model.node_template.is_target_node_template_valid(
+                    requirement_template.target_node_template):
+                self._topology.report(
+                    'requirement "{0}" of node template "{1}" is for node '
+                    'template "{2}" but it does not match constraints'.format(
+                        requirement_template.name,
+                        requirement_template.target_node_template.name,
+                        self._model.node_template.name),
+                    level=self._topology.Issue.BETWEEN_TYPES)
+            if (requirement_template.target_capability_type is not None or
+                    requirement_template.target_capability_name is not None):
+                target_node_capability = self._get_capability(requirement_template)
+                if target_node_capability is None:
+                    return None, None
+            else:
+                target_node_capability = None
+
+            return requirement_template.target_node_template, target_node_capability
+
+        # Find first node that matches the type
+        elif requirement_template.target_node_type is not None:
+            for target_node_template in \
+                    self._model.node_template.service_template.node_templates.itervalues():
+                if requirement_template.target_node_type.get_descendant(
+                        target_node_template.type.name) is None:
+                    continue
+
+                if not self._model.node_template.is_target_node_template_valid(
+                        target_node_template):
+                    continue
+
+                target_node_capability = self._get_capability(requirement_template,
+                                                              target_node_template)
+
+                if target_node_capability is None:
+                    continue
+
+                return target_node_template, target_node_capability
+
+        # Find the first node which has a capability of the required type
+        elif requirement_template.target_capability_type is not None:
+            for target_node_template in \
+                    self._model.node_template.service_template.node_templates.itervalues():
+                target_node_capability = \
+                    self._get_capability(requirement_template, target_node_template)
+                if target_node_capability:
+                    return target_node_template, target_node_capability
+
+        return None, None
+
+    def _get_capability(self, requirement_template, target_node_template=None):
+        target_node_template = target_node_template or requirement_template.target_node_template
+
+        for capability_template in target_node_template.capability_templates.values():
+            if self._satisfies_requirement(
+                    capability_template, requirement_template, target_node_template):
+                return capability_template
+
+        return None
+
+    def _satisfies_requirement(
+            self, capability_template, requirement_template, target_node_template):
+        # Do we match the required capability type?
+        if (requirement_template.target_capability_type and
+                requirement_template.target_capability_type.get_descendant(
+                    capability_template.type.name) is None):
+            return False
+
+        # Are we in valid_source_node_types?
+        if capability_template.valid_source_node_types:
+            for valid_source_node_type in capability_template.valid_source_node_types:
+                if valid_source_node_type.get_descendant(
+                        self._model.node_template.type.name) is None:
+                    return False
+
+        # Apply requirement constraints
+        if requirement_template.target_node_template_constraints:
+            for node_template_constraint in requirement_template.target_node_template_constraints:
+                if not node_template_constraint.matches(
+                        self._model.node_template, target_node_template):
+                    return False
+
+        return True
+
+
+class Operation(common.ActorHandlerBase):
+    def coerce(self, **kwargs):
+        self._coerce(self._model.inputs,
+                     self._model.configurations,
+                     self._model.arguments,
+                     **kwargs)
+
+    def validate(self, **kwargs):
+        self._validate(self._model.inputs,
+                       self._model.configurations,
+                       self._model.arguments,
+                       **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            if self._model.implementation is not None:
+                out_stream.write('Implementation: {0}'.format(
+                    out_stream.literal_style(self._model.implementation)))
+            if self._model.dependencies:
+                out_stream.write(
+                    'Dependencies: {0}'.format(', '.join((str(out_stream.literal_style(v))
+                                                          for v in self._model.dependencies))))
+            self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+            if self._model.executor is not None:
+                out_stream.write('Executor: {0}'.format(out_stream.literal_style(
+                    self._model.executor)))
+            if self._model.max_attempts is not None:
+                out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+                    self._model.max_attempts)))
+            if self._model.retry_interval is not None:
+                out_stream.write('Retry interval: {0}'.format(
+                    out_stream.literal_style(self._model.retry_interval)))
+            if self._model.plugin is not None:
+                out_stream.write('Plugin: {0}'.format(
+                    out_stream.literal_style(self._model.plugin.name)))
+            self._topology.dump(self._model.configurations, out_stream, title='Configuration')
+            if self._model.function is not None:
+                out_stream.write('Function: {0}'.format(out_stream.literal_style(
+                    self._model.function)))
+            self._topology.dump(self._model.arguments, out_stream, title='Arguments')
+
+    def configure_operations(self):
+        if self._model.implementation is None and self._model.function is None:
+            return
+
+        if (self._model.interface is not None and
+                self._model.plugin is None and
+                self._model.function is None):
+            # ("interface" is None for workflow operations, which do not currently use "plugin")
+            # The default (None) plugin is the execution plugin
+            execution_plugin.instantiation.configure_operation(self._model, self._topology)
+        else:
+            # In the future plugins may be able to add their own "configure_operation" hook that
+            # can validate the configuration and otherwise create specially derived arguments. For
+            # now, we just send all configuration parameters as arguments without validation.
+            for key, conf in self._model.configurations.items():
+                self._model.arguments[key] = self._topology.instantiate(conf.as_argument())
+
+        if self._model.interface is not None:
+            # Send all interface inputs as extra arguments
+            # ("interface" is None for workflow operations)
+            # Note that they will override existing arguments of the same names
+            for key, input in self._model.interface.inputs.items():
+                self._model.arguments[key] = self._topology.instantiate(input.as_argument())
+
+        # Send all inputs as extra arguments
+        # Note that they will override existing arguments of the same names
+        for key, input in self._model.inputs.items():
+            self._model.arguments[key] = self._topology.instantiate(input.as_argument())
+
+        # Check for reserved arguments
+        used_reserved_names = set(decorators.OPERATION_DECORATOR_RESERVED_ARGUMENTS).intersection(
+            self._model.arguments.keys())
+        if used_reserved_names:
+            self._topology.report(
+                'using reserved arguments in operation "{0}": {1}'.format(
+                    self._model.name, formatting.string_list_as_string(used_reserved_names)),
+                level=self._topology.Issue.EXTERNAL)
+
+
+class Policy(common.InstanceHandlerBase):
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write('Policy: {0}'.format(out_stream.node_style(self._model.name)))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            if self._model.nodes:
+                out_stream.write('Target nodes:')
+                with out_stream.indent():
+                    for node in self._model.nodes:
+                        out_stream.write(out_stream.node_style(node.name))
+            if self._model.groups:
+                out_stream.write('Target groups:')
+                with out_stream.indent():
+                    for group in self._model.groups:
+                        out_stream.write(out_stream.node_style(group.name))
+
+
+class Relationship(common.ActorHandlerBase):
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties,
+                     self._model.interfaces,
+                     **kwargs)
+
+    def validate(self, **kwargs):
+        self._validate(self._model.properties,
+                       self._model.interfaces,
+                       **kwargs)
+
+    def dump(self, out_stream):
+        if self._model.name:
+            out_stream.write('{0} ->'.format(out_stream.node_style(self._model.name)))
+        else:
+            out_stream.write('->')
+        with out_stream.indent():
+            out_stream.write('Node: {0}'.format(out_stream.node_style(
+                self._model.target_node.name)))
+            if self._model.target_capability:
+                out_stream.write('Capability: {0}'.format(out_stream.node_style(
+                    self._model.target_capability.name)))
+            if self._model.type is not None:
+                out_stream.write('Relationship type: {0}'.format(
+                    out_stream.type_style(self._model.type.name)))
+            if (self._model.relationship_template is not None and
+                    self._model.relationship_template.name):
+                out_stream.write('Relationship template: {0}'.format(
+                    out_stream.node_style(self._model.relationship_template.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
+
+    def configure_operations(self):
+        for interface in self._model.interfaces.values():
+            self._topology.configure_operations(interface)
+
+
+class Service(common.ActorHandlerBase):
+    def coerce(self, **kwargs):
+        self._coerce(self._model.meta_data,
+                     self._model.nodes,
+                     self._model.groups,
+                     self._model.policies,
+                     self._model.substitution,
+                     self._model.inputs,
+                     self._model.outputs,
+                     self._model.workflows,
+                     **kwargs)
+
+    def validate(self, **kwargs):
+        self._validate(self._model.meta_data,
+                       self._model.nodes,
+                       self._model.groups,
+                       self._model.policies,
+                       self._model.substitution,
+                       self._model.inputs,
+                       self._model.outputs,
+                       self._model.workflows,
+                       **kwargs)
+
+    def dump(self, out_stream):
+        if self._model.description is not None:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        self._topology.dump(self._model.meta_data, out_stream, title='Metadata')
+        self._topology.dump(self._model.nodes, out_stream)
+        self._topology.dump(self._model.groups, out_stream)
+        self._topology.dump(self._model.policies, out_stream)
+        self._topology.dump(self._model.substitution, out_stream)
+        self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+        self._topology.dump(self._model.outputs, out_stream, title='Outputs')
+        self._topology.dump(self._model.workflows, out_stream, title='Workflows')
+
+    def configure_operations(self):
+        for node in self._model.nodes.itervalues():
+            self._topology.configure_operations(node)
+        for group in self._model.groups.itervalues():
+            self._topology.configure_operations(group)
+        for operation in self._model.workflows.itervalues():
+            self._topology.configure_operations(operation)
+
+    def validate_capabilities(self):
+        satisfied = True
+        for node in self._model.nodes.values():
+            if not self._topology.validate_capabilities(node):
+                satisfied = False
+        return satisfied
+
+    def satisfy_requirements(self):
+        return all(self._topology.satisfy_requirements(node)
+                   for node in self._model.nodes.values())
+
+
+class Substitution(common.InstanceHandlerBase):
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.mappings, **kwargs)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.mappings, **kwargs)
+
+    def dump(self, out_stream):
+        out_stream.write('Substitution:')
+        with out_stream.indent():
+            out_stream.write('Node type: {0}'.format(out_stream.type_style(
+                self._model.node_type.name)))
+            self._topology.dump(self._model.mappings, out_stream, title='Mappings')
+
+
+class SubstitutionMapping(common.InstanceHandlerBase):
+
+    def coerce(self, **kwargs):
+        pass
+
+    def validate(self, **_):
+        if (self._model.capability is None) and (self._model.requirement_template is None):
+            self._topology.report(
+                'mapping "{0}" refers to neither capability nor a requirement'
+                ' in node: {1}'.format(
+                    self._model.name, formatting.safe_repr(self._model.node_style.name)),
+                level=self._topology.Issue.BETWEEN_TYPES)
+
+    def dump(self, out_stream):
+        if self._model.capability is not None:
+            out_stream.write('{0} -> {1}.{2}'.format(
+                out_stream.node_style(self._model.name),
+                out_stream.node_style(self._model.capability.node.name),
+                out_stream.node_style(self._model.capability.name)))
+        else:
+            out_stream.write('{0} -> {1}.{2}'.format(
+                out_stream.node_style(self._model.name),
+                out_stream.node_style(self._model.node.name),
+                out_stream.node_style(self._model.requirement_template.name)))
+
+
+class Metadata(common.InstanceHandlerBase):
+
+    def dump(self, out_stream):
+        out_stream.write('{0}: {1}'.format(
+            out_stream.property_style(self._model.name),
+            out_stream.literal_style(self._model.value)))
+
+    def coerce(self, **_):
+        pass
+
+    def instantiate(self, instance_cls):
+        return instance_cls(name=self._model.name, value=self._model.value)
+
+    def validate(self):
+        pass
+
+
+class _Parameter(common.InstanceHandlerBase):
+
+    def dump(self, out_stream):
+        if self._model.type_name is not None:
+            out_stream.write('{0}: {1} ({2})'.format(
+                out_stream.property_style(self._model.name),
+                out_stream.literal_style(formatting.as_raw(self._model.value)),
+                out_stream.type_style(self._model.type_name)))
+        else:
+            out_stream.write('{0}: {1}'.format(
+                out_stream.property_style(self._model.name),
+                out_stream.literal_style(formatting.as_raw(self._model.value))))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+
+    def instantiate(self, instance_cls, **kwargs):
+        return instance_cls(
+            name=self._model.name,                                                                  # pylint: disable=unexpected-keyword-arg
+            type_name=self._model.type_name,
+            _value=self._model._value,
+            description=self._model.description
+        )
+
+    def validate(self):
+        pass
+
+    def coerce(self, report_issues):                                                                # pylint: disable=arguments-differ
+        value = self._model._value
+        if value is not None:
+            evaluation = functions.evaluate(value, self._model, report_issues)
+            if (evaluation is not None) and evaluation.final:
+                # A final evaluation can safely replace the existing value
+                self._model._value = evaluation.value
+
+
+class Attribute(_Parameter):
+    pass
+
+
+class Input(_Parameter):
+    pass
+
+
+class Output(_Parameter):
+    pass
+
+
+class Argument(_Parameter):
+    pass
+
+
+class Property(_Parameter):
+    pass
+
+
+class Configuration(_Parameter):
+    pass
+
+
+class Type(common.InstanceHandlerBase):
+    def coerce(self, **_):
+        pass
+
+    def dump(self, out_stream):
+        if self._model.name:
+            out_stream.write(out_stream.type_style(self._model.name))
+        with out_stream.indent():
+            for child in self._model.children:
+                self._topology.dump(child, out_stream)
+
+    def validate(self, **kwargs):
+        pass

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/4ad58742/aria/orchestrator/topology/template_handler.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/template_handler.py b/aria/orchestrator/topology/template_handler.py
new file mode 100644
index 0000000..bf0ef9f
--- /dev/null
+++ b/aria/orchestrator/topology/template_handler.py
@@ -0,0 +1,606 @@
+# 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 datetime import datetime
+
+from ...utils import (
+    formatting,
+    versions
+)
+from ...modeling import utils as modeling_utils
+from . import utils, common
+
+
+class ServiceTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        if self._model.description is not None:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        self._topology.dump(self._model.meta_data, out_stream, title='Metadata')
+        self._topology.dump(self._model.node_templates, out_stream)
+        self._topology.dump(self._model.group_templates, out_stream)
+        self._topology.dump(self._model.policy_templates, out_stream)
+        self._topology.dump(self._model.substitution_template, out_stream)
+        self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+        self._topology.dump(self._model.outputs, out_stream, title='Outputs')
+        self._topology.dump(self._model.workflow_templates, out_stream, title='Workflow templates')
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.meta_data,
+                     self._model.node_templates,
+                     self._model.group_templates,
+                     self._model.policy_templates,
+                     self._model.substitution_template,
+                     self._model.inputs,
+                     self._model.outputs,
+                     self._model.workflow_templates,
+                     **kwargs)
+
+    def instantiate(self, instance_cls, inputs=None, plugins=None):                                 # pylint: disable=arguments-differ
+        now = datetime.now()
+
+        modeling_utils.validate_no_undeclared_inputs(
+            declared_inputs=self._model.inputs, supplied_inputs=inputs or {})
+        modeling_utils.validate_required_inputs_are_supplied(
+            declared_inputs=self._model.inputs, supplied_inputs=inputs or {})
+
+        service = instance_cls(
+            created_at=now,
+            updated_at=now,
+            description=utils.deepcopy_with_locators(self._model.description),
+            service_template=self._model,
+            inputs=modeling_utils.merge_parameter_values(inputs, self._model.inputs)
+        )
+
+        for plugin_specification in self._model.plugin_specifications.itervalues():
+            if plugin_specification.enabled and plugins:
+                if self._resolve_plugin_specification(plugin_specification, plugins):
+                    plugin = plugin_specification.plugin
+                    service.plugins[plugin.name] = plugin
+                else:
+                    self._topology.report('specified plugin not found: {0}'.format(
+                        plugin_specification.name), level=self._topology.Issue.EXTERNAL)
+        service.meta_data = self._topology.instantiate(self._model.meta_data)
+
+        for node_template in self._model.node_templates.itervalues():
+            for _ in range(self._scaling(node_template)['default_instances']):
+                node = self._topology.instantiate(node_template)
+                service.nodes[node.name] = node
+
+        service.groups = self._topology.instantiate(self._model.group_templates)
+        service.policies = self._topology.instantiate(self._model.policy_templates)
+        service.workflows = self._topology.instantiate(self._model.workflow_templates)
+        service.substitution = self._topology.instantiate(self._model.substitution_template)
+        service.outputs = self._topology.instantiate(self._model.outputs)
+
+        return service
+
+    @staticmethod
+    def _resolve_plugin_specification(plugin_specification, plugins):
+        matching_plugins = []
+        if plugins:
+            for plugin in plugins:
+                if (plugin.name == plugin_specification.name and
+                        (plugin_specification.version is None or
+                         versions.VersionString(plugin.package_version) >=
+                         plugin_specification.version)
+                   ):
+                    matching_plugins.append(plugin)
+        plugin_specification.plugin = None
+        if matching_plugins:
+            # Return highest version of plugin
+            plugin_specification.plugin = \
+                max(matching_plugins,
+                    key=lambda plugin: versions.VersionString(plugin.package_version).key)
+        return plugin_specification.plugin is not None
+
+    def _scaling(self, node_template):
+        scaling = node_template.scaling
+
+        if any([scaling['min_instances'] < 0,
+                scaling['max_instances'] < scaling['min_instances'],
+                scaling['max_instances'] < 0,
+
+                scaling['default_instances'] < 0,
+                scaling['default_instances'] < scaling['min_instances'],
+                scaling['default_instances'] > scaling['max_instances']
+               ]):
+            self._topology.report(
+                'invalid scaling parameters for node template "{0}": min={min_instances}, max='
+                '{max_instances}, default={default_instances}'.format(self._model.name, **scaling),
+                level=self._topology.Issue.BETWEEN_TYPES)
+
+        return scaling
+
+    def validate(self, **kwargs):
+        self._validate(
+            self._model.meta_data,
+            self._model.node_templates,
+            self._model.group_templates,
+            self._model.policy_templates,
+            self._model.substitution_template,
+            self._model.inputs,
+            self._model.outputs,
+            self._model.workflow_templates,
+            self._model.node_types,
+            self._model.group_types,
+            self._model.policy_types,
+            self._model.relationship_types,
+            self._model.capability_types,
+            self._model.interface_types,
+            self._model.artifact_types,
+            **kwargs
+        )
+
+
+class ArtifactTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+                self._model.type.name)))
+            out_stream.write('Source path: {0}'.format(out_stream.literal_style(
+                self._model.source_path)))
+            if self._model.target_path is not None:
+                out_stream.write('Target path: {0}'.format(out_stream.literal_style(
+                    self._model.target_path)))
+            if self._model.repository_url is not None:
+                out_stream.write('Repository URL: {0}'.format(
+                    out_stream.literal_style(self._model.repository_url)))
+            if self._model.repository_credential:
+                out_stream.write('Repository credential: {0}'.format(
+                    out_stream.literal_style(self._model.repository_credential)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        return instance_cls(
+            name=self._model.name,
+            type=self._model.type,
+            description=utils.deepcopy_with_locators(self._model.description),
+            source_path=self._model.source_path,
+            target_path=self._model.target_path,
+            repository_url=self._model.repository_url,
+            repository_credential=self._model.repository_credential,
+            artifact_template=self._model)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+
+class CapabilityTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            out_stream.write(
+                'Occurrences: {0:d}{1}'.format(
+                    self._model.min_occurrences or 0,
+                    ' to {0:d}'.format(self._model.max_occurrences)
+                    if self._model.max_occurrences is not None
+                    else ' or more'))
+            if self._model.valid_source_node_types:
+                out_stream.write('Valid source node types: {0}'.format(
+                    ', '.join((str(out_stream.type_style(v.name))
+                               for v in self._model.valid_source_node_types))))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        return instance_cls(name=self._model.name,
+                            type=self._model.type,
+                            min_occurrences=self._model.min_occurrences,
+                            max_occurrences=self._model.max_occurrences,
+                            occurrences=0,
+                            capability_template=self._model)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+
+class RequirementTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        if self._model.name:
+            out_stream.write(out_stream.node_style(self._model.name))
+        else:
+            out_stream.write('Requirement:')
+        with out_stream.indent():
+            if self._model.target_node_type is not None:
+                out_stream.write('Target node type: {0}'.format(
+                    out_stream.type_style(self._model.target_node_type.name)))
+            elif self._model.target_node_template is not None:
+                out_stream.write('Target node template: {0}'.format(
+                    out_stream.node_style(self._model.target_node_template.name)))
+            if self._model.target_capability_type is not None:
+                out_stream.write('Target capability type: {0}'.format(
+                    out_stream.type_style(self._model.target_capability_type.name)))
+            elif self._model.target_capability_name is not None:
+                out_stream.write('Target capability name: {0}'.format(
+                    out_stream.node_style(self._model.target_capability_name)))
+            if self._model.target_node_template_constraints:
+                out_stream.write('Target node template constraints:')
+                with out_stream.indent():
+                    for constraint in self._model.target_node_template_constraints:
+                        out_stream.write(out_stream.literal_style(constraint))
+            if self._model.relationship_template:
+                out_stream.write('Relationship:')
+                with out_stream.indent():
+                    self._topology.dump(self._model.relationship_template, out_stream)
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.relationship_template, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        pass
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.relationship_template, **kwargs)
+
+
+class GroupTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write('Group template: {0}'.format(out_stream.node_style(self._model.name)))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.interface_templates, out_stream,
+                                title='Interface Templates')
+            if self._model.node_templates:
+                out_stream.write('Member node templates: {0}'.format(', '.join(
+                    (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties,
+                     self._model.interface_templates,
+                     **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        group = instance_cls(
+            name=self._model.name,
+            type=self._model.type,
+            description=utils.deepcopy_with_locators(self._model.description),
+            group_template=self._model)
+        group.properties = self._topology.instantiate(self._model.properties)
+        group.interfaces = self._topology.instantiate(self._model.interface_templates)
+        if self._model.node_templates:
+            for node_template in self._model.node_templates:
+                group.nodes += node_template.nodes
+        return group
+
+    def validate(self, **kwargs):
+        self._validate(self._model.properties,
+                       self._model.interface_templates,
+                       **kwargs)
+
+
+class InterfaceTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Interface type: {0}'.format(out_stream.type_style(
+                self._model.type.name)))
+            self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+            self._topology.dump(self._model.operation_templates, out_stream,
+                                title='Operation templates')
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.inputs,
+                     self._model.operation_templates,
+                     **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        interface = instance_cls(
+            name=self._model.name,
+            type=self._model.type,
+            description=utils.deepcopy_with_locators(self._model.description),
+            interface_template=self._model)
+        interface.inputs = self._topology.instantiate(self._model.inputs)
+        interface.operations = self._topology.instantiate(self._model.operation_templates)
+        return interface
+
+    def validate(self, **kwargs):
+        self._validate(self._model.inputs,
+                       self._model.operation_templates,
+                       **kwargs)
+
+
+class NodeTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write('Node template: {0}'.format(out_stream.node_style(self._model.name)))
+        with out_stream.indent():
+            if self._model.description:
+                out_stream.write(out_stream.meta_style(self._model.description))
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.attributes, out_stream, title='Attributes')
+            self._topology.dump(
+                self._model.interface_templates, out_stream, title='Interface Templates')
+            self._topology.dump(
+                self._model.artifact_templates, out_stream, title='Artifact Templates')
+            self._topology.dump(
+                self._model.capability_templates, out_stream, title='Capability Templates')
+            self._topology.dump(
+                self._model.requirement_templates, out_stream, title='Requirement Templates')
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties,
+                     self._model.attributes,
+                     self._model.interface_templates,
+                     self._model.artifact_templates,
+                     self._model.capability_templates,
+                     self._model.requirement_templates,
+                     **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        node = instance_cls(
+            name=self._model._next_name,
+            type=self._model.type,
+            description=utils.deepcopy_with_locators(self._model.description),
+            node_template=self._model
+        )
+
+        node.properties = self._topology.instantiate(self._model.properties)
+        node.attributes = self._topology.instantiate(self._model.attributes)
+        node.interfaces = self._topology.instantiate(self._model.interface_templates)
+        node.artifacts = self._topology.instantiate(self._model.artifact_templates)
+        node.capabilities = self._topology.instantiate(self._model.capability_templates)
+
+        # Default attributes
+        if 'tosca_name' in node.attributes and node.attributes['tosca_name'].type_name == 'string':
+            node.attributes['tosca_name'].value = self._model.name
+        if 'tosca_id' in node.attributes and node.attributes['tosca_id'].type_name == 'string':
+            node.attributes['tosca_id'].value = node.name
+
+        return node
+
+    def validate(self, **kwargs):
+        self._validate(self._model.properties,
+                       self._model.attributes,
+                       self._model.interface_templates,
+                       self._model.artifact_templates,
+                       self._model.capability_templates,
+                       self._model.requirement_templates,
+                       **kwargs)
+
+
+class PolicyTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        out_stream.write('Policy template: {0}'.format(out_stream.node_style(self._model.name)))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            if self._model.node_templates:
+                out_stream.write('Target node templates: {0}'.format(', '.join(
+                    (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
+            if self._model.group_templates:
+                out_stream.write('Target group templates: {0}'.format(', '.join(
+                    (str(out_stream.node_style(v.name)) for v in self._model.group_templates))))
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.properties, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        policy = instance_cls(
+            name=self._model.name,
+            type=self._model.type,
+            description=utils.deepcopy_with_locators(self._model.description),
+            policy_template=self._model)
+
+        policy.properties = self._topology.instantiate(self._model.properties)
+        if self._model.node_templates:
+            for node_template in self._model.node_templates:
+                policy.nodes += node_template.nodes
+        if self._model.group_templates:
+            for group_template in self._model.group_templates:
+                policy.groups += group_template.groups
+        return policy
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.properties, **kwargs)
+
+
+class SubstitutionTemplate(common.TemplateHandlerBase):
+
+    def dump(self, out_stream):
+        out_stream.write('Substitution template:')
+        with out_stream.indent():
+            out_stream.write('Node type: {0}'.format(out_stream.type_style(
+                self._model.node_type.name)))
+            self._topology.dump(self._model.mappings, out_stream, title='Mappings')
+
+    def coerce(self, **kwargs):
+        self._topology.coerce(self._model.mappings, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        return instance_cls(node_type=self._model.node_type, substitution_template=self._model)
+
+    def validate(self, **kwargs):
+        self._topology.validate(self._model.mappings, **kwargs)
+
+
+class SubstitutionTemplateMapping(common.TemplateHandlerBase):
+
+    def dump(self, out_stream):
+        if self._topology.capability_template is not None:
+            node_template = self._model.capability_template.node_template
+        else:
+            node_template = self._model.requirement_template.node_template
+        out_stream.write('{0} -> {1}.{2}'.format(
+            out_stream.node_style(self._model.name),
+            out_stream.node_style(node_template.name),
+            out_stream.node_style(self._model.capability_template.name
+                                  if self._model.capability_template
+                                  else self._model.requirement_template.name)))
+
+    def coerce(self, **_):
+        pass
+
+    def instantiate(self, instance_cls, **_):
+        substitution_mapping = instance_cls(
+            name=self._model.name,
+            requirement_template=self._model.requirement_template)
+
+        if self._model.capability_template is not None:
+            node_template = self._model.capability_template.node_template
+        else:
+            node_template = self._model.requirement_template.node_template
+        nodes = node_template.nodes
+        if len(nodes) == 0:
+            self._topology.report(
+                'mapping "{0}" refers to node template "{1}" but there are no node instances'.
+                format(self._model.mapped_name, self._model.node_template.name),
+                level=self._topology.Issue.BETWEEN_INSTANCES)
+            return None
+        # The TOSCA spec does not provide a way to choose the node,
+        # so we will just pick the first one
+        substitution_mapping.node_style = nodes[0]
+        if self._model.capability_template:
+            for a_capability in substitution_mapping.node_style.capabilities.itervalues():
+                if a_capability.capability_template.name == \
+                        self._model.capability_template.name:
+                    substitution_mapping.capability = a_capability
+
+        return substitution_mapping
+
+    def validate(self, **_):
+        if self._model.capability_template is None and self._model.requirement_template is None:
+            self._topology.report(
+                'mapping "{0}" refers to neither capability nor a requirement '
+                'in node template: {1}'.format(
+                    self._model.name, formatting.safe_repr(self._model.node_template.name)),
+                level=self._topology.Issue.BETWEEN_TYPES)
+
+
+class RelationshipTemplate(common.TemplateHandlerBase):
+    def dump(self, out_stream):
+        if self._model.type is not None:
+            out_stream.write('Relationship type: {0}'.format(out_stream.type_style(
+                self._model.type.name)))
+        else:
+            out_stream.write('Relationship template: {0}'.format(
+                out_stream.node_style(self._model.name)))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            self._topology.dump(self._model.properties, out_stream, title='Properties')
+            self._topology.dump(self._model.interface_templates, out_stream,
+                                title='Interface Templates')
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.properties, self._model.interface_templates, **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        relationship = instance_cls(
+            name=self._model.name,
+            type=self._model.type,
+            relationship_template=self._model)
+
+        relationship.properties = self._topology.instantiate(self._model.properties)
+        relationship.interfaces = self._topology.instantiate(self._model.interface_templates)
+        return relationship
+
+    def validate(self, **kwargs):
+        self._validate(self._model.properties, self._model.interface_templates, **kwargs)
+
+
+class OperationTemplate(common.TemplateHandlerBase):
+
+    def dump(self, out_stream):
+        out_stream.write(out_stream.node_style(self._model.name))
+        if self._model.description:
+            out_stream.write(out_stream.meta_style(self._model.description))
+        with out_stream.indent():
+            if self._model.implementation is not None:
+                out_stream.write('Implementation: {0}'.format(
+                    out_stream.literal_style(self._model.implementation)))
+            if self._model.dependencies:
+                out_stream.write('Dependencies: {0}'.format(', '.join(
+                    (str(out_stream.literal_style(v)) for v in self._model.dependencies))))
+            self._topology.dump(self._model.inputs, out_stream, title='Inputs')
+            if self._model.executor is not None:
+                out_stream.write('Executor: {0}'.format(
+                    out_stream.literal_style(self._model.executor)))
+            if self._model.max_attempts is not None:
+                out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+                    self._model.max_attempts)))
+            if self._model.retry_interval is not None:
+                out_stream.write('Retry interval: {0}'.format(
+                    out_stream.literal_style(self._model.retry_interval)))
+            if self._model.plugin_specification is not None:
+                out_stream.write('Plugin specification: {0}'.format(
+                    out_stream.literal_style(self._model.plugin_specification.name)))
+            self._topology.dump(self._model.configurations, out_stream, title='Configuration')
+            if self._model.function is not None:
+                out_stream.write('Function: {0}'.format(out_stream.literal_style(
+                    self._model.function)))
+
+    def coerce(self, **kwargs):
+        self._coerce(self._model.inputs,
+                     self._model.configurations,
+                     **kwargs)
+
+    def instantiate(self, instance_cls, **_):
+        operation = instance_cls(
+            name=self._model.name,
+            description=utils.deepcopy_with_locators(self._model.description),
+            relationship_edge=self._model.relationship_edge,
+            implementation=self._model.implementation,
+            dependencies=self._model.dependencies,
+            executor=self._model.executor,
+            function=self._model.function,
+            max_attempts=self._model.max_attempts,
+            retry_interval=self._model.retry_interval,
+            operation_template=self._model)
+
+        if (self._model.plugin_specification is not None and
+                self._model.plugin_specification.enabled):
+            operation.plugin = self._model.plugin_specification.plugin
+
+        operation.inputs = self._topology.instantiate(self._model.inputs)
+        operation.configurations = self._topology.instantiate(self._model.configurations)
+
+        return operation
+
+    def validate(self, **kwargs):
+        self._validate(self._model.inputs,
+                       self._model.configurations,
+                       **kwargs)
+
+
+class PluginSpecification(common.HandlerBase):
+    def validate(self, **kwargs):
+        pass
+
+    def coerce(self, **kwargs):
+        pass
+
+    def instantiate(self, **_):
+        pass
+
+    def dump(self, out_stream):
+        pass