http://git-wip-us.apache.org/repos/asf/incubator-ariatosca-website/blob/23d6ba76/apache-ariatosca-0.1.1/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/apache-ariatosca-0.1.1/aria/modeling/service_instance.py
b/apache-ariatosca-0.1.1/aria/modeling/service_instance.py
deleted file mode 100644
index 002a87c..0000000
--- a/apache-ariatosca-0.1.1/aria/modeling/service_instance.py
+++ /dev/null
@@ -1,2240 +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.
-
-"""
-ARIA modeling service instance module
-"""
-
-# pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
-
-from sqlalchemy import (
- Column,
- Text,
- Integer,
- Enum,
- Boolean
-)
-from sqlalchemy import DateTime
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy.ext.orderinglist import ordering_list
-
-from .mixins import InstanceModelMixin
-from ..orchestrator import execution_plugin
-from ..parser import validation
-from ..parser.consumption import ConsumptionContext
-from ..utils import (
- collections,
- formatting,
- console
-)
-from . import (
- relationship,
- utils,
- types as modeling_types
-)
-
-
-class ServiceBase(InstanceModelMixin):
- """
- Usually an instance of a :class:`ServiceTemplate` and its many associated
templates (node
- templates, group templates, policy templates, etc.). However, it can also
be created
- programmatically.
- """
-
- __tablename__ = 'service'
-
- __private_fields__ = ('substitution_fk',
- 'service_template_fk')
-
- # region association proxies
-
- @declared_attr
- def service_template_name(cls):
- return relationship.association_proxy('service_template', 'name',
type=':obj:`basestring`')
-
- # endregion
-
- # region one_to_one relationships
-
- @declared_attr
- def substitution(cls):
- """
- Exposes the entire service as a single node.
-
- :type: :class:`Substitution`
- """
- return relationship.one_to_one(cls, 'substitution',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def outputs(cls):
- """
- Output parameters.
-
- :type: {:obj:`basestring`: :class:`Output`}
- """
- return relationship.one_to_many(cls, 'output', dict_key='name')
-
- @declared_attr
- def inputs(cls):
- """
- Externally provided parameters.
-
- :type: {:obj:`basestring`: :class:`Input`}
- """
- return relationship.one_to_many(cls, 'input', dict_key='name')
-
- @declared_attr
- def updates(cls):
- """
- Service updates.
-
- :type: [:class:`ServiceUpdate`]
- """
- return relationship.one_to_many(cls, 'service_update')
-
- @declared_attr
- def modifications(cls):
- """
- Service modifications.
-
- :type: [:class:`ServiceModification`]
- """
- return relationship.one_to_many(cls, 'service_modification')
-
- @declared_attr
- def executions(cls):
- """
- Executions.
-
- :type: [:class:`Execution`]
- """
- return relationship.one_to_many(cls, 'execution')
-
- @declared_attr
- def nodes(cls):
- """
- Nodes.
-
- :type: {:obj:`basestring`, :class:`Node`}
- """
- return relationship.one_to_many(cls, 'node', dict_key='name')
-
- @declared_attr
- def groups(cls):
- """
- Groups.
-
- :type: {:obj:`basestring`, :class:`Group`}
- """
- return relationship.one_to_many(cls, 'group', dict_key='name')
-
- @declared_attr
- def policies(cls):
- """
- Policies.
-
- :type: {:obj:`basestring`, :class:`Policy`}
- """
- return relationship.one_to_many(cls, 'policy', dict_key='name')
-
- @declared_attr
- def workflows(cls):
- """
- Workflows.
-
- :type: {:obj:`basestring`, :class:`Operation`}
- """
- return relationship.one_to_many(cls, 'operation', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service_template(cls):
- """
- Source service template (can be ``None``).
-
- :type: :class:`ServiceTemplate`
- """
- return relationship.many_to_one(cls, 'service_template')
-
- # endregion
-
- # region many_to_many relationships
-
- @declared_attr
- def meta_data(cls):
- """
- Associated metadata.
-
- :type: {:obj:`basestring`, :class:`Metadata`}
- """
- # Warning! We cannot use the attr name "metadata" because it's used by
SQLAlchemy!
- return relationship.many_to_many(cls, 'metadata', dict_key='name')
-
- @declared_attr
- def plugins(cls):
- """
- Associated plugins.
-
- :type: {:obj:`basestring`, :class:`Plugin`}
- """
- return relationship.many_to_many(cls, 'plugin', dict_key='name')
-
- # endregion
-
- # region foreign keys
-
- @declared_attr
- def substitution_fk(cls):
- """Service one-to-one to Substitution"""
- return relationship.foreign_key('substitution', nullable=True)
-
- @declared_attr
- def service_template_fk(cls):
- """For Service many-to-one to ServiceTemplate"""
- return relationship.foreign_key('service_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- created_at = Column(DateTime, nullable=False, index=True, doc="""
- Creation timestamp.
-
- :type: :class:`~datetime.datetime`
- """)
-
- updated_at = Column(DateTime, doc="""
- Update timestamp.
-
- :type: :class:`~datetime.datetime`
- """)
-
- def satisfy_requirements(self):
- satisfied = True
- for node in self.nodes.itervalues():
- if not node.satisfy_requirements():
- satisfied = False
- return satisfied
-
- def validate_capabilities(self):
- satisfied = True
- for node in self.nodes.itervalues():
- if not node.validate_capabilities():
- satisfied = False
- return satisfied
-
- def find_hosts(self):
- for node in self.nodes.itervalues():
- node.find_host()
-
- def configure_operations(self):
- for node in self.nodes.itervalues():
- node.configure_operations()
- for group in self.groups.itervalues():
- group.configure_operations()
- for operation in self.workflows.itervalues():
- operation.configure()
-
- def is_node_a_target(self, target_node):
- for node in self.nodes.itervalues():
- if self._is_node_a_target(node, target_node):
- return True
- return False
-
- def _is_node_a_target(self, source_node, target_node):
- if source_node.outbound_relationships:
- for relationship_model in source_node.outbound_relationships:
- if relationship_model.target_node.name == target_node.name:
- return True
- else:
- node = relationship_model.target_node
- if node is not None:
- if self._is_node_a_target(node, target_node):
- return True
- return False
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('description', self.description),
- ('metadata', formatting.as_raw_dict(self.meta_data)),
- ('nodes', formatting.as_raw_list(self.nodes)),
- ('groups', formatting.as_raw_list(self.groups)),
- ('policies', formatting.as_raw_list(self.policies)),
- ('substitution', formatting.as_raw(self.substitution)),
- ('inputs', formatting.as_raw_dict(self.inputs)),
- ('outputs', formatting.as_raw_dict(self.outputs)),
- ('workflows', formatting.as_raw_list(self.workflows))))
-
- def validate(self):
- utils.validate_dict_values(self.meta_data)
- utils.validate_dict_values(self.nodes)
- utils.validate_dict_values(self.groups)
- utils.validate_dict_values(self.policies)
- if self.substitution is not None:
- self.substitution.validate()
- utils.validate_dict_values(self.inputs)
- utils.validate_dict_values(self.outputs)
- utils.validate_dict_values(self.workflows)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.meta_data, report_issues)
- utils.coerce_dict_values(self.nodes, report_issues)
- utils.coerce_dict_values(self.groups, report_issues)
- utils.coerce_dict_values(self.policies, report_issues)
- if self.substitution is not None:
- self.substitution.coerce_values(report_issues)
- utils.coerce_dict_values(self.inputs, report_issues)
- utils.coerce_dict_values(self.outputs, report_issues)
- utils.coerce_dict_values(self.workflows, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- if self.description is not None:
- console.puts(context.style.meta(self.description))
- utils.dump_dict_values(self.meta_data, 'Metadata')
- for node in self.nodes.itervalues():
- node.dump()
- for group in self.groups.itervalues():
- group.dump()
- for policy in self.policies.itervalues():
- policy.dump()
- if self.substitution is not None:
- self.substitution.dump()
- utils.dump_dict_values(self.inputs, 'Inputs')
- utils.dump_dict_values(self.outputs, 'Outputs')
- utils.dump_dict_values(self.workflows, 'Workflows')
-
- def dump_graph(self):
- for node in self.nodes.itervalues():
- if not self.is_node_a_target(node):
- self._dump_graph_node(node)
-
- def _dump_graph_node(self, node, capability=None):
- context = ConsumptionContext.get_thread_local()
- console.puts(context.style.node(node.name))
- if capability is not None:
- console.puts('{0}
({1})'.format(context.style.property(capability.name),
-
context.style.type(capability.type.name)))
- if node.outbound_relationships:
- with context.style.indent:
- for relationship_model in node.outbound_relationships:
- relationship_name =
context.style.property(relationship_model.name)
- if relationship_model.type is not None:
- console.puts('-> {0} ({1})'.format(relationship_name,
- context.style.type(
-
relationship_model.type.name)))
- else:
- console.puts('-> {0}'.format(relationship_name))
- with console.indent(3):
- self._dump_graph_node(relationship_model.target_node,
-
relationship_model.target_capability)
-
-
-class NodeBase(InstanceModelMixin):
- """
- Typed vertex in the service topology.
-
- Nodes may have zero or more :class:`Relationship` instances to other
nodes, together forming
- a many-to-many node graph.
-
- Usually an instance of a :class:`NodeTemplate`.
- """
-
- __tablename__ = 'node'
-
- __private_fields__ = ('type_fk',
- 'host_fk',
- 'service_fk',
- 'node_template_fk')
-
- INITIAL = 'initial'
- CREATING = 'creating'
- CREATED = 'created'
- CONFIGURING = 'configuring'
- CONFIGURED = 'configured'
- STARTING = 'starting'
- STARTED = 'started'
- STOPPING = 'stopping'
- DELETING = 'deleting'
- DELETED = 'deleted'
- ERROR = 'error'
-
- # 'deleted' isn't actually part of the TOSCA spec, since according the
description of the
- # 'deleting' state: "Node is transitioning from its current state to one
where it is deleted and
- # its state is no longer tracked by the instance model." However, we
prefer to be able to
- # retrieve information about deleted nodes, so we chose to add this
'deleted' state to enable us
- # to do so.
-
- STATES = (INITIAL, CREATING, CREATED, CONFIGURING, CONFIGURED, STARTING,
STARTED, STOPPING,
- DELETING, DELETED, ERROR)
-
- _OP_TO_STATE = {'create': {'transitional': CREATING, 'finished': CREATED},
- 'configure': {'transitional': CONFIGURING, 'finished':
CONFIGURED},
- 'start': {'transitional': STARTING, 'finished': STARTED},
- 'stop': {'transitional': STOPPING, 'finished': CONFIGURED},
- 'delete': {'transitional': DELETING, 'finished': DELETED}}
-
- # region association proxies
-
- @declared_attr
- def service_name(cls):
- return relationship.association_proxy('service', 'name',
type=':obj:`basestring`')
-
- @declared_attr
- def node_template_name(cls):
- return relationship.association_proxy('node_template', 'name',
type=':obj:`basestring`')
-
- # endregion
-
- # region one_to_one relationships
-
- @declared_attr
- def host(cls): # pylint: disable=method-hidden
- """
- Node in which we are hosted (can be ``None``).
-
- Normally the host node is found by following the relationship graph
(relationships with
- ``host`` roles) to final nodes (with ``host`` roles).
-
- :type: :class:`Node`
- """
- return relationship.one_to_one_self(cls, 'host_fk')
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def tasks(cls):
- """
- Associated tasks.
-
- :type: [:class:`Task`]
- """
- return relationship.one_to_many(cls, 'task')
-
- @declared_attr
- def interfaces(cls):
- """
- Associated interfaces.
-
- :type: {:obj:`basestring`: :class:`Interface`}
- """
- return relationship.one_to_many(cls, 'interface', dict_key='name')
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- @declared_attr
- def attributes(cls):
- """
- Associated mutable parameters.
-
- :type: {:obj:`basestring`: :class:`Attribute`}
- """
- return relationship.one_to_many(cls, 'attribute', dict_key='name')
-
- @declared_attr
- def artifacts(cls):
- """
- Associated artifacts.
-
- :type: {:obj:`basestring`: :class:`Artifact`}
- """
- return relationship.one_to_many(cls, 'artifact', dict_key='name')
-
- @declared_attr
- def capabilities(cls):
- """
- Associated exposed capabilities.
-
- :type: {:obj:`basestring`: :class:`Capability`}
- """
- return relationship.one_to_many(cls, 'capability', dict_key='name')
-
- @declared_attr
- def outbound_relationships(cls):
- """
- Relationships to other nodes.
-
- :type: [:class:`Relationship`]
- """
- return relationship.one_to_many(
- cls, 'relationship', other_fk='source_node_fk',
back_populates='source_node',
- rel_kwargs=dict(
- order_by='Relationship.source_position',
- collection_class=ordering_list('source_position', count_from=0)
- )
- )
-
- @declared_attr
- def inbound_relationships(cls):
- """
- Relationships from other nodes.
-
- :type: [:class:`Relationship`]
- """
- return relationship.one_to_many(
- cls, 'relationship', other_fk='target_node_fk',
back_populates='target_node',
- rel_kwargs=dict(
- order_by='Relationship.target_position',
- collection_class=ordering_list('target_position', count_from=0)
- )
- )
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service(cls):
- """
- Containing service.
-
- :type: :class:`Service`
- """
- return relationship.many_to_one(cls, 'service')
-
- @declared_attr
- def node_template(cls):
- """
- Source node template (can be ``None``).
-
- :type: :class:`NodeTemplate`
- """
- return relationship.many_to_one(cls, 'node_template')
-
- @declared_attr
- def type(cls):
- """
- Node type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Node many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def host_fk(cls):
- """For Node one-to-one to Node"""
- return relationship.foreign_key('node', nullable=True)
-
- @declared_attr
- def service_fk(cls):
- """For Service one-to-many to Node"""
- return relationship.foreign_key('service')
-
- @declared_attr
- def node_template_fk(cls):
- """For Node many-to-one to NodeTemplate"""
- return relationship.foreign_key('node_template')
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- state = Column(Enum(*STATES, name='node_state'), nullable=False,
default=INITIAL, doc="""
- TOSCA state.
-
- :type: :obj:`basestring`
- """)
-
- version = Column(Integer, default=1, doc="""
- Used by :mod:`aria.storage.instrumentation`.
-
- :type: :obj:`int`
- """)
-
- __mapper_args__ = {'version_id_col': version} # Enable SQLAlchemy
automatic version counting
-
- @classmethod
- def determine_state(cls, op_name, is_transitional):
- """ :returns the state the node should be in as a result of running the
- operation on this node.
-
- e.g. if we are running
tosca.interfaces.node.lifecycle.Standard.create, then
- the resulting state should either 'creating' (if the task just
started) or 'created'
- (if the task ended).
-
- If the operation is not a standard tosca lifecycle operation, then
we return None"""
-
- state_type = 'transitional' if is_transitional else 'finished'
- try:
- return cls._OP_TO_STATE[op_name][state_type]
- except KeyError:
- return None
-
- def is_available(self):
- return self.state not in (self.INITIAL, self.DELETED, self.ERROR)
-
- @property
- def host_address(self):
- if self.host and self.host.attributes:
- attribute = self.host.attributes.get('ip')
- return attribute.value if attribute else None
- return None
-
- def satisfy_requirements(self):
- node_template = self.node_template
- satisfied = True
- for requirement_template in node_template.requirement_templates:
- # Find target template
- target_node_template, target_node_capability = \
- requirement_template.find_target(node_template)
- if target_node_template is not None:
- satisfied = self._satisfy_capability(target_node_capability,
- target_node_template,
- requirement_template)
- else:
- context = ConsumptionContext.get_thread_local()
- context.validation.report('requirement "{0}" of node "{1}" has
no target node '
-
'template'.format(requirement_template.name, self.name),
-
level=validation.Issue.BETWEEN_INSTANCES)
- satisfied = False
- return satisfied
-
- def _satisfy_capability(self, target_node_capability, target_node_template,
- requirement_template):
- from . import models
- context = ConsumptionContext.get_thread_local()
- # Find target nodes
- target_nodes = target_node_template.nodes
- if target_nodes:
- target_node = None
- target_capability = None
-
- if target_node_capability is not None:
- # Relate to the first target node that has capacity
- for node in target_nodes:
- a_target_capability =
node.capabilities.get(target_node_capability.name)
- if a_target_capability.relate():
- target_node = node
- target_capability = a_target_capability
- break
- else:
- # Use first target node
- target_node = target_nodes[0]
-
- if target_node is not None:
- if requirement_template.relationship_template is not None:
- relationship_model = \
-
requirement_template.relationship_template.instantiate(self)
- else:
- relationship_model = models.Relationship()
- relationship_model.name = requirement_template.name
- relationship_model.requirement_template = requirement_template
- relationship_model.target_node = target_node
- relationship_model.target_capability = target_capability
- self.outbound_relationships.append(relationship_model)
- return True
- else:
- context.validation.report('requirement "{0}" of node "{1}"
targets node '
- 'template "{2}" but its instantiated
nodes do not '
- 'have enough capacity'.format(
- requirement_template.name,
- self.name,
- target_node_template.name),
-
level=validation.Issue.BETWEEN_INSTANCES)
- return False
- else:
- context.validation.report('requirement "{0}" of node "{1}" targets
node template '
- '"{2}" but it has no instantiated
nodes'.format(
- requirement_template.name,
- self.name,
- target_node_template.name),
- level=validation.Issue.BETWEEN_INSTANCES)
- return False
-
- def validate_capabilities(self):
- context = ConsumptionContext.get_thread_local()
- satisfied = False
- for capability in self.capabilities.itervalues():
- if not capability.has_enough_relationships:
- context.validation.report('capability "{0}" of node "{1}"
requires at least {2:d} '
- 'relationships but has {3:d}'.format(
- capability.name,
- self.name,
- capability.min_occurrences,
- capability.occurrences),
-
level=validation.Issue.BETWEEN_INSTANCES)
- satisfied = False
- return satisfied
-
- def find_host(self):
- def _find_host(node):
- if node.type.role == 'host':
- return node
- for the_relationship in node.outbound_relationships:
- if (the_relationship.target_capability is not None) and \
- the_relationship.target_capability.type.role == 'host':
- host = _find_host(the_relationship.target_node)
- if host is not None:
- return host
- for the_relationship in node.inbound_relationships:
- if (the_relationship.target_capability is not None) and \
- the_relationship.target_capability.type.role == 'feature':
- host = _find_host(the_relationship.source_node)
- if host is not None:
- return host
- return None
-
- self.host = _find_host(self)
-
- def configure_operations(self):
- for interface in self.interfaces.itervalues():
- interface.configure_operations()
- for the_relationship in self.outbound_relationships:
- the_relationship.configure_operations()
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type.name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('attributes', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces)),
- ('artifacts', formatting.as_raw_list(self.artifacts)),
- ('capabilities', formatting.as_raw_list(self.capabilities)),
- ('relationships',
formatting.as_raw_list(self.outbound_relationships))))
-
- def validate(self):
- context = ConsumptionContext.get_thread_local()
- if len(self.name) > context.modeling.id_max_length:
- context.validation.report('"{0}" has an ID longer than the limit
of {1:d} characters: '
- '{2:d}'.format(
- self.name,
- context.modeling.id_max_length,
- len(self.name)),
- level=validation.Issue.BETWEEN_INSTANCES)
-
- # TODO: validate that node template is of type?
-
- utils.validate_dict_values(self.properties)
- utils.validate_dict_values(self.attributes)
- utils.validate_dict_values(self.interfaces)
- utils.validate_dict_values(self.artifacts)
- utils.validate_dict_values(self.capabilities)
- utils.validate_list_values(self.outbound_relationships)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
- utils.coerce_dict_values(self.attributes, report_issues)
- utils.coerce_dict_values(self.interfaces, report_issues)
- utils.coerce_dict_values(self.artifacts, report_issues)
- utils.coerce_dict_values(self.capabilities, report_issues)
- utils.coerce_list_values(self.outbound_relationships, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts('Node: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type:
{0}'.format(context.style.type(self.type.name)))
- console.puts('Template:
{0}'.format(context.style.node(self.node_template.name)))
- utils.dump_dict_values(self.properties, 'Properties')
- utils.dump_dict_values(self.attributes, 'Attributes')
- utils.dump_interfaces(self.interfaces)
- utils.dump_dict_values(self.artifacts, 'Artifacts')
- utils.dump_dict_values(self.capabilities, 'Capabilities')
- utils.dump_list_values(self.outbound_relationships,
'Relationships')
-
-
-class GroupBase(InstanceModelMixin):
- """
- Typed logical container for zero or more :class:`Node` instances.
-
- Usually an instance of a :class:`GroupTemplate`.
- """
-
- __tablename__ = 'group'
-
- __private_fields__ = ('type_fk',
- 'service_fk',
- 'group_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- """
- Associated interfaces.
-
- :type: {:obj:`basestring`: :class:`Interface`}
- """
- return relationship.one_to_many(cls, 'interface', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service(cls):
- """
- Containing service.
-
- :type: :class:`Service`
- """
- return relationship.many_to_one(cls, 'service')
-
- @declared_attr
- def group_template(cls):
- """
- Source group template (can be ``None``).
-
- :type: :class:`GroupTemplate`
- """
- return relationship.many_to_one(cls, 'group_template')
-
- @declared_attr
- def type(cls):
- """
- Group type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region many_to_many relationships
-
- @declared_attr
- def nodes(cls):
- """
- Member nodes.
-
- :type: [:class:`Node`]
- """
- return relationship.many_to_many(cls, 'node')
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Group many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def service_fk(cls):
- """For Service one-to-many to Group"""
- return relationship.foreign_key('service')
-
- @declared_attr
- def group_template_fk(cls):
- """For Group many-to-one to GroupTemplate"""
- return relationship.foreign_key('group_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- def configure_operations(self):
- for interface in self.interfaces.itervalues():
- interface.configure_operations()
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces))))
-
- def validate(self):
- utils.validate_dict_values(self.properties)
- utils.validate_dict_values(self.interfaces)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
- utils.coerce_dict_values(self.interfaces, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts('Group: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type:
{0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(self.properties, 'Properties')
- utils.dump_interfaces(self.interfaces)
- if self.nodes:
- console.puts('Member nodes:')
- with context.style.indent:
- for node in self.nodes:
- console.puts(context.style.node(node.name))
-
-
-class PolicyBase(InstanceModelMixin):
- """
- Typed set of orchestration hints applied to zero or more :class:`Node` or
:class:`Group`
- instances.
-
- Usually an instance of a :class:`PolicyTemplate`.
- """
-
- __tablename__ = 'policy'
-
- __private_fields__ = ('type_fk',
- 'service_fk',
- 'policy_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service(cls):
- """
- Containing service.
-
- :type: :class:`Service`
- """
- return relationship.many_to_one(cls, 'service')
-
- @declared_attr
- def policy_template(cls):
- """
- Source policy template (can be ``None``).
-
- :type: :class:`PolicyTemplate`
- """
- return relationship.many_to_one(cls, 'policy_template')
-
- @declared_attr
- def type(cls):
- """
- Group type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region many_to_many relationships
-
- @declared_attr
- def nodes(cls):
- """
- Policy is enacted on these nodes.
-
- :type: {:obj:`basestring`: :class:`Node`}
- """
- return relationship.many_to_many(cls, 'node')
-
- @declared_attr
- def groups(cls):
- """
- Policy is enacted on nodes in these groups.
-
- :type: {:obj:`basestring`: :class:`Group`}
- """
- return relationship.many_to_many(cls, 'group')
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Policy many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def service_fk(cls):
- """For Service one-to-many to Policy"""
- return relationship.foreign_key('service')
-
- @declared_attr
- def policy_template_fk(cls):
- """For Policy many-to-one to PolicyTemplate"""
- return relationship.foreign_key('policy_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type.name),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self):
- utils.validate_dict_values(self.properties)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts('Policy: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type:
{0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(self.properties, 'Properties')
- if self.nodes:
- console.puts('Target nodes:')
- with context.style.indent:
- for node in self.nodes:
- console.puts(context.style.node(node.name))
- if self.groups:
- console.puts('Target groups:')
- with context.style.indent:
- for group in self.groups:
- console.puts(context.style.node(group.name))
-
-
-class SubstitutionBase(InstanceModelMixin):
- """
- Exposes the entire service as a single node.
-
- Usually an instance of a :class:`SubstitutionTemplate`.
- """
-
- __tablename__ = 'substitution'
-
- __private_fields__ = ('node_type_fk',
- 'substitution_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def mappings(cls):
- """
- Map requirement and capabilities to exposed node.
-
- :type: {:obj:`basestring`: :class:`SubstitutionMapping`}
- """
- return relationship.one_to_many(cls, 'substitution_mapping',
dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service(cls):
- """
- Containing service.
-
- :type: :class:`Service`
- """
- return relationship.one_to_one(cls, 'service',
back_populates=relationship.NO_BACK_POP)
-
- @declared_attr
- def substitution_template(cls):
- """
- Source substitution template (can be ``None``).
-
- :type: :class:`SubstitutionTemplate`
- """
- return relationship.many_to_one(cls, 'substitution_template')
-
- @declared_attr
- def node_type(cls):
- """
- Exposed node type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def node_type_fk(cls):
- """For Substitution many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def substitution_template_fk(cls):
- """For Substitution many-to-one to SubstitutionTemplate"""
- return relationship.foreign_key('substitution_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('node_type_name', self.node_type.name),
- ('mappings', formatting.as_raw_dict(self.mappings))))
-
- def validate(self):
- utils.validate_dict_values(self.mappings)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.mappings, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts('Substitution:')
- with context.style.indent:
- console.puts('Node type:
{0}'.format(context.style.type(self.node_type.name)))
- utils.dump_dict_values(self.mappings, 'Mappings')
-
-
-class SubstitutionMappingBase(InstanceModelMixin):
- """
- Used by :class:`Substitution` to map a capability or a requirement to the
exposed node.
-
- The :attr:`name` field should match the capability or requirement template
name on the exposed
- node's type.
-
- Only one of :attr:`capability` and :attr:`requirement_template` can be
set. If the latter is
- set, then :attr:`node` must also be set.
-
- Usually an instance of a :class:`SubstitutionMappingTemplate`.
- """
-
- __tablename__ = 'substitution_mapping'
-
- __private_fields__ = ('substitution_fk',
- 'capability_fk',
- 'requirement_template_fk',
- 'node_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- @declared_attr
- def capability(cls):
- """
- Capability to expose (can be ``None``).
-
- :type: :class:`Capability`
- """
- return relationship.one_to_one(cls, 'capability',
back_populates=relationship.NO_BACK_POP)
-
- @declared_attr
- def requirement_template(cls):
- """
- Requirement template to expose (can be ``None``).
-
- :type: :class:`RequirementTemplate`
- """
- return relationship.one_to_one(cls, 'requirement_template',
- back_populates=relationship.NO_BACK_POP)
-
- @declared_attr
- def node(cls):
- """
- Node for which to expose :attr:`requirement_template` (can be
``None``).
-
- :type: :class:`Node`
- """
- return relationship.one_to_one(cls, 'node',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region one_to_many relationships
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def substitution(cls):
- """
- Containing substitution.
-
- :type: :class:`Substitution`
- """
- return relationship.many_to_one(cls, 'substitution',
back_populates='mappings')
-
- # endregion
-
- # region foreign keys
-
- @declared_attr
- def substitution_fk(cls):
- """For Substitution one-to-many to SubstitutionMapping"""
- return relationship.foreign_key('substitution')
-
- @declared_attr
- def capability_fk(cls):
- """For Substitution one-to-one to Capability"""
- return relationship.foreign_key('capability', nullable=True)
-
- @declared_attr
- def node_fk(cls):
- """For Substitution one-to-one to Node"""
- return relationship.foreign_key('node', nullable=True)
-
- @declared_attr
- def requirement_template_fk(cls):
- """For Substitution one-to-one to RequirementTemplate"""
- return relationship.foreign_key('requirement_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),))
-
- def coerce_values(self, report_issues):
- pass
-
- def validate(self):
- context = ConsumptionContext.get_thread_local()
- if (self.capability is None) and (self.requirement_template is None):
- context.validation.report('mapping "{0}" refers to neither
capability nor a requirement'
- ' in node: {1}'.format(
- self.name,
-
formatting.safe_repr(self.node.name)),
- level=validation.Issue.BETWEEN_TYPES)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- if self.capability is not None:
- console.puts('{0} -> {1}.{2}'.format(
- context.style.node(self.name),
- context.style.node(self.capability.node.name),
- context.style.node(self.capability.name)))
- else:
- console.puts('{0} -> {1}.{2}'.format(
- context.style.node(self.name),
- context.style.node(self.node.name),
- context.style.node(self.requirement_template.name)))
-
-
-class RelationshipBase(InstanceModelMixin):
- """
- Optionally-typed edge in the service topology, connecting a :class:`Node`
to a
- :class:`Capability` of another node.
-
- Might be an instance of :class:`RelationshipTemplate` and/or
:class:`RequirementTemplate`.
- """
-
- __tablename__ = 'relationship'
-
- __private_fields__ = ('type_fk',
- 'source_node_fk',
- 'target_node_fk',
- 'target_capability_fk',
- 'requirement_template_fk',
- 'relationship_template_fk',
- 'target_position',
- 'source_position')
-
- # region association proxies
-
- @declared_attr
- def source_node_name(cls):
- return relationship.association_proxy('source_node', 'name')
-
- @declared_attr
- def target_node_name(cls):
- return relationship.association_proxy('target_node', 'name')
-
- # endregion
-
- # region one_to_one relationships
-
- @declared_attr
- def target_capability(cls):
- """
- Target capability.
-
- :type: :class:`Capability`
- """
- return relationship.one_to_one(cls, 'capability',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def tasks(cls):
- """
- Associated tasks.
-
- :type: [:class:`Task`]
- """
- return relationship.one_to_many(cls, 'task')
-
- @declared_attr
- def interfaces(cls):
- """
- Associated interfaces.
-
- :type: {:obj:`basestring`: :class:`Interface`}
- """
- return relationship.one_to_many(cls, 'interface', dict_key='name')
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def source_node(cls):
- """
- Source node.
-
- :type: :class:`Node`
- """
- return relationship.many_to_one(
- cls, 'node', fk='source_node_fk',
back_populates='outbound_relationships')
-
- @declared_attr
- def target_node(cls):
- """
- Target node.
-
- :type: :class:`Node`
- """
- return relationship.many_to_one(
- cls, 'node', fk='target_node_fk',
back_populates='inbound_relationships')
-
- @declared_attr
- def relationship_template(cls):
- """
- Source relationship template (can be ``None``).
-
- :type: :class:`RelationshipTemplate`
- """
- return relationship.many_to_one(cls, 'relationship_template')
-
- @declared_attr
- def requirement_template(cls):
- """
- Source requirement template (can be ``None``).
-
- :type: :class:`RequirementTemplate`
- """
- return relationship.many_to_one(cls, 'requirement_template')
-
- @declared_attr
- def type(cls):
- """
- Relationship type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign keys
-
- @declared_attr
- def type_fk(cls):
- """For Relationship many-to-one to Type"""
- return relationship.foreign_key('type', nullable=True)
-
- @declared_attr
- def source_node_fk(cls):
- """For Node one-to-many to Relationship"""
- return relationship.foreign_key('node')
-
- @declared_attr
- def target_node_fk(cls):
- """For Node one-to-many to Relationship"""
- return relationship.foreign_key('node')
-
- @declared_attr
- def target_capability_fk(cls):
- """For Relationship one-to-one to Capability"""
- return relationship.foreign_key('capability', nullable=True)
-
- @declared_attr
- def requirement_template_fk(cls):
- """For Relationship many-to-one to RequirementTemplate"""
- return relationship.foreign_key('requirement_template', nullable=True)
-
- @declared_attr
- def relationship_template_fk(cls):
- """For Relationship many-to-one to RelationshipTemplate"""
- return relationship.foreign_key('relationship_template', nullable=True)
-
- # endregion
-
- source_position = Column(Integer, doc="""
- Position at source.
-
- :type: :obj:`int`
- """)
-
- target_position = Column(Integer, doc="""
- Position at target.
-
- :type: :obj:`int`
- """)
-
- def configure_operations(self):
- for interface in self.interfaces.itervalues():
- interface.configure_operations()
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('target_node_id', self.target_node.name),
- ('type_name', self.type.name
- if self.type is not None else None),
- ('template_name', self.relationship_template.name
- if self.relationship_template is not None else None),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces))))
-
- def validate(self):
- utils.validate_dict_values(self.properties)
- utils.validate_dict_values(self.interfaces)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
- utils.coerce_dict_values(self.interfaces, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- if self.name:
- console.puts('{0} ->'.format(context.style.node(self.name)))
- else:
- console.puts('->')
- with context.style.indent:
- console.puts('Node:
{0}'.format(context.style.node(self.target_node.name)))
- if self.target_capability:
- console.puts('Capability: {0}'.format(context.style.node(
- self.target_capability.name)))
- if self.type is not None:
- console.puts('Relationship type:
{0}'.format(context.style.type(self.type.name)))
- if (self.relationship_template is not None) and
self.relationship_template.name:
- console.puts('Relationship template: {0}'.format(
- context.style.node(self.relationship_template.name)))
- utils.dump_dict_values(self.properties, 'Properties')
- utils.dump_interfaces(self.interfaces, 'Interfaces')
-
-
-class CapabilityBase(InstanceModelMixin):
- """
- Typed attachment serving two purposes: to provide extra properties and
attributes to a
- :class:`Node`, and to expose targets for :class:`Relationship` instances
from other nodes.
-
- Usually an instance of a :class:`CapabilityTemplate`.
- """
-
- __tablename__ = 'capability'
-
- __private_fields__ = ('capability_fk',
- 'node_fk',
- 'capability_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def node(cls):
- """
- Containing node.
-
- :type: :class:`Node`
- """
- return relationship.many_to_one(cls, 'node')
-
- @declared_attr
- def capability_template(cls):
- """
- Source capability template (can be ``None``).
-
- :type: :class:`CapabilityTemplate`
- """
- return relationship.many_to_one(cls, 'capability_template')
-
- @declared_attr
- def type(cls):
- """
- Capability type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Capability many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def node_fk(cls):
- """For Node one-to-many to Capability"""
- return relationship.foreign_key('node')
-
- @declared_attr
- def capability_template_fk(cls):
- """For Capability many-to-one to CapabilityTemplate"""
- return relationship.foreign_key('capability_template', nullable=True)
-
- # endregion
-
- min_occurrences = Column(Integer, default=None, doc="""
- Minimum number of requirement matches required.
-
- :type: :obj:`int`
- """)
-
- max_occurrences = Column(Integer, default=None, doc="""
- Maximum number of requirement matches allowed.
-
- :type: :obj:`int`
- """)
-
- occurrences = Column(Integer, default=0, doc="""
- Number of requirement matches.
-
- :type: :obj:`int`
- """)
-
- @property
- def has_enough_relationships(self):
- if self.min_occurrences is not None:
- return self.occurrences >= self.min_occurrences
- return True
-
- def relate(self):
- if self.max_occurrences is not None:
- if self.occurrences == self.max_occurrences:
- return False
- self.occurrences += 1
- return True
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type.name),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self):
- utils.validate_dict_values(self.properties)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts(context.style.node(self.name))
- with context.style.indent:
- console.puts('Type:
{0}'.format(context.style.type(self.type.name)))
- console.puts('Occurrences: {0:d} ({1:d}{2})'.format(
- self.occurrences,
- self.min_occurrences or 0,
- ' to {0:d}'.format(self.max_occurrences)
- if self.max_occurrences is not None
- else ' or more'))
- utils.dump_dict_values(self.properties, 'Properties')
-
-
-class InterfaceBase(InstanceModelMixin):
- """
- Typed bundle of :class:`Operation` instances.
-
- Can be associated with a :class:`Node`, a :class:`Group`, or a
:class:`Relationship`.
-
- Usually an instance of a :class:`InterfaceTemplate`.
- """
-
- __tablename__ = 'interface'
-
- __private_fields__ = ('type_fk',
- 'node_fk',
- 'group_fk',
- 'relationship_fk',
- 'interface_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def inputs(cls):
- """
- Parameters for all operations of the interface.
-
- :type: {:obj:`basestring`: :class:`Input`}
- """
- return relationship.one_to_many(cls, 'input', dict_key='name')
-
- @declared_attr
- def operations(cls):
- """
- Associated operations.
-
- :type: {:obj:`basestring`: :class:`Operation`}
- """
- return relationship.one_to_many(cls, 'operation', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def node(cls):
- """
- Containing node (can be ``None``).
-
- :type: :class:`Node`
- """
- return relationship.many_to_one(cls, 'node')
-
- @declared_attr
- def group(cls):
- """
- Containing group (can be ``None``).
-
- :type: :class:`Group`
- """
- return relationship.many_to_one(cls, 'group')
-
- @declared_attr
- def relationship(cls):
- """
- Containing relationship (can be ``None``).
-
- :type: :class:`Relationship`
- """
- return relationship.many_to_one(cls, 'relationship')
-
- @declared_attr
- def interface_template(cls):
- """
- Source interface template (can be ``None``).
-
- :type: :class:`InterfaceTemplate`
- """
- return relationship.many_to_one(cls, 'interface_template')
-
- @declared_attr
- def type(cls):
- """
- Interface type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Interface many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def node_fk(cls):
- """For Node one-to-many to Interface"""
- return relationship.foreign_key('node', nullable=True)
-
- @declared_attr
- def group_fk(cls):
- """For Group one-to-many to Interface"""
- return relationship.foreign_key('group', nullable=True)
-
- @declared_attr
- def relationship_fk(cls):
- """For Relationship one-to-many to Interface"""
- return relationship.foreign_key('relationship', nullable=True)
-
- @declared_attr
- def interface_template_fk(cls):
- """For Interface many-to-one to InterfaceTemplate"""
- return relationship.foreign_key('interface_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- def configure_operations(self):
- for operation in self.operations.itervalues():
- operation.configure()
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('type_name', self.type.name),
- ('inputs', formatting.as_raw_dict(self.inputs)),
- ('operations', formatting.as_raw_list(self.operations))))
-
- def validate(self):
- utils.validate_dict_values(self.inputs)
- utils.validate_dict_values(self.operations)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.inputs, report_issues)
- utils.coerce_dict_values(self.operations, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- console.puts('Interface type:
{0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(self.inputs, 'Inputs')
- utils.dump_dict_values(self.operations, 'Operations')
-
-
-class OperationBase(InstanceModelMixin):
- """
- Entry points to Python functions called as part of a workflow execution.
-
- The operation signature (its :attr:`name` and its :attr:`inputs`'s names
and types) is declared
- by the type of the :class:`Interface`, however each operation can provide
its own
- :attr:`implementation` as well as additional inputs.
-
- The Python :attr:`function` is usually provided by an associated
:class:`Plugin`. Its purpose is
- to execute the implementation, providing it with both the operation's and
interface's inputs.
- The :attr:`arguments` of the function should be set according to the
specific signature of the
- function.
-
- Additionally, :attr:`configuration` parameters can be provided as hints to
configure the
- function's behavior. For example, they can be used to configure remote
execution credentials.
-
- Might be an instance of :class:`OperationTemplate`.
- """
-
- __tablename__ = 'operation'
-
- __private_fields__ = ('service_fk',
- 'interface_fk',
- 'plugin_fk',
- 'operation_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- @declared_attr
- def plugin(cls):
- """
- Associated plugin.
-
- :type: :class:`Plugin`
- """
- return relationship.one_to_one(cls, 'plugin',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def inputs(cls):
- """
- Parameters provided to the :attr:`implementation`.
-
- :type: {:obj:`basestring`: :class:`Input`}
- """
- return relationship.one_to_many(cls, 'input', dict_key='name')
-
- @declared_attr
- def arguments(cls):
- """
- Arguments sent to the Python :attr:`function`.
-
- :type: {:obj:`basestring`: :class:`Argument`}
- """
- return relationship.one_to_many(cls, 'argument', dict_key='name')
-
- @declared_attr
- def configurations(cls):
- """
- Configuration parameters for the Python :attr:`function`.
-
- :type: {:obj:`basestring`: :class:`Configuration`}
- """
- return relationship.one_to_many(cls, 'configuration', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def service(cls):
- """
- Containing service (can be ``None``). For workflow operations.
-
- :type: :class:`Service`
- """
- return relationship.many_to_one(cls, 'service',
back_populates='workflows')
-
- @declared_attr
- def interface(cls):
- """
- Containing interface (can be ``None``).
-
- :type: :class:`Interface`
- """
- return relationship.many_to_one(cls, 'interface')
-
- @declared_attr
- def operation_template(cls):
- """
- Source operation template (can be ``None``).
-
- :type: :class:`OperationTemplate`
- """
- return relationship.many_to_one(cls, 'operation_template')
-
- # endregion
-
- # region many_to_many relationships
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def service_fk(cls):
- """For Service one-to-many to Operation"""
- return relationship.foreign_key('service', nullable=True)
-
- @declared_attr
- def interface_fk(cls):
- """For Interface one-to-many to Operation"""
- return relationship.foreign_key('interface', nullable=True)
-
- @declared_attr
- def plugin_fk(cls):
- """For Operation one-to-one to Plugin"""
- return relationship.foreign_key('plugin', nullable=True)
-
- @declared_attr
- def operation_template_fk(cls):
- """For Operation many-to-one to OperationTemplate"""
- return relationship.foreign_key('operation_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- relationship_edge = Column(Boolean, doc="""
- When ``True`` specifies that the operation is on the relationship's target
edge; ``False`` is
- the source edge (only used by operations on relationships)
-
- :type: :obj:`bool`
- """)
-
- implementation = Column(Text, doc="""
- Implementation (usually the name of an artifact).
-
- :type: :obj:`basestring`
- """)
-
- dependencies = Column(modeling_types.StrictList(item_cls=basestring),
doc="""
- Dependencies (usually names of artifacts).
-
- :type: [:obj:`basestring`]
- """)
-
- function = Column(Text, doc="""
- Full path to Python function.
-
- :type: :obj:`basestring`
- """)
-
- executor = Column(Text, doc="""
- Name of executor.
-
- :type: :obj:`basestring`
- """)
-
- max_attempts = Column(Integer, doc="""
- Maximum number of attempts allowed in case of task failure.
-
- :type: :obj:`int`
- """)
-
- retry_interval = Column(Integer, doc="""
- Interval between task retry attemps (in seconds).
-
- :type: :obj:`float`
- """)
-
- def configure(self):
- if (self.implementation is None) and (self.function is None):
- return
-
- if (self.interface is not None) and (self.plugin is None) and
(self.function is None):
- # ("interface" is None for workflow operations, which do not
currently use "plugin")
- # The default (None) plugin is the execution plugin
- execution_plugin.instantiation.configure_operation(self)
- else:
- # In the future plugins may be able to add their own
"configure_operation" hook that
- # can validate the configuration and otherwise create specially
derived arguments. For
- # now, we just send all configuration parameters as arguments
without validation.
- configurations_as_arguments = {}
- for configuration in self.configurations.itervalues():
- configurations_as_arguments[configuration.name] =
configuration.as_argument()
-
- utils.instantiate_dict(self, self.arguments,
configurations_as_arguments)
-
- # Send all inputs as extra arguments
- # Note that they will override existing arguments of the same names
- inputs_as_arguments = {}
- for input in self.inputs.itervalues():
- inputs_as_arguments[input.name] = input.as_argument()
-
- utils.instantiate_dict(self, self.arguments, inputs_as_arguments)
-
- # Check for reserved arguments
- from ..orchestrator.decorators import
OPERATION_DECORATOR_RESERVED_ARGUMENTS
- used_reserved_names = \
-
OPERATION_DECORATOR_RESERVED_ARGUMENTS.intersection(self.arguments.keys())
- if used_reserved_names:
- context = ConsumptionContext.get_thread_local()
- context.validation.report('using reserved arguments in node "{0}":
{1}'
- .format(
- self.name,
-
formatting.string_list_as_string(used_reserved_names)),
- level=validation.Issue.EXTERNAL)
-
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('implementation', self.implementation),
- ('dependencies', self.dependencies),
- ('inputs', formatting.as_raw_dict(self.inputs))))
-
- def validate(self):
- # TODO must be associated with either interface or service
- utils.validate_dict_values(self.inputs)
- utils.validate_dict_values(self.configurations)
- utils.validate_dict_values(self.arguments)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.inputs, report_issues)
- utils.coerce_dict_values(self.configurations, report_issues)
- utils.coerce_dict_values(self.arguments, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- if self.implementation is not None:
- console.puts('Implementation: {0}'.format(
- context.style.literal(self.implementation)))
- if self.dependencies:
- console.puts(
- 'Dependencies: {0}'.format(
- ', '.join((str(context.style.literal(v)) for v in
self.dependencies))))
- utils.dump_dict_values(self.inputs, 'Inputs')
- if self.executor is not None:
- console.puts('Executor:
{0}'.format(context.style.literal(self.executor)))
- if self.max_attempts is not None:
- console.puts('Max attempts:
{0}'.format(context.style.literal(self.max_attempts)))
- if self.retry_interval is not None:
- console.puts('Retry interval: {0}'.format(
- context.style.literal(self.retry_interval)))
- if self.plugin is not None:
- console.puts('Plugin: {0}'.format(
- context.style.literal(self.plugin.name)))
- utils.dump_dict_values(self.configurations, 'Configuration')
- if self.function is not None:
- console.puts('Function:
{0}'.format(context.style.literal(self.function)))
- utils.dump_dict_values(self.arguments, 'Arguments')
-
-
-class ArtifactBase(InstanceModelMixin):
- """
- Typed file, either provided in a CSAR or downloaded from a repository.
-
- Usually an instance of :class:`ArtifactTemplate`.
- """
-
- __tablename__ = 'artifact'
-
- __private_fields__ = ('type_fk',
- 'node_fk',
- 'artifact_template_fk')
-
- # region association proxies
-
- # endregion
-
- # region one_to_one relationships
-
- # endregion
-
- # region one_to_many relationships
-
- @declared_attr
- def properties(cls):
- """
- Associated immutable parameters.
-
- :type: {:obj:`basestring`: :class:`Property`}
- """
- return relationship.one_to_many(cls, 'property', dict_key='name')
-
- # endregion
-
- # region many_to_one relationships
-
- @declared_attr
- def node(cls):
- """
- Containing node.
-
- :type: :class:`Node`
- """
- return relationship.many_to_one(cls, 'node')
-
- @declared_attr
- def artifact_template(cls):
- """
- Source artifact template (can be ``None``).
-
- :type: :class:`ArtifactTemplate`
- """
- return relationship.many_to_one(cls, 'artifact_template')
-
- @declared_attr
- def type(cls):
- """
- Artifact type.
-
- :type: :class:`Type`
- """
- return relationship.many_to_one(cls, 'type',
back_populates=relationship.NO_BACK_POP)
-
- # endregion
-
- # region foreign_keys
-
- @declared_attr
- def type_fk(cls):
- """For Artifact many-to-one to Type"""
- return relationship.foreign_key('type')
-
- @declared_attr
- def node_fk(cls):
- """For Node one-to-many to Artifact"""
- return relationship.foreign_key('node')
-
- @declared_attr
- def artifact_template_fk(cls):
- """For Artifact many-to-one to ArtifactTemplate"""
- return relationship.foreign_key('artifact_template', nullable=True)
-
- # endregion
-
- description = Column(Text, doc="""
- Human-readable description.
-
- :type: :obj:`basestring`
- """)
-
- source_path = Column(Text, doc="""
- Source path (in CSAR or repository).
-
- :type: :obj:`basestring`
- """)
-
- target_path = Column(Text, doc="""
- Path at which to install at destination.
-
- :type: :obj:`basestring`
- """)
-
- repository_url = Column(Text, doc="""
- Repository URL.
-
- :type: :obj:`basestring`
- """)
-
- repository_credential = Column(modeling_types.StrictDict(basestring,
basestring), doc="""
- Credentials for accessing the repository.
-
- :type: {:obj:`basestring`, :obj:`basestring`}
- """)
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('type_name', self.type.name),
- ('source_path', self.source_path),
- ('target_path', self.target_path),
- ('repository_url', self.repository_url),
- ('repository_credential',
formatting.as_agnostic(self.repository_credential)),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self):
- utils.validate_dict_values(self.properties)
-
- def coerce_values(self, report_issues):
- utils.coerce_dict_values(self.properties, report_issues)
-
- def dump(self):
- context = ConsumptionContext.get_thread_local()
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- console.puts('Artifact type:
{0}'.format(context.style.type(self.type.name)))
- console.puts('Source path:
{0}'.format(context.style.literal(self.source_path)))
- if self.target_path is not None:
- console.puts('Target path:
{0}'.format(context.style.literal(self.target_path)))
- if self.repository_url is not None:
- console.puts('Repository URL: {0}'.format(
- context.style.literal(self.repository_url)))
- if self.repository_credential:
- console.puts('Repository credential: {0}'.format(
- context.style.literal(self.repository_credential)))
- utils.dump_dict_values(self.properties, 'Properties')