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