removed the old style class from consumption, removed any unnecessary and local imports
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/ec7b9c97 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/ec7b9c97 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/ec7b9c97 Branch: refs/heads/ARIA-174-Refactor-instantiation-phase Commit: ec7b9c9702509f6619d4ae20508d06134ee10f26 Parents: bb67dbb Author: max-orlov <[email protected]> Authored: Thu Jul 27 16:45:03 2017 +0300 Committer: max-orlov <[email protected]> Committed: Thu Jul 27 17:15:25 2017 +0300 ---------------------------------------------------------------------- aria/cli/commands/service_templates.py | 6 +- aria/cli/commands/services.py | 6 +- aria/core.py | 24 +- aria/orchestrator/topology/__init__.py | 216 +------ aria/orchestrator/topology/instance.py | 633 -------------------- aria/orchestrator/topology/instance_handler.py | 633 ++++++++++++++++++++ aria/orchestrator/topology/template.py | 583 ------------------ aria/orchestrator/topology/template_handler.py | 583 ++++++++++++++++++ aria/orchestrator/topology/topology.py | 256 ++++++++ aria/parser/consumption/__init__.py | 3 - aria/parser/consumption/consumer.py | 9 +- aria/parser/consumption/context.py | 2 - aria/parser/consumption/modeling.py | 24 +- aria/parser/consumption/style.py | 50 -- 14 files changed, 1507 insertions(+), 1521 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/aria/cli/commands/service_templates.py ---------------------------------------------------------------------- diff --git a/aria/cli/commands/service_templates.py b/aria/cli/commands/service_templates.py index 6eef97a..482170b 100644 --- a/aria/cli/commands/service_templates.py +++ b/aria/cli/commands/service_templates.py @@ -28,7 +28,7 @@ from ...core import Core from ...storage import exceptions as storage_exceptions from ...parser import consumption from ...utils import (formatting, collections, console) -from ... orchestrator import topology +from ... orchestrator.topology import Topology DESCRIPTION_FIELD_LENGTH_LIMIT = 20 SERVICE_TEMPLATE_COLUMNS = \ @@ -73,9 +73,9 @@ def show(service_template_name, model_storage, mode_full, mode_types, format_jso elif format_yaml: console.puts(formatting.yaml_dumps(collections.prune(service_template.as_raw))) else: - logger.info(topology.Handler().dump(service_template)) + logger.info(Topology().dump(service_template)) elif mode_types: - logger.info(topology.Handler().dump_types(service_template=service_template)) + logger.info(Topology().dump_types(service_template=service_template)) else: logger.info('Showing service template {0}...'.format(service_template_name)) service_template_dict = service_template.to_dict() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/aria/cli/commands/services.py ---------------------------------------------------------------------- diff --git a/aria/cli/commands/services.py b/aria/cli/commands/services.py index 93bea7f..3b07866 100644 --- a/aria/cli/commands/services.py +++ b/aria/cli/commands/services.py @@ -20,7 +20,7 @@ CLI ``services`` sub-commands. import os from StringIO import StringIO -from aria.orchestrator import topology +from aria.orchestrator.topology import Topology from . import service_templates from .. import helptexts from .. import table @@ -74,9 +74,9 @@ def show(service_name, model_storage, mode_full, mode_graph, format_json, format elif format_yaml: console.puts(formatting.yaml_dumps(collections.prune(service.as_raw))) else: - logger.info(topology.Handler().dump(service)) + logger.info(Topology().dump(service)) elif mode_graph: - logger.info(topology.Handler().dump_graph(service)) + logger.info(Topology().dump_graph(service)) else: logger.info('Showing service {0}...'.format(service_name)) service_dict = service.to_dict() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/aria/core.py ---------------------------------------------------------------------- diff --git a/aria/core.py b/aria/core.py index 444bcb9..e726be7 100644 --- a/aria/core.py +++ b/aria/core.py @@ -17,10 +17,10 @@ ARIA core module. """ -from aria.orchestrator import topology from . import exceptions from .parser import consumption from .parser.loading.location import UriLocation +from .orchestrator.topology import Topology class Core(object): @@ -74,20 +74,20 @@ class Core(object): # setting no autoflush for the duration of instantiation - this helps avoid dependency # constraints as they're being set up with storage_session.no_autoflush: - handler = topology.Handler(self.model_storage) - service = handler.instantiate(service_template, inputs=inputs) - handler.coerce(service) + topology = Topology(self.model_storage) + service = topology.instantiate(service_template, inputs=inputs) + topology.coerce(service) - handler.validate(service) - handler.satisfy_requirements(service) - handler.coerce(service) + topology.validate(service) + topology.satisfy_requirements(service) + topology.coerce(service) - handler.validate_capabilities(service) - handler.find_hosts(service) - handler.configure_operations(service) - handler.coerce(service) + topology.validate_capabilities(service) + topology.find_hosts(service) + topology.configure_operations(service) + topology.coerce(service) - if handler.dump_issues(): + if topology.dump_issues(): raise exceptions.InstantiationError('Failed to instantiate service template `{0}`' .format(service_template.name)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/aria/orchestrator/topology/__init__.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/topology/__init__.py b/aria/orchestrator/topology/__init__.py index 1ba12aa..099a950 100644 --- a/aria/orchestrator/topology/__init__.py +++ b/aria/orchestrator/topology/__init__.py @@ -12,219 +12,5 @@ # 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 StringIO import StringIO -from ...parser.validation import issue -from ...parser.consumption.style import Style -from ...modeling import models -from ...utils import console as console_utils -from . import ( - template, - instance, - common -) - - -class Handler(issue.Reporter): - - _init_map = { - models.ServiceTemplate: models.Service, - models.ArtifactTemplate: models.Artifact, - models.CapabilityTemplate: models.Capability, - models.GroupTemplate: models.Group, - models.InterfaceTemplate: models.Interface, - models.NodeTemplate: models.Node, - models.PolicyTemplate: models.Policy, - models.SubstitutionTemplate: models.Substitution, - models.RelationshipTemplate: models.Relationship, - models.OperationTemplate: models.Operation, - models.RequirementTemplate: None, - models.SubstitutionTemplateMapping: models.SubstitutionMapping, - - # Common - models.Metadata: models.Metadata, - models.Attribute: models.Attribute, - models.Property: models.Property, - models.Input: models.Input, - models.Output: models.Output, - models.Configuration: models.Configuration, - models.Argument: models.Argument, - models.Type: models.Type - } - - class TopologyStylizer(object): - def __init__(self): - self._style = Style() - self._str = StringIO() - - def write(self, str_): - console_utils.puts(str_, stream=self._str) - - def __repr__(self): - return self._str.getvalue() - - def __str__(self): - return repr(self) - - def __getattr__(self, item): - try: - return getattr(self._style, item) - except AttributeError: - return super(Handler.TopologyStylizer, self).__getattribute__(item) - - def __init__(self, model_storage=None, *args, **kwargs): - # TODO: model storage is required only for the list of plugins, can we get it - # somewhere else? - super(Handler, self).__init__(*args, **kwargs) - self._model_storage = model_storage - self._handlers = dict(self._init_handlers(instance), **self._init_handlers(template)) - - @staticmethod - def _init_handlers(module_): - handlers = {} - for attribute_name in dir(module_): - if attribute_name.startswith('_'): - continue - attribute = getattr(module_, attribute_name) - if isinstance(attribute, type) and issubclass(attribute, common._Handler): - handlers[getattr(models, attribute_name)] = attribute - return handlers - - def instantiate(self, model, **kwargs): - """ - all handlers used by instantiate should hold a tuple as value (handler, instnace_cls) - :param model: - :param kwargs: - :return: - """ - if isinstance(model, dict): - return dict((name, self.instantiate(value, **kwargs)) - for name, value in model.iteritems()) - elif isinstance(model, list): - return list(self.instantiate(value, **kwargs) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - instance_cls = self._init_map.get(model.__class__) - return _handler(self, model).instantiate(instance_cls, **kwargs) - - def validate(self, model, **kwargs): - if isinstance(model, dict): - return self.validate(model.values()) - elif isinstance(model, list): - return all(self.validate(value) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - return _handler(self, model).validate(**kwargs) - - def dump(self, model, console=None, section_name=None, **kwargs): - console = console or self.TopologyStylizer() - - # if model is empty, no need to print out the section name - if model and section_name: - console.write('{0}:'.format(section_name)) - - if isinstance(model, dict): - return self.dump(model.values(), console=console, **kwargs) - elif isinstance(model, list): - with console.indent: - for value in model: - self.dump(value, console=console, **kwargs) - elif model is not None: - _handler = self._handlers.get(model.__class__) - _handler(self, model).dump(console=console, **kwargs) - return str(console) - - def dump_graph(self, service, **kwargs): - console = self.TopologyStylizer() - for node in service.nodes.itervalues(): - if not node.inbound_relationships: - self._dump_graph_node(console, node) - - def _dump_graph_node(self, console, node, capability=None): - console.write(console.style.node(node.name)) - if capability is not None: - console.write('{0} ({1})'.format(console.style.property(capability.name), - console.style.type(capability.type.name))) - if node.outbound_relationships: - with console.style.indent: - for relationship_model in node.outbound_relationships: - relationship_name = console.style.property(relationship_model.name) - if relationship_model.type is not None: - console.write('-> {0} ({1})'.format( - relationship_name, console.style.type(relationship_model.type.name))) - else: - console.write('-> {0}'.format(relationship_name)) - with console.indent(3): - self._dump_graph_node(relationship_model.target_node, - relationship_model.target_capability) - - def coerce(self, model, **kwargs): - if isinstance(model, dict): - return self.validate(model.values()) - elif isinstance(model, list): - return all(self.validate(value) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - return _handler(self, model).coerce(**kwargs) - - def dump_types(self, service_template, console=None): - console = console or self.TopologyStylizer() - console.write(self.dump(service_template.node_types, console)) - console.write(self.dump(service_template.group_types, console)) - console.write(self.dump(service_template.capability_types, console)) - console.write(self.dump(service_template.relationship_types, console)) - console.write(self.dump(service_template.policy_types, console)) - console.write(self.dump(service_template.artifact_types, console)) - console.write(self.dump(service_template.interface_types, console)) - - return str(console) - - def satisfy_requirements(self, model, **kwargs): - if isinstance(model, dict): - return self.satisfy_requirements(model.values()) - elif isinstance(model, list): - return all(self.satisfy_requirements(value) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - return _handler(self, model).satisfy_requirements(**kwargs) - - def validate_capabilities(self, model, **kwargs): - if isinstance(model, dict): - return self.validate_capabilities(model.values()) - elif isinstance(model, list): - return all(self.validate_capabilities(value) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - return _handler(self, model).validate_capabilities(**kwargs) - - def _find_host(self, node): - if node.type.role == 'host': - return node - - has_role = lambda rel, role: \ - rel.target_capability is not None and rel.target_capability.type.role == role - - for relationship in node.outbound_relationships: - if has_role(relationship, 'host'): - host = self._find_host(relationship.target_node) - if host is not None: - return host - for relationship in node.inbound_relationships: - if has_role(relationship, 'feature'): - host = self._find_host(relationship.source_node) - if host is not None: - return host - return None - - def find_hosts(self, service): - for node in service.nodes.values(): - node.host = self._find_host(node) - - def configure_operations(self, model, **kwargs): - if isinstance(model, dict): - return self.configure_operations(model.values()) - elif isinstance(model, list): - return all(self.configure_operations(value) for value in model) - elif model is not None: - _handler = self._handlers.get(model.__class__) - return _handler(self, model).configure_operations(**kwargs) +from .topology import Topology http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/aria/orchestrator/topology/instance.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/topology/instance.py b/aria/orchestrator/topology/instance.py deleted file mode 100644 index dc204fe..0000000 --- a/aria/orchestrator/topology/instance.py +++ /dev/null @@ -1,633 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from ... parser.modeling import context -from ... modeling import models -from ... utils import formatting -from .. import execution_plugin -from .. import decorators -from . import common - - -class Artifact(common._InstanceHandlerMixin): - - def coerce(self, **kwargs): - self._topology.coerce(self._model.properties, **kwargs) - - def validate(self, **kwargs): - self._topology.validate(self._model.properties) - - def dump(self, console): - console.write(console.node(self._model.name)) - if self._model.description: - console.write(console.meta(self._model.description)) - with console.indent: - console.write('Artifact type: {0}'.format(console.type(self._model.type.name))) - console.write('Source path: {0}'.format( - console.literal(self._model.source_path))) - if self._model.target_path is not None: - console.write('Target path: {0}'.format( - console.literal(self._model.target_path))) - if self._model.repository_url is not None: - console.write('Repository URL: {0}'.format( - console.literal(self._model.repository_url))) - if self._model.repository_credential: - console.write('Repository credential: {0}'.format( - console.literal(self._model.repository_credential))) - self._topology.dump(self._model.properties, console, 'Properties') - - -class Capability(common._InstanceHandlerMixin): - def coerce(self, **kwargs): - self._topology.coerce(self._model.properties, **kwargs) - - def validate(self, **kwargs): - self._topology.validate(self._model.properties) - - def dump(self, console): - console.write(console.node(self._model.name)) - with console.indent: - console.write('Type: {0}'.format(console.type(self._model.type.name))) - console.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, console, 'Properties') - - -class Group(common._OperatorHolderHandlerMixin): - - 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) - - def dump(self, console): - console.write('Group: {0}'.format(console.node(self._model.name))) - with console.indent: - console.write('Type: {0}'.format(console.type(self._model.type.name))) - self._topology.dump(self._model.properties, console, 'Properties') - self._topology.dump(self._model.interfaces, console, 'Interfaces') - if self._model.nodes: - console.write('Member nodes:') - with console.indent: - for node in self._model.nodes: - console.write(console.node(node.name)) - - def configure_operations(self): - for interface in self._model.interfaces.values(): - self._topology.configure_operations(interface) - - -class Interface(common._OperatorHolderHandlerMixin): - 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) - - def dump(self, console): - console.write(console.node(self._model.name)) - if self._model.description: - console.write(console.meta(self._model.description)) - with console.indent: - console.write('Interface type: {0}'.format(console.type(self._model.type.name))) - self._topology.dump(self._model.inputs, console, 'Inputs') - self._topology.dump(self._model.operations, console, 'Operations') - - def configure_operations(self): - for operation in self._model.operations.values(): - self._topology.configure_operations(operation) - - -class Node(common._OperatorHolderHandlerMixin): - 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, console): - console.write('Node: {0}'.format(console.node(self._model.name))) - with console.indent: - console.write('Type: {0}'.format(console.type(self._model.type.name))) - console.write('Template: {0}'.format(console.node(self._model.node_template.name))) - self._topology.dump(self._model.properties, console, 'Properties') - self._topology.dump(self._model.attributes, console, 'Attributes') - self._topology.dump(self._model.interfaces, console, 'Interfaces') - self._topology.dump(self._model.artifacts, console, 'Artifacts') - self._topology.dump(self._model.capabilities, console, 'Capabilities') - self._topology.dump(self._model.outbound_relationships, console, '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(r.requirement_template == requirement_template - for r in self._model.outbound_relationships): - return satisfied - - # 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, 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 - - 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._OperatorHolderHandlerMixin): - 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) - - def dump(self, console): - console.write(console.node(self._model.name)) - if self._model.description: - console.write(console.meta(self._model.description)) - with console.indent: - if self._model.implementation is not None: - console.write('Implementation: {0}'.format( - console.literal(self._model.implementation))) - if self._model.dependencies: - console.write( - 'Dependencies: {0}'.format( - ', '.join((str(console.literal(v)) for v in self._model.dependencies)))) - self._topology.dump(self._model.inputs, console, 'Inputs') - if self._model.executor is not None: - console.write('Executor: {0}'.format(console.literal(self._model.executor))) - if self._model.max_attempts is not None: - console.write('Max attempts: {0}'.format(console.literal(self._model.max_attempts))) - if self._model.retry_interval is not None: - console.write('Retry interval: {0}'.format( - console.literal(self._model.retry_interval))) - if self._model.plugin is not None: - console.write('Plugin: {0}'.format( - console.literal(self._model.plugin.name))) - self._topology.dump(self._model.configurations, console, 'Configuration') - if self._model.function is not None: - console.write('Function: {0}'.format(console.literal(self._model.function))) - self._topology.dump(self._model.arguments, console, '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) - 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._InstanceHandlerMixin): - def coerce(self, **kwargs): - self._topology.coerce(self._model.properties, **kwargs) - - def validate(self, **kwargs): - self._topology.validate(self._model.properties) - - def dump(self, console): - console.write('Policy: {0}'.format(console.node(self._model.name))) - with console.indent: - console.write('Type: {0}'.format(console.type(self._model.type.name))) - self._topology.dump(self._model.properties, console, 'Properties') - if self._model.nodes: - console.write('Target nodes:') - with console.indent: - for node in self._model.nodes: - console.write(console.node(node.name)) - if self._model.groups: - console.write('Target groups:') - with console.indent: - for group in self._model.groups: - console.write(console.node(group.name)) - - -class Relationship(common._OperatorHolderHandlerMixin): - 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) - - def dump(self, console): - if self._model.name: - console.write('{0} ->'.format(console.node(self._model.name))) - else: - console.write('->') - with console.indent: - console.write('Node: {0}'.format(console.node(self._model.target_node.name))) - if self._model.target_capability: - console.write('Capability: {0}'.format(console.node( - self._model.target_capability.name))) - if self._model.type is not None: - console.write('Relationship type: {0}'.format( - console.type(self._model.type.name))) - if (self._model.relationship_template is not None and - self._model.relationship_template.name): - console.write('Relationship template: {0}'.format( - console.node(self._model.relationship_template.name))) - self._topology.dump(self._model.properties, console, 'Properties') - self._topology.dump(self._model.interfaces, console, 'Interfaces') - - def configure_operations(self): - for interface in self._model.interfaces.values(): - self._topology.configure_operations(interface) - - -class Service(common._OperatorHolderHandlerMixin): - 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) - - def dump(self, console): - if self._model.description is not None: - console.write(console.meta(self._model.description)) - self._topology.dump(self._model.meta_data, console, 'Metadata') - self._topology.dump(self._model.nodes, console) - self._topology.dump(self._model.groups, console) - self._topology.dump(self._model.policies, console) - self._topology.dump(self._model.substitution, console) - self._topology.dump(self._model.inputs, console, 'Inputs') - self._topology.dump(self._model.outputs, console, 'Outputs') - self._topology.dump(self._model.workflows, console, '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._InstanceHandlerMixin): - def coerce(self, **kwargs): - self._topology.coerce(self._model.mappings, **kwargs) - - def validate(self, **kwargs): - self._topology.validate(self._model.mappings) - - def dump(self, console): - console.write('Substitution:') - with console.indent: - console.write('Node type: {0}'.format(console.type(self._model.node_type.name))) - self._topology.dump(self._model.mappings, console, 'Mappings') - - -class SubstitutionMapping(common._InstanceHandlerMixin): - - def validate(self, **kwargs): - 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.name)), - level=self._topology.Issue.BETWEEN_TYPES) - - def dump(self, console): - if self._model.capability is not None: - console.write('{0} -> {1}.{2}'.format( - console.node(self._model.name), - console.node(self._model.capability.node.name), - console.node(self._model.capability.name))) - else: - console.write('{0} -> {1}.{2}'.format( - console.node(self._model.name), - console.node(self._model.node.name), - console.node(self._model.requirement_template.name))) - - -class Metadata(common._InstanceHandlerMixin): - - def dump(self, console): - console.write('{0}: {1}'.format( - console.property(self._topology.name), - console.literal(self._topology.value))) - - def coerce(self): - pass - - def instantiate(self, instance_cls, **kwargs): - return instance_cls(name=self._model.name, value=self._model.value) - - def validate(self): - pass - - -class _Parameter(common._InstanceHandlerMixin): - - def dump(self, console): - if self._model.type_name is not None: - console.write('{0}: {1} ({2})'.format( - console.property(self._model.name), - console.literal(formatting.as_raw(self._model.value)), - console.type(self._model.type_name))) - else: - console.write('{0}: {1}'.format( - console.property(self._model.name), - console.literal(formatting.as_raw(self._model.value)))) - if self._model.description: - console.write(console.meta(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 - - -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._InstanceHandlerMixin): - def coerce(self): - pass - - def dump(self, console): - if self._model.name: - console.write(console.type(self._model.name)) - with console.indent: - for child in self._model.children: - self._topology.dump(child, console) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/ec7b9c97/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..dc204fe --- /dev/null +++ b/aria/orchestrator/topology/instance_handler.py @@ -0,0 +1,633 @@ +# 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 +from ... utils import formatting +from .. import execution_plugin +from .. import decorators +from . import common + + +class Artifact(common._InstanceHandlerMixin): + + def coerce(self, **kwargs): + self._topology.coerce(self._model.properties, **kwargs) + + def validate(self, **kwargs): + self._topology.validate(self._model.properties) + + def dump(self, console): + console.write(console.node(self._model.name)) + if self._model.description: + console.write(console.meta(self._model.description)) + with console.indent: + console.write('Artifact type: {0}'.format(console.type(self._model.type.name))) + console.write('Source path: {0}'.format( + console.literal(self._model.source_path))) + if self._model.target_path is not None: + console.write('Target path: {0}'.format( + console.literal(self._model.target_path))) + if self._model.repository_url is not None: + console.write('Repository URL: {0}'.format( + console.literal(self._model.repository_url))) + if self._model.repository_credential: + console.write('Repository credential: {0}'.format( + console.literal(self._model.repository_credential))) + self._topology.dump(self._model.properties, console, 'Properties') + + +class Capability(common._InstanceHandlerMixin): + def coerce(self, **kwargs): + self._topology.coerce(self._model.properties, **kwargs) + + def validate(self, **kwargs): + self._topology.validate(self._model.properties) + + def dump(self, console): + console.write(console.node(self._model.name)) + with console.indent: + console.write('Type: {0}'.format(console.type(self._model.type.name))) + console.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, console, 'Properties') + + +class Group(common._OperatorHolderHandlerMixin): + + 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) + + def dump(self, console): + console.write('Group: {0}'.format(console.node(self._model.name))) + with console.indent: + console.write('Type: {0}'.format(console.type(self._model.type.name))) + self._topology.dump(self._model.properties, console, 'Properties') + self._topology.dump(self._model.interfaces, console, 'Interfaces') + if self._model.nodes: + console.write('Member nodes:') + with console.indent: + for node in self._model.nodes: + console.write(console.node(node.name)) + + def configure_operations(self): + for interface in self._model.interfaces.values(): + self._topology.configure_operations(interface) + + +class Interface(common._OperatorHolderHandlerMixin): + 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) + + def dump(self, console): + console.write(console.node(self._model.name)) + if self._model.description: + console.write(console.meta(self._model.description)) + with console.indent: + console.write('Interface type: {0}'.format(console.type(self._model.type.name))) + self._topology.dump(self._model.inputs, console, 'Inputs') + self._topology.dump(self._model.operations, console, 'Operations') + + def configure_operations(self): + for operation in self._model.operations.values(): + self._topology.configure_operations(operation) + + +class Node(common._OperatorHolderHandlerMixin): + 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, console): + console.write('Node: {0}'.format(console.node(self._model.name))) + with console.indent: + console.write('Type: {0}'.format(console.type(self._model.type.name))) + console.write('Template: {0}'.format(console.node(self._model.node_template.name))) + self._topology.dump(self._model.properties, console, 'Properties') + self._topology.dump(self._model.attributes, console, 'Attributes') + self._topology.dump(self._model.interfaces, console, 'Interfaces') + self._topology.dump(self._model.artifacts, console, 'Artifacts') + self._topology.dump(self._model.capabilities, console, 'Capabilities') + self._topology.dump(self._model.outbound_relationships, console, '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(r.requirement_template == requirement_template + for r in self._model.outbound_relationships): + return satisfied + + # 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, 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 + + 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._OperatorHolderHandlerMixin): + 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) + + def dump(self, console): + console.write(console.node(self._model.name)) + if self._model.description: + console.write(console.meta(self._model.description)) + with console.indent: + if self._model.implementation is not None: + console.write('Implementation: {0}'.format( + console.literal(self._model.implementation))) + if self._model.dependencies: + console.write( + 'Dependencies: {0}'.format( + ', '.join((str(console.literal(v)) for v in self._model.dependencies)))) + self._topology.dump(self._model.inputs, console, 'Inputs') + if self._model.executor is not None: + console.write('Executor: {0}'.format(console.literal(self._model.executor))) + if self._model.max_attempts is not None: + console.write('Max attempts: {0}'.format(console.literal(self._model.max_attempts))) + if self._model.retry_interval is not None: + console.write('Retry interval: {0}'.format( + console.literal(self._model.retry_interval))) + if self._model.plugin is not None: + console.write('Plugin: {0}'.format( + console.literal(self._model.plugin.name))) + self._topology.dump(self._model.configurations, console, 'Configuration') + if self._model.function is not None: + console.write('Function: {0}'.format(console.literal(self._model.function))) + self._topology.dump(self._model.arguments, console, '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) + 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._InstanceHandlerMixin): + def coerce(self, **kwargs): + self._topology.coerce(self._model.properties, **kwargs) + + def validate(self, **kwargs): + self._topology.validate(self._model.properties) + + def dump(self, console): + console.write('Policy: {0}'.format(console.node(self._model.name))) + with console.indent: + console.write('Type: {0}'.format(console.type(self._model.type.name))) + self._topology.dump(self._model.properties, console, 'Properties') + if self._model.nodes: + console.write('Target nodes:') + with console.indent: + for node in self._model.nodes: + console.write(console.node(node.name)) + if self._model.groups: + console.write('Target groups:') + with console.indent: + for group in self._model.groups: + console.write(console.node(group.name)) + + +class Relationship(common._OperatorHolderHandlerMixin): + 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) + + def dump(self, console): + if self._model.name: + console.write('{0} ->'.format(console.node(self._model.name))) + else: + console.write('->') + with console.indent: + console.write('Node: {0}'.format(console.node(self._model.target_node.name))) + if self._model.target_capability: + console.write('Capability: {0}'.format(console.node( + self._model.target_capability.name))) + if self._model.type is not None: + console.write('Relationship type: {0}'.format( + console.type(self._model.type.name))) + if (self._model.relationship_template is not None and + self._model.relationship_template.name): + console.write('Relationship template: {0}'.format( + console.node(self._model.relationship_template.name))) + self._topology.dump(self._model.properties, console, 'Properties') + self._topology.dump(self._model.interfaces, console, 'Interfaces') + + def configure_operations(self): + for interface in self._model.interfaces.values(): + self._topology.configure_operations(interface) + + +class Service(common._OperatorHolderHandlerMixin): + 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) + + def dump(self, console): + if self._model.description is not None: + console.write(console.meta(self._model.description)) + self._topology.dump(self._model.meta_data, console, 'Metadata') + self._topology.dump(self._model.nodes, console) + self._topology.dump(self._model.groups, console) + self._topology.dump(self._model.policies, console) + self._topology.dump(self._model.substitution, console) + self._topology.dump(self._model.inputs, console, 'Inputs') + self._topology.dump(self._model.outputs, console, 'Outputs') + self._topology.dump(self._model.workflows, console, '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._InstanceHandlerMixin): + def coerce(self, **kwargs): + self._topology.coerce(self._model.mappings, **kwargs) + + def validate(self, **kwargs): + self._topology.validate(self._model.mappings) + + def dump(self, console): + console.write('Substitution:') + with console.indent: + console.write('Node type: {0}'.format(console.type(self._model.node_type.name))) + self._topology.dump(self._model.mappings, console, 'Mappings') + + +class SubstitutionMapping(common._InstanceHandlerMixin): + + def validate(self, **kwargs): + 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.name)), + level=self._topology.Issue.BETWEEN_TYPES) + + def dump(self, console): + if self._model.capability is not None: + console.write('{0} -> {1}.{2}'.format( + console.node(self._model.name), + console.node(self._model.capability.node.name), + console.node(self._model.capability.name))) + else: + console.write('{0} -> {1}.{2}'.format( + console.node(self._model.name), + console.node(self._model.node.name), + console.node(self._model.requirement_template.name))) + + +class Metadata(common._InstanceHandlerMixin): + + def dump(self, console): + console.write('{0}: {1}'.format( + console.property(self._topology.name), + console.literal(self._topology.value))) + + def coerce(self): + pass + + def instantiate(self, instance_cls, **kwargs): + return instance_cls(name=self._model.name, value=self._model.value) + + def validate(self): + pass + + +class _Parameter(common._InstanceHandlerMixin): + + def dump(self, console): + if self._model.type_name is not None: + console.write('{0}: {1} ({2})'.format( + console.property(self._model.name), + console.literal(formatting.as_raw(self._model.value)), + console.type(self._model.type_name))) + else: + console.write('{0}: {1}'.format( + console.property(self._model.name), + console.literal(formatting.as_raw(self._model.value)))) + if self._model.description: + console.write(console.meta(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 + + +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._InstanceHandlerMixin): + def coerce(self): + pass + + def dump(self, console): + if self._model.name: + console.write(console.type(self._model.name)) + with console.indent: + for child in self._model.children: + self._topology.dump(child, console)
