ariatosca-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mxm...@apache.org
Subject [2/3] incubator-ariatosca git commit: ARIA-174 Refactor instantiation phase
Date Wed, 09 Aug 2017 12:52:41 GMT
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/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/df2b916e/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/df2b916e/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/df2b916e/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/df2b916e/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/df2b916e/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


Mime
View raw message