| # 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, **_): |
| capability = 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) |
| capability.properties = self._topology.instantiate(self._model.properties) |
| return capability |
| |
| 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._model.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 |