You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by mx...@apache.org on 2017/03/12 09:45:51 UTC
[1/8] incubator-ariatosca git commit: All tests pass :) [Forced
Update!]
Repository: incubator-ariatosca
Updated Branches:
refs/heads/Unified_coerce 2c2fe8688 -> 367b18bc0 (forced update)
All tests pass :)
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/aa01cd4e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/aa01cd4e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/aa01cd4e
Branch: refs/heads/Unified_coerce
Commit: aa01cd4e9a60507bafb98707c7793c3a89424db8
Parents: dd99f0f
Author: Tal Liron <ta...@gmail.com>
Authored: Tue Mar 7 12:13:28 2017 -0600
Committer: Tal Liron <ta...@gmail.com>
Committed: Tue Mar 7 21:22:45 2017 -0600
----------------------------------------------------------------------
aria/modeling/bases.py | 2 +-
aria/modeling/misc.py | 6 +-
aria/modeling/orchestration.py | 2 +-
aria/modeling/service.py | 12 +-
aria/modeling/service_template.py | 8 +-
aria/orchestrator/workflows/api/task.py | 2 +-
aria/orchestrator/workflows/api/task_graph.py | 2 +-
.../workflows/builtin/execute_operation.py | 18 +-
aria/orchestrator/workflows/builtin/heal.py | 188 +++++++++----------
aria/parser/modeling/context.py | 5 +-
aria/utils/uuid.py | 6 +-
docs/requirements.txt | 2 +-
tests/mock/models.py | 6 +-
tests/mock/topology.py | 6 +-
tests/orchestrator/context/test_operation.py | 9 +-
.../orchestrator/execution_plugin/test_local.py | 26 +--
tests/orchestrator/execution_plugin/test_ssh.py | 29 +--
.../orchestrator/workflows/builtin/test_heal.py | 20 +-
tests/storage/test_models.py | 8 +-
tests/storage/test_structures.py | 12 +-
20 files changed, 187 insertions(+), 182 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/modeling/bases.py
----------------------------------------------------------------------
diff --git a/aria/modeling/bases.py b/aria/modeling/bases.py
index a4db320..efcb968 100644
--- a/aria/modeling/bases.py
+++ b/aria/modeling/bases.py
@@ -119,7 +119,7 @@ class ModelMixin(object):
return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
backreference, key=key, foreign_key=foreign_key)
-
+
@classmethod
def one_to_many_relationship(cls,
child_table,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/modeling/misc.py
----------------------------------------------------------------------
diff --git a/aria/modeling/misc.py b/aria/modeling/misc.py
index 0bb5cda..105876a 100644
--- a/aria/modeling/misc.py
+++ b/aria/modeling/misc.py
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# pylint: disable=no-self-argument, no-member, abstract-method
+
import cPickle as pickle
import logging
@@ -113,7 +115,7 @@ class TypeBase(InstanceModelMixin):
__tablename__ = 'type'
- variant = Column(Text, nullable=False)
+ variant = Column(Text, nullable=False)
description = Column(Text)
_role = Column(Text, name='role')
@@ -135,7 +137,7 @@ class TypeBase(InstanceModelMixin):
return cls.foreign_key('type', nullable=True)
# endregion
-
+
@property
def role(self):
def get_role(the_type):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/modeling/orchestration.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index d9d9908..c842c07 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -152,7 +152,7 @@ class ServiceUpdateBase(ModelMixin):
steps = None
- __tablename__ = 'service_update'
+ __tablename__ = 'service_update'
_private_fields = ['execution_fk',
'service_fk']
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/modeling/service.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service.py b/aria/modeling/service.py
index eb8acb5..bf189f7 100644
--- a/aria/modeling/service.py
+++ b/aria/modeling/service.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# pylint: disable=no-self-argument, no-member, abstract-method
+# pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
from sqlalchemy import (
Column,
@@ -286,7 +286,7 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
self._dump_graph_node(context, target_node)
-class NodeBase(InstanceModelMixin):
+class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
"""
Usually an instance of a :class:`NodeTemplate`.
@@ -844,7 +844,7 @@ class SubstitutionBase(InstanceModelMixin):
class SubstitutionMappingBase(InstanceModelMixin):
"""
Used by :class:`Substitution` to map a capability or a requirement to a node.
-
+
Only one of `capability_template` and `requirement_template` can be set.
Usually an instance of a :class:`SubstitutionTemplate`.
@@ -1186,7 +1186,7 @@ class CapabilityBase(InstanceModelMixin):
class InterfaceBase(InstanceModelMixin):
"""
A typed set of :class:`Operation`.
-
+
Usually an instance of :class:`InterfaceTemplate`.
:ivar name: Name (unique for the node, group, or relationship)
@@ -1296,7 +1296,7 @@ class InterfaceBase(InstanceModelMixin):
class OperationBase(InstanceModelMixin):
"""
An operation in a :class:`Interface`.
-
+
Might be an instance of :class:`OperationTemplate`.
:ivar name: Name (unique for the interface or service)
@@ -1423,7 +1423,7 @@ class OperationBase(InstanceModelMixin):
class ArtifactBase(InstanceModelMixin):
"""
A file associated with a :class:`Node`.
-
+
Usually an instance of :class:`ArtifactTemplate`.
:ivar name: Name (unique for the node)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index ebbe904..092de51 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -37,7 +37,7 @@ from . import (
)
-class ServiceTemplateBase(TemplateModelMixin):
+class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public-methods
"""
A service template is a source for creating :class:`Service` instances.
@@ -827,7 +827,7 @@ class SubstitutionTemplateBase(TemplateModelMixin):
class SubstitutionTemplateMappingBase(TemplateModelMixin):
"""
Used by :class:`SubstitutionTemplate` to map a capability or a requirement to a node.
-
+
Only one of `capability_template` and `requirement_template` can be set.
:ivar name: Exposed capability or requirement name
@@ -1120,7 +1120,7 @@ class RelationshipTemplateBase(TemplateModelMixin):
"""
Optional addition to a :class:`RequirementTemplate` in :class:`NodeTemplate` that can be applied
when the requirement is matched with a capability.
-
+
Note that a relationship template here is not equivalent to a relationship template entity in
TOSCA. For example, a TOSCA requirement specifying a relationship type instead of a template
would still be represented here as a relationship template.
@@ -1276,7 +1276,7 @@ class CapabilityTemplateBase(TemplateModelMixin):
# endregion
def satisfies_requirement(self,
- context,
+ context, # pylint: disable=unused-argument
source_node_template,
requirement,
target_node_template):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index f1812b1..d434da8 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -61,7 +61,7 @@ class OperationTask(BaseTask):
SOURCE_OPERATION = 'source'
TARGET_OPERATION = 'target'
-
+
NAME_FORMAT = '{type}:{id}->{interface}/{operation}'
def __init__(self,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/orchestrator/workflows/api/task_graph.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task_graph.py b/aria/orchestrator/workflows/api/task_graph.py
index 2ead4d0..92a39d2 100644
--- a/aria/orchestrator/workflows/api/task_graph.py
+++ b/aria/orchestrator/workflows/api/task_graph.py
@@ -17,11 +17,11 @@
Task graph. Used by users to build workflows
"""
-from ....utils.uuid import generate_uuid
from collections import Iterable
from networkx import DiGraph, topological_sort
+from ....utils.uuid import generate_uuid
from . import task as api_task
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/orchestrator/workflows/builtin/execute_operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py
index e7c2085..ed4ada3 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -51,24 +51,24 @@ def execute_operation(
"""
subgraphs = {}
# filtering node instances
- filtered_nodes = list(_filter_node_instances(
+ filtered_nodes = list(_filter_nodes(
context=ctx,
node_template_ids=node_template_ids,
node_ids=node_ids,
type_names=type_names))
if run_by_dependency_order:
- filtered_node_instances_ids = set(node_instance.id
+ filtered_node_ids = set(node_instance.id
for node_instance in filtered_nodes)
- for node in ctx.node_instances:
- if node.id not in filtered_node_instances_ids:
+ for node in ctx.nodes:
+ if node.id not in filtered_node_ids:
subgraphs[node.id] = ctx.task_graph(
name='execute_operation_stub_{0}'.format(node.id))
# registering actual tasks to sequences
for node in filtered_nodes:
graph.add_tasks(
- _create_node_instance_task(
+ _create_node_task(
node=node,
interface_name=interface_name,
operation_name=operation_name,
@@ -77,8 +77,8 @@ def execute_operation(
)
)
- for _, node_instance_sub_workflow in subgraphs.items():
- graph.add_tasks(node_instance_sub_workflow)
+ for _, node_sub_workflow in subgraphs.items():
+ graph.add_tasks(node_sub_workflow)
# adding tasks dependencies if required
if run_by_dependency_order:
@@ -88,7 +88,7 @@ def execute_operation(
source_task=subgraphs[node.id], after=[subgraphs[relationship.target_id]])
-def _filter_node_instances(context, node_template_ids=(), node_ids=(), type_names=()):
+def _filter_nodes(context, node_template_ids=(), node_ids=(), type_names=()):
def _is_node_template_by_id(node_template_id):
return not node_template_ids or node_template_id in node_template_ids
@@ -105,7 +105,7 @@ def _filter_node_instances(context, node_template_ids=(), node_ids=(), type_name
yield node
-def _create_node_instance_task(
+def _create_node_task(
node,
interface_name,
operation_name,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/orchestrator/workflows/builtin/heal.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/heal.py b/aria/orchestrator/workflows/builtin/heal.py
index 2592323..92b96ea 100644
--- a/aria/orchestrator/workflows/builtin/heal.py
+++ b/aria/orchestrator/workflows/builtin/heal.py
@@ -26,156 +26,156 @@ from ..api import task
@workflow
-def heal(ctx, graph, node_instance_id):
+def heal(ctx, graph, node_id):
"""
The heal workflow
:param WorkflowContext ctx: the workflow context
:param TaskGraph graph: the graph which will describe the workflow.
- :param node_instance_id: the id of the node instance to heal
+ :param node_id: the id of the node to heal
:return:
"""
- failing_node = ctx.model.node.get(node_instance_id)
+ failing_node = ctx.model.node.get(node_id)
host_node = ctx.model.node.get(failing_node.host.id)
- failed_node_instance_subgraph = _get_contained_subgraph(ctx, host_node)
- failed_node_instance_ids = list(n.id for n in failed_node_instance_subgraph)
+ failed_node_subgraph = _get_contained_subgraph(ctx, host_node)
+ failed_node_ids = list(n.id for n in failed_node_subgraph)
- targeted_node_instances = [node_instance for node_instance in ctx.node_instances
- if node_instance.id not in failed_node_instance_ids]
+ targeted_nodes = [node for node in ctx.nodes
+ if node.id not in failed_node_ids]
uninstall_subgraph = task.WorkflowTask(
heal_uninstall,
- failing_node_instances=failed_node_instance_subgraph,
- targeted_node_instances=targeted_node_instances
+ failing_nodes=failed_node_subgraph,
+ targeted_nodes=targeted_nodes
)
install_subgraph = task.WorkflowTask(
heal_install,
- failing_node_instances=failed_node_instance_subgraph,
- targeted_node_instances=targeted_node_instances)
+ failing_nodes=failed_node_subgraph,
+ targeted_nodes=targeted_nodes)
graph.sequence(uninstall_subgraph, install_subgraph)
-@workflow(suffix_template='{failing_node_instances}')
-def heal_uninstall(ctx, graph, failing_node_instances, targeted_node_instances):
+@workflow(suffix_template='{failing_nodes}')
+def heal_uninstall(ctx, graph, failing_nodes, targeted_nodes):
"""
the uninstall part of the heal mechanism
:param WorkflowContext ctx: the workflow context
:param TaskGraph graph: the task graph to edit.
- :param failing_node_instances: the failing nodes to heal.
- :param targeted_node_instances: the targets of the relationships where the failing node are
+ :param failing_nodes: the failing nodes to heal.
+ :param targeted_nodes: the targets of the relationships where the failing node are
source
:return:
"""
- node_instance_sub_workflows = {}
-
- # Create install stub workflow for each unaffected node instance
- for node_instance in targeted_node_instances:
- node_instance_stub = task.StubTask()
- node_instance_sub_workflows[node_instance.id] = node_instance_stub
- graph.add_tasks(node_instance_stub)
-
- # create install sub workflow for every node instance
- for node_instance in failing_node_instances:
- node_instance_sub_workflow = task.WorkflowTask(uninstall_node,
- node_instance=node_instance)
- node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_tasks(node_instance_sub_workflow)
-
- # create dependencies between the node instance sub workflow
- for node_instance in failing_node_instances:
- node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
- for relationship_instance in reversed(node_instance.outbound_relationship_instances):
+ node_sub_workflows = {}
+
+ # Create install stub workflow for each unaffected node
+ for node in targeted_nodes:
+ node_stub = task.StubTask()
+ node_sub_workflows[node.id] = node_stub
+ graph.add_tasks(node_stub)
+
+ # create install sub workflow for every node
+ for node in failing_nodes:
+ node_sub_workflow = task.WorkflowTask(uninstall_node,
+ node=node)
+ node_sub_workflows[node.id] = node_sub_workflow
+ graph.add_tasks(node_sub_workflow)
+
+ # create dependencies between the node sub workflow
+ for node in failing_nodes:
+ node_sub_workflow = node_sub_workflows[node.id]
+ for relationship in reversed(node.outbound_relationships):
graph.add_dependency(
- node_instance_sub_workflows[relationship_instance.target_node_instance.id],
- node_instance_sub_workflow)
+ node_sub_workflows[relationship.target_node.id],
+ node_sub_workflow)
- # Add operations for intact nodes depending on a node instance belonging to node_instances
- for node_instance in targeted_node_instances:
- node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
+ # Add operations for intact nodes depending on a node belonging to nodes
+ for node in targeted_nodes:
+ node_sub_workflow = node_sub_workflows[node.id]
- for relationship_instance in reversed(node_instance.outbound_relationship_instances):
+ for relationship in reversed(node.outbound_relationships):
- target_node_instance = \
- ctx.model.node.get(relationship_instance.target_node_instance.id)
- target_node_instance_subgraph = node_instance_sub_workflows[target_node_instance.id]
- graph.add_dependency(target_node_instance_subgraph, node_instance_sub_workflow)
+ target_node = \
+ ctx.model.node.get(relationship.target_node.id)
+ target_node_subgraph = node_sub_workflows[target_node.id]
+ graph.add_dependency(target_node_subgraph, node_sub_workflow)
- if target_node_instance in failing_node_instances:
+ if target_node in failing_nodes:
dependency = relationship_tasks(
- relationship_instance=relationship_instance,
+ relationship=relationship,
operation_name='aria.interfaces.relationship_lifecycle.unlink')
graph.add_tasks(*dependency)
- graph.add_dependency(node_instance_sub_workflow, dependency)
+ graph.add_dependency(node_sub_workflow, dependency)
-@workflow(suffix_template='{failing_node_instances}')
-def heal_install(ctx, graph, failing_node_instances, targeted_node_instances):
+@workflow(suffix_template='{failing_nodes}')
+def heal_install(ctx, graph, failing_nodes, targeted_nodes):
"""
the install part of the heal mechanism
:param WorkflowContext ctx: the workflow context
:param TaskGraph graph: the task graph to edit.
- :param failing_node_instances: the failing nodes to heal.
- :param targeted_node_instances: the targets of the relationships where the failing node are
+ :param failing_nodes: the failing nodes to heal.
+ :param targeted_nodes: the targets of the relationships where the failing node are
source
:return:
"""
- node_instance_sub_workflows = {}
+ node_sub_workflows = {}
# Create install sub workflow for each unaffected
- for node_instance in targeted_node_instances:
- node_instance_stub = task.StubTask()
- node_instance_sub_workflows[node_instance.id] = node_instance_stub
- graph.add_tasks(node_instance_stub)
-
- # create install sub workflow for every node instance
- for node_instance in failing_node_instances:
- node_instance_sub_workflow = task.WorkflowTask(install_node,
- node_instance=node_instance)
- node_instance_sub_workflows[node_instance.id] = node_instance_sub_workflow
- graph.add_tasks(node_instance_sub_workflow)
-
- # create dependencies between the node instance sub workflow
- for node_instance in failing_node_instances:
- node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
- if node_instance.outbound_relationship_instances:
+ for node in targeted_nodes:
+ node_stub = task.StubTask()
+ node_sub_workflows[node.id] = node_stub
+ graph.add_tasks(node_stub)
+
+ # create install sub workflow for every node
+ for node in failing_nodes:
+ node_sub_workflow = task.WorkflowTask(install_node,
+ node=node)
+ node_sub_workflows[node.id] = node_sub_workflow
+ graph.add_tasks(node_sub_workflow)
+
+ # create dependencies between the node sub workflow
+ for node in failing_nodes:
+ node_sub_workflow = node_sub_workflows[node.id]
+ if node.outbound_relationships:
dependencies = \
- [node_instance_sub_workflows[relationship_instance.target_node_instance.id]
- for relationship_instance in node_instance.outbound_relationship_instances]
- graph.add_dependency(node_instance_sub_workflow, dependencies)
-
- # Add operations for intact nodes depending on a node instance
- # belonging to node_instances
- for node_instance in targeted_node_instances:
- node_instance_sub_workflow = node_instance_sub_workflows[node_instance.id]
-
- for relationship_instance in node_instance.outbound_relationship_instances:
- target_node_instance = ctx.model.node.get(
- relationship_instance.target_node_instance.id)
- target_node_instance_subworkflow = node_instance_sub_workflows[target_node_instance.id]
- graph.add_dependency(node_instance_sub_workflow, target_node_instance_subworkflow)
-
- if target_node_instance in failing_node_instances:
+ [node_sub_workflows[relationship.target_node.id]
+ for relationship in node.outbound_relationships]
+ graph.add_dependency(node_sub_workflow, dependencies)
+
+ # Add operations for intact nodes depending on a node
+ # belonging to nodes
+ for node in targeted_nodes:
+ node_sub_workflow = node_sub_workflows[node.id]
+
+ for relationship in node.outbound_relationships:
+ target_node = ctx.model.node.get(
+ relationship.target_node.id)
+ target_node_subworkflow = node_sub_workflows[target_node.id]
+ graph.add_dependency(node_sub_workflow, target_node_subworkflow)
+
+ if target_node in failing_nodes:
dependent = relationship_tasks(
- relationship_instance=relationship_instance,
+ relationship=relationship,
operation_name='aria.interfaces.relationship_lifecycle.establish')
graph.add_tasks(*dependent)
- graph.add_dependency(dependent, node_instance_sub_workflow)
+ graph.add_dependency(dependent, node_sub_workflow)
-def _get_contained_subgraph(context, host_node_instance):
- contained_instances = [node_instance
- for node_instance in context.node_instances
- if node_instance.host_fk == host_node_instance.id and
- node_instance.host_fk != node_instance.id]
- result = [host_node_instance]
+def _get_contained_subgraph(context, host_node):
+ contained_instances = [node
+ for node in context.nodes
+ if node.host_fk == host_node.id and
+ node.host_fk != node.id]
+ result = [host_node]
if not contained_instances:
return result
result.extend(contained_instances)
- for node_instance in contained_instances:
- result.extend(_get_contained_subgraph(context, node_instance))
+ for node in contained_instances:
+ result.extend(_get_contained_subgraph(context, node))
return set(result)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/parser/modeling/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/context.py b/aria/parser/modeling/context.py
index 52e724a..dff5991 100644
--- a/aria/parser/modeling/context.py
+++ b/aria/parser/modeling/context.py
@@ -15,9 +15,7 @@
import itertools
-from ...utils.collections import StrictDict, prune, OrderedDict
-from ...utils.formatting import as_raw
-from ...utils.console import puts
+from ...utils.collections import StrictDict, prune
from ...utils.uuid import generate_uuid
@@ -51,7 +49,6 @@ class ModelingContext(object):
"""
def __init__(self):
- from ...modeling.models import Type
self.template = None
self.instance = None
self.node_id_format = '{template}_{id}'
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/aria/utils/uuid.py
----------------------------------------------------------------------
diff --git a/aria/utils/uuid.py b/aria/utils/uuid.py
index b5f39f8..1f340c6 100644
--- a/aria/utils/uuid.py
+++ b/aria/utils/uuid.py
@@ -31,10 +31,10 @@ UUID_LOWERCASE_ALPHANUMERIC = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123
def generate_uuid(length=None, variant='base57'):
"""
A random string with varying degrees of guarantee of universal uniqueness.
-
+
:param variant: options are:
- * 'base57' (the default) uses a mix of upper and lowercase alphanumerics ensuring
- no visually ambiguous characters; default length 22
+ * 'base57' (the default) uses a mix of upper and lowercase alphanumerics
+ ensuring no visually ambiguous characters; default length 22
* 'alphanumeric' uses lowercase alphanumeric; default length 25
* 'uuid' user lowercase hexadecimal in the classic UUID format, including
dashes; length is always 36
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/docs/requirements.txt
----------------------------------------------------------------------
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 7baba04..669522a 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -11,4 +11,4 @@
# limitations under the License.
Sphinx==1.5.3
-sphinx_rtd_theme==0.2.0
+sphinx_rtd_theme==0.2.2
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 78e9373..716254e 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -65,12 +65,12 @@ def create_service(service_template):
def create_dependency_node_template(service_template):
node_type = service_template.node_types.get_descendant('test_node_type')
capability_type = service_template.capability_types.get_descendant('test_capability_type')
-
+
capability_template = models.CapabilityTemplate(
name='capability',
type=capability_type
)
-
+
node_template = models.NodeTemplate(
name=DEPENDENCY_NODE_TEMPLATE_NAME,
type=node_type,
@@ -90,7 +90,7 @@ def create_dependent_node_template(service_template, dependency_node_template):
operation_templates = dict((op, models.OperationTemplate(
name=op,
implementation='test'))
- for _, op in operations.NODE_OPERATIONS)
+ for _, op in operations.NODE_OPERATIONS)
interface_template = models.InterfaceTemplate(
type=service_template.interface_types.get_descendant('test_interface_type'),
operation_templates=operation_templates)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/mock/topology.py
----------------------------------------------------------------------
diff --git a/tests/mock/topology.py b/tests/mock/topology.py
index c7d8087..5f31661 100644
--- a/tests/mock/topology.py
+++ b/tests/mock/topology.py
@@ -31,7 +31,7 @@ def create_simple_topology_single_node(model_storage, create_operation):
inputs={'key': aria_models.Parameter(name='key', value='create', type_name='string'),
'value': aria_models.Parameter(name='value', value=True, type_name='boolean')})
)
- node_template.interface_templates[interface_template.name] = interface_template
+ node_template.interface_templates[interface_template.name] = interface_template # pylint: disable=unsubscriptable-object
node = models.create_dependency_node(node_template, service)
interface = models.create_interface(
@@ -42,7 +42,7 @@ def create_simple_topology_single_node(model_storage, create_operation):
inputs={'key': aria_models.Parameter(name='key', value='create', type_name='string'),
'value': aria_models.Parameter(name='value', value=True, type_name='boolean')})
)
- node.interfaces[interface.name] = interface
+ node.interfaces[interface.name] = interface # pylint: disable=unsubscriptable-object
model_storage.service_template.put(service_template)
model_storage.service.put(service)
@@ -61,7 +61,7 @@ def create_simple_topology_two_nodes(model_storage):
dependency_node = models.create_dependency_node(dependency_node_template, service)
dependent_node = models.create_dependent_node(dependent_node_template, service)
- dependent_node.outbound_relationships.append(models.create_relationship(
+ dependent_node.outbound_relationships.append(models.create_relationship( # pylint: disable=no-member
source=dependent_node,
target=dependency_node
))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index ce3bd84..8ac8d49 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -101,7 +101,7 @@ def test_node_operation_task_execution(ctx, executor):
)
operations = interface.operations
assert len(operations) == 1
- assert operation_context.task.implementation == operations.values()[0].implementation
+ assert operation_context.task.implementation == operations.values()[0].implementation # pylint: disable=no-member
assert operation_context.task.inputs['putput'].value is True
# Context based attributes (sugaring)
@@ -151,7 +151,7 @@ def test_relationship_operation_task_execution(ctx, executor):
assert operation_context.task.actor == relationship
assert interface_name in operation_context.task.name
operations = interface.operations
- assert operation_context.task.implementation == operations.values()[0].implementation
+ assert operation_context.task.implementation == operations.values()[0].implementation # pylint: disable=no-member
assert operation_context.task.inputs['putput'].value is True
# Context based attributes (sugaring)
@@ -172,7 +172,7 @@ def test_relationship_operation_task_execution(ctx, executor):
def test_invalid_task_operation_id(ctx, executor):
"""
Checks that the right id is used. The task created with id == 1, thus running the task on
- node_instance with id == 2. will check that indeed the node_instance uses the correct id.
+ node with id == 2. will check that indeed the node uses the correct id.
:param ctx:
:param executor:
:return:
@@ -241,8 +241,7 @@ def test_plugin_workdir(ctx, executor, tmpdir):
graph.add_tasks(api.task.OperationTask.for_node(node=node,
interface_name=interface_name,
operation_name=operation_name,
- inputs=inputs)
- )
+ inputs=inputs))
execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
expected_file = tmpdir.join('workdir', 'plugins', str(ctx.service.id),
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/orchestrator/execution_plugin/test_local.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_local.py b/tests/orchestrator/execution_plugin/test_local.py
index 9e9540f..6f146a6 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -462,7 +462,7 @@ if __name__ == '__main__':
script_path = os.path.basename(local_script_path) if local_script_path else None
if script_path:
workflow_context.resource.deployment.upload(
- entry_id=str(workflow_context.service_instance.id),
+ entry_id=str(workflow_context.service.id),
source=local_script_path,
path=script_path)
@@ -475,20 +475,20 @@ if __name__ == '__main__':
@workflow
def mock_workflow(ctx, graph):
- op = 'test.op'
- node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node.interfaces = [mock.models.get_interface(
- op,
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ interface = mock.models.create_interface(
+ node.service,
+ 'test',
+ 'op',
operation_kwargs=dict(implementation='{0}.{1}'.format(
operations.__name__,
operations.run_script_locally.__name__))
- )]
- # node.operations[op] = {
- # 'operation': '{0}.{1}'.format(operations.__name__,
- # operations.run_script_locally.__name__)}
- graph.add_tasks(api.task.OperationTask.node(
- instance=node,
- name=op,
+ )
+ node.interfaces[interface.name] = interface
+ graph.add_tasks(api.task.OperationTask.for_node(
+ node=node,
+ interface_name='test',
+ operation_name='op',
inputs=inputs))
return graph
tasks_graph = mock_workflow(ctx=workflow_context) # pylint: disable=no-value-for-parameter
@@ -498,7 +498,7 @@ if __name__ == '__main__':
tasks_graph=tasks_graph)
eng.execute()
return workflow_context.model.node.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
+ mock.models.DEPENDENCY_NODE_NAME).runtime_properties
@pytest.fixture
def executor(self):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/orchestrator/execution_plugin/test_ssh.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/execution_plugin/test_ssh.py b/tests/orchestrator/execution_plugin/test_ssh.py
index a65ee34..78341b8 100644
--- a/tests/orchestrator/execution_plugin/test_ssh.py
+++ b/tests/orchestrator/execution_plugin/test_ssh.py
@@ -124,10 +124,10 @@ class TestWithActualSSHServer(object):
def test_run_script_download_resource_and_render(self, tmpdir):
resource = tmpdir.join('resource')
- resource.write('{{ctx.service_instance.name}}')
+ resource.write('{{ctx.service.name}}')
self._upload(str(resource), 'test_resource')
props = self._execute()
- assert props['test_value'] == self._workflow_context.service_instance.name
+ assert props['test_value'] == self._workflow_context.service.name
@pytest.mark.parametrize('value', ['string-value', [1, 2, 3], {'key': 'value'}])
def test_run_script_inputs_as_env_variables_no_override(self, value):
@@ -216,15 +216,20 @@ class TestWithActualSSHServer(object):
@workflow
def mock_workflow(ctx, graph):
- op = 'test.op'
- node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node.interfaces = [mock.models.get_interface(
- op,
- dict(implementation='{0}.{1}'.format(operations.__name__, operation.__name__))
- )]
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ interface = mock.models.create_interface(
+ node.service,
+ 'test',
+ 'op',
+ operation_kwargs=dict(implementation='{0}.{1}'.format(
+ operations.__name__,
+ operation.__name__))
+ )
+ node.interfaces[interface.name] = interface
graph.sequence(*[api.task.OperationTask.for_node(
node=node,
- name=op,
+ interface_name='test',
+ operation_name='op',
inputs={
'script_path': script_path,
'fabric_env': _FABRIC_ENV,
@@ -243,7 +248,7 @@ class TestWithActualSSHServer(object):
tasks_graph=tasks_graph)
eng.execute()
return self._workflow_context.model.node.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
+ mock.models.DEPENDENCY_NODE_NAME).runtime_properties
def _execute_and_get_task_exception(self, *args, **kwargs):
signal = events.on_failure_task_signal
@@ -254,7 +259,7 @@ class TestWithActualSSHServer(object):
def _upload(self, source, path):
self._workflow_context.resource.deployment.upload(
- entry_id=str(self._workflow_context.service_instance.id),
+ entry_id=str(self._workflow_context.service.id),
source=source,
path=path)
@@ -407,7 +412,7 @@ class TestFabricEnvHideGroupsAndRunCommands(object):
class Stub(object):
@staticmethod
def abort(message=None):
- model.Task.abort(message)
+ models.Task.abort(message)
ip = None
task = Stub
task.runs_on = Stub
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/orchestrator/workflows/builtin/test_heal.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/builtin/test_heal.py b/tests/orchestrator/workflows/builtin/test_heal.py
index b553049..92fa7ea 100644
--- a/tests/orchestrator/workflows/builtin/test_heal.py
+++ b/tests/orchestrator/workflows/builtin/test_heal.py
@@ -33,11 +33,11 @@ def ctx(tmpdir):
@pytest.mark.skip(reason='heal is not implemented for now')
def test_heal_dependent_node(ctx):
- dependent_node_instance = \
- ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
- dependent_node_instance.host_fk = dependent_node_instance.id
- ctx.model.node.update(dependent_node_instance)
- heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependent_node_instance.id)
+ dependent_node = \
+ ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+ dependent_node.host_fk = dependent_node.id
+ ctx.model.node.update(dependent_node)
+ heal_graph = task.WorkflowTask(heal, ctx=ctx, node_id=dependent_node.id)
assert len(list(heal_graph.tasks)) == 2
uninstall_subgraph, install_subgraph = list(heal_graph.topological_order(reverse=True))
@@ -63,11 +63,11 @@ def test_heal_dependent_node(ctx):
@pytest.mark.skip(reason='heal is not implemented for now')
def test_heal_dependency_node(ctx):
- dependency_node_instance = \
- ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- dependency_node_instance.host_fk = dependency_node_instance.id
- ctx.model.node.update(dependency_node_instance)
- heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependency_node_instance.id)
+ dependency_node = \
+ ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ dependency_node.host_fk = dependency_node.id
+ ctx.model.node.update(dependency_node)
+ heal_graph = task.WorkflowTask(heal, ctx=ctx, node_id=dependency_node.id)
# both subgraphs should contain un\install for both the dependent and the dependency
assert len(list(heal_graph.tasks)) == 2
uninstall_subgraph, install_subgraph = list(heal_graph.topological_order(reverse=True))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/storage/test_models.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py
index 0088314..c80659b 100644
--- a/tests/storage/test_models.py
+++ b/tests/storage/test_models.py
@@ -662,13 +662,15 @@ class TestNodeInstanceIP(object):
@pytest.mark.skip('Should be reworked into relationship')
class TestRelationship(object):
def test_relationship_model_creation(self, nodes_storage):
+ nodes = nodes_storage.node
+ source_node = nodes.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+ target_node = nodes.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+
relationship = mock.models.create_relationship(
+ source=source_node,
target=nodes_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
)
nodes_storage.relationship.put(relationship)
- nodes = nodes_storage.node
- source_node = nodes.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- target_node = nodes.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
relationship_instance = _test_model(
is_valid=True,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/aa01cd4e/tests/storage/test_structures.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_structures.py b/tests/storage/test_structures.py
index ceaea11..cacec2e 100644
--- a/tests/storage/test_structures.py
+++ b/tests/storage/test_structures.py
@@ -47,7 +47,7 @@ def storage():
@pytest.fixture(scope='module', autouse=True)
def module_cleanup():
- modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member
+ modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) # pylint: disable=no-member
@pytest.fixture
@@ -137,7 +137,7 @@ def test_relationship_model_ordering(context):
target_node=new_node,
))
- new_node.outbound_relationships.append(modeling.models.Relationship(
+ new_node.outbound_relationships.append(modeling.models.Relationship( # pylint: disable=no-member
source_node=new_node,
target_node=target_node,
))
@@ -159,12 +159,12 @@ def test_relationship_model_ordering(context):
relationships = getattr(node, direction + '_relationships')
assert len(relationships) == 2
- reversed_relationship_instances = list(reversed(relationships))
- assert relationships != reversed_relationship_instances
+ reversed_relationship = list(reversed(relationships))
+ assert relationships != reversed_relationship
- relationships[:] = reversed_relationship_instances
+ relationships[:] = reversed_relationship
context.model.node.update(node)
- assert relationships == reversed_relationship_instances
+ assert relationships == reversed_relationship
flip_and_assert(source_node, 'outbound')
flip_and_assert(target_node, 'inbound')
[6/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service.py b/aria/modeling/service.py
deleted file mode 100644
index bf189f7..0000000
--- a/aria/modeling/service.py
+++ /dev/null
@@ -1,1529 +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.
-
-# pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
-
-from sqlalchemy import (
- Column,
- Text,
- Integer
-)
-from sqlalchemy import DateTime
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import declared_attr
-
-from .bases import InstanceModelMixin
-from ..parser import validation
-from ..utils import collections, formatting, console
-
-from . import (
- utils,
- types as modeling_types
-)
-
-
-class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
- """
- A service is usually an instance of a :class:`ServiceTemplate`.
-
- You will usually not create it programmatically, but instead instantiate it from a service
- template.
-
- :ivar name: Name (unique for this ARIA installation)
- :vartype name: basestring
- :ivar service_template: Template from which this service was instantiated (optional)
- :vartype service_template: :class:`ServiceTemplate`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar meta_data: Custom annotations
- :vartype meta_data: {basestring: :class:`Metadata`}
- :ivar node: Nodes
- :vartype node: [:class:`Node`]
- :ivar groups: Groups of nodes
- :vartype groups: [:class:`Group`]
- :ivar policies: Policies
- :vartype policies: [:class:`Policy`]
- :ivar substitution: The entire service can appear as a node
- :vartype substitution: :class:`Substitution`
- :ivar inputs: Externally provided parameters
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar outputs: These parameters are filled in after service installation
- :vartype outputs: {basestring: :class:`Parameter`}
- :ivar operations: Custom operations that can be performed on the service
- :vartype operations: {basestring: :class:`Operation`}
- :ivar plugins: Plugins required to be installed
- :vartype plugins: {basestring: :class:`Plugin`}
- :ivar created_at: Creation timestamp
- :vartype created_at: :class:`datetime.datetime`
- :ivar updated_at: Update timestamp
- :vartype updated_at: :class:`datetime.datetime`
-
- :ivar permalink: ??
- :vartype permalink: basestring
- :ivar scaling_groups: ??
- :vartype scaling_groups: {}
-
- :ivar modifications: Modifications of this service
- :vartype modifications: [:class:`ServiceModification`]
- :ivar updates: Updates of this service
- :vartype updates: [:class:`ServiceUpdate`]
- :ivar executions: Executions on this service
- :vartype executions: [:class:`Execution`]
- """
-
- __tablename__ = 'service'
-
- @declared_attr
- def service_template(cls):
- return cls.many_to_one_relationship('service_template')
-
- description = Column(Text)
-
- @declared_attr
- def meta_data(cls):
- # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy!
- return cls.many_to_many_relationship('metadata', dict_key='name')
-
- @declared_attr
- def nodes(cls):
- return cls.one_to_many_relationship('node')
-
- @declared_attr
- def groups(cls):
- return cls.one_to_many_relationship('group')
-
- @declared_attr
- def policies(cls):
- return cls.one_to_many_relationship('policy')
-
- @declared_attr
- def substitution(cls):
- return cls.one_to_one_relationship('substitution')
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- @declared_attr
- def outputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='outputs',
- dict_key='name')
-
- @declared_attr
- def operations(cls):
- return cls.one_to_many_relationship('operation', dict_key='name')
-
- @declared_attr
- def plugins(cls):
- return cls.many_to_many_relationship('plugin')
-
- created_at = Column(DateTime, nullable=False, index=True)
- updated_at = Column(DateTime)
-
- # region orchestration
-
- permalink = Column(Text)
- scaling_groups = Column(modeling_types.Dict)
-
- # endregion
-
- # region foreign keys
-
- __private_fields__ = ['substituion_fk',
- 'service_template_fk']
-
- # Service one-to-one to Substitution
- @declared_attr
- def substitution_fk(cls):
- return cls.foreign_key('substitution', nullable=True)
-
- # Service many-to-one to ServiceTemplate
- @declared_attr
- def service_template_fk(cls):
- return cls.foreign_key('service_template', nullable=True)
-
- # endregion
-
- def satisfy_requirements(self, context):
- satisfied = True
- for node in self.nodes:
- if not node.satisfy_requirements(context):
- satisfied = False
- return satisfied
-
- def validate_capabilities(self, context):
- satisfied = True
- for node in self.nodes:
- if not node.validate_capabilities(context):
- satisfied = False
- return satisfied
-
- def find_nodes(self, node_template_name):
- nodes = []
- for node in self.nodes:
- if node.node_template.name == node_template_name:
- nodes.append(node)
- return collections.FrozenList(nodes)
-
- def get_node_ids(self, node_template_name):
- return collections.FrozenList((node.name for node in self.find_nodes(node_template_name)))
-
- def find_groups(self, group_template_name):
- groups = []
- for group in self.groups:
- if group.template_name == group_template_name:
- groups.append(group)
- return collections.FrozenList(groups)
-
- def get_group_ids(self, group_template_name):
- return collections.FrozenList((group.name
- for group in self.find_groups(group_template_name)))
-
- def is_node_a_target(self, context, target_node):
- for node in self.nodes:
- if self._is_node_a_target(context, node, target_node):
- return True
- return False
-
- def _is_node_a_target(self, context, source_node, target_node):
- if source_node.relationships:
- for relationship in source_node.relationships:
- if relationship.target_node_id == target_node.name:
- return True
- else:
- node = context.modeling.instance.nodes.get(relationship.target_node_id)
- if node is not None:
- if self._is_node_a_target(context, 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)),
- ('operations', formatting.as_raw_list(self.operations))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.meta_data)
- utils.validate_list_values(context, self.nodes)
- utils.validate_list_values(context, self.groups)
- utils.validate_list_values(context, self.policies)
- if self.substitution is not None:
- self.substitution.validate(context)
- utils.validate_dict_values(context, self.inputs)
- utils.validate_dict_values(context, self.outputs)
- utils.validate_dict_values(context, self.operations)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.meta_data, report_issues)
- utils.coerce_list_values(context, container, self.nodes, report_issues)
- utils.coerce_list_values(context, container, self.groups, report_issues)
- utils.coerce_list_values(context, container, self.policies, report_issues)
- if self.substitution is not None:
- self.substitution.coerce_values(context, container, report_issues)
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.outputs, report_issues)
- utils.coerce_dict_values(context, container, self.operations, report_issues)
-
- def dump(self, context):
- if self.description is not None:
- console.puts(context.style.meta(self.description))
- utils.dump_dict_values(context, self.meta_data, 'Metadata')
- for node in self.nodes:
- node.dump(context)
- for group in self.groups:
- group.dump(context)
- for policy in self.policies:
- policy.dump(context)
- if self.substitution is not None:
- self.substitution.dump(context)
- utils.dump_dict_values(context, self.inputs, 'Inputs')
- utils.dump_dict_values(context, self.outputs, 'Outputs')
- utils.dump_dict_values(context, self.operations, 'Operations')
-
- def dump_graph(self, context):
- for node in self.nodes.itervalues():
- if not self.is_node_a_target(context, node):
- self._dump_graph_node(context, node)
-
- def _dump_graph_node(self, context, node):
- console.puts(context.style.node(node.name))
- if node.relationships:
- with context.style.indent:
- for relationship in node.relationships:
- relationship_name = (context.style.node(relationship.template_name)
- if relationship.template_name is not None
- else context.style.type(relationship.type_name))
- capability_name = (context.style.node(relationship.target_capability_name)
- if relationship.target_capability_name is not None
- else None)
- if capability_name is not None:
- console.puts('-> {0} {1}'.format(relationship_name, capability_name))
- else:
- console.puts('-> {0}'.format(relationship_name))
- target_node = self.nodes.get(relationship.target_node_id)
- with console.indent(3):
- self._dump_graph_node(context, target_node)
-
-
-class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
- """
- Usually an instance of a :class:`NodeTemplate`.
-
- Nodes may have zero or more :class:`Relationship` instances to other nodes.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar node_template: Template from which this node was instantiated (optional)
- :vartype node_template: :class:`NodeTemplate`
- :ivar type: Node type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interface`}
- :ivar artifacts: Associated files
- :vartype artifacts: {basestring: :class:`Artifact`}
- :ivar capabilities: Exposed capabilities
- :vartype capabilities: {basestring: :class:`Capability`}
- :ivar outbound_relationships: Relationships to other nodes
- :vartype outbound_relationships: [:class:`Relationship`]
- :ivar inbound_relationships: Relationships from other nodes
- :vartype inbound_relationships: [:class:`Relationship`]
- :ivar plugins: Plugins required to be installed on the node's host
- :vartype plugins: {basestring: :class:`Plugin`}
- :ivar host: Host node (can be self)
- :vartype host: :class:`Node`
-
- :ivar runtime_properties: TODO: should be replaced with attributes
- :vartype runtime_properties: {}
- :ivar scaling_groups: ??
- :vartype scaling_groups: []
- :ivar state: ??
- :vartype state: basestring
- :ivar version: ??
- :vartype version: int
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- :ivar groups: We are a member of these groups
- :vartype groups: [:class:`Group`]
- :ivar policies: Policies enacted on this node
- :vartype policies: [:class:`Policy`]
- :ivar substitution_mapping: Our contribution to service substitution
- :vartype substitution_mapping: :class:`SubstitutionMapping`
- :ivar tasks: Tasks on this node
- :vartype tasks: [:class:`Task`]
- """
-
- __tablename__ = 'node'
-
- @declared_attr
- def node_template(cls):
- return cls.many_to_one_relationship('node_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- @declared_attr
- def artifacts(cls):
- return cls.one_to_many_relationship('artifact', dict_key='name')
-
- @declared_attr
- def capabilities(cls):
- return cls.one_to_many_relationship('capability', dict_key='name')
-
- @declared_attr
- def outbound_relationships(cls):
- return cls.one_to_many_relationship('relationship',
- foreign_key='source_node_fk',
- backreference='source_node')
-
- @declared_attr
- def inbound_relationships(cls):
- return cls.one_to_many_relationship('relationship',
- foreign_key='target_node_fk',
- backreference='target_node')
-
- @declared_attr
- def plugins(cls):
- return cls.many_to_many_relationship('plugin')
-
- @declared_attr
- def host(cls):
- return cls.relationship_to_self('host_fk')
-
- # region orchestration
-
- runtime_properties = Column(modeling_types.Dict)
- scaling_groups = Column(modeling_types.List)
- state = Column(Text, nullable=False)
- version = Column(Integer, default=1)
-
- @declared_attr
- def service_name(cls):
- return association_proxy('service', 'name')
-
- @property
- def ip(self):
- # TODO: totally broken
- if not self.host_fk:
- return None
- host_node = self.host
- if 'ip' in host_node.runtime_properties: # pylint: disable=no-member
- return host_node.runtime_properties['ip'] # pylint: disable=no-member
- host_node = host_node.node_template # pylint: disable=no-member
- host_ip_property = host_node.properties.get('ip')
- if host_ip_property:
- return host_ip_property.value
- return None
-
- # endregion
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'host_fk',
- 'service_fk',
- 'node_template_fk']
-
- # Node many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-one to Node
- @declared_attr
- def host_fk(cls):
- return cls.foreign_key('node', nullable=True)
-
- # Service one-to-many to Node
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Node many-to-one to NodeTemplate
- @declared_attr
- def node_template_fk(cls):
- return cls.foreign_key('node_template', nullable=True)
-
- # endregion
-
- def satisfy_requirements(self, context):
- 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(context, node_template)
- if target_node_template is not None:
- satisfied = self._satisfy_capability(context,
- target_node_capability,
- target_node_template,
- requirement_template)
- else:
- 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, context, target_node_capability, target_node_template,
- requirement_template):
- from . import models
- # Find target nodes
- target_nodes = context.modeling.instance.find_nodes(target_node_template.name)
- 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:
- target_capability = node.capabilities.get(target_node_capability.name)
- if target_capability.relate():
- target_node = node
- 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 = \
- requirement_template.relationship_template.instantiate(context, self)
- else:
- relationship = models.Relationship(target_capability=target_capability)
- relationship.name = requirement_template.name
- relationship.requirement_template = requirement_template
- relationship.target_node = target_node
- self.outbound_relationships.append(relationship)
- 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):
- 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
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type_name),
- ('properties', 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):
- 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(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
- utils.validate_dict_values(context, self.artifacts)
- utils.validate_dict_values(context, self.capabilities)
- utils.validate_list_values(context, self.outbound_relationships)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interfaces, report_issues)
- utils.coerce_dict_values(context, self, self.artifacts, report_issues)
- utils.coerce_dict_values(context, self, self.capabilities, report_issues)
- utils.coerce_list_values(context, self, self.outbound_relationships, report_issues)
-
- def dump(self, context):
- 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(context, self.properties, 'Properties')
- utils.dump_interfaces(context, self.interfaces)
- utils.dump_dict_values(context, self.artifacts, 'Artifacts')
- utils.dump_dict_values(context, self.capabilities, 'Capabilities')
- utils.dump_list_values(context, self.outbound_relationships, 'Relationships')
-
-
-class GroupBase(InstanceModelMixin):
- """
- Usually an instance of a :class:`GroupTemplate`.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar group_template: Template from which this group was instantiated (optional)
- :vartype group_template: :class:`GroupTemplate`
- :ivar type: Group type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar nodes: Members of this group
- :vartype nodes: [:class:`Node`]
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interface`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- :ivar policies: Policies enacted on this group
- :vartype policies: [:class:`Policy`]
- """
-
- __tablename__ = 'group'
-
- @declared_attr
- def group_template(cls):
- return cls.many_to_one_relationship('group_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def nodes(cls):
- return cls.many_to_many_relationship('node')
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'service_fk',
- 'group_template_fk']
-
- # Group many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Service one-to-many to Group
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Group many-to-one to GroupTemplate
- @declared_attr
- def group_template_fk(cls):
- return cls.foreign_key('group_template', nullable=True)
-
- # endregion
-
- @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, context):
- utils.validate_dict_values(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
- def dump(self, context):
- 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(context, self.properties, 'Properties')
- utils.dump_interfaces(context, 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):
- """
- Usually an instance of a :class:`PolicyTemplate`.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar policy_template: Template from which this policy was instantiated (optional)
- :vartype policy_template: :class:`PolicyTemplate`
- :ivar type: Policy type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar nodes: Policy will be enacted on all these nodes
- :vartype nodes: [:class:`Node`]
- :ivar groups: Policy will be enacted on all nodes in these groups
- :vartype groups: [:class:`Group`]
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'policy'
-
- @declared_attr
- def policy_template(cls):
- return cls.many_to_one_relationship('policy_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def nodes(cls):
- return cls.many_to_many_relationship('node')
-
- @declared_attr
- def groups(cls):
- return cls.many_to_many_relationship('group')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'service_fk',
- 'policy_template_fk']
-
- # Policy many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Service one-to-many to Policy
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Policy many-to-one to PolicyTemplate
- @declared_attr
- def policy_template_fk(cls):
- return cls.foreign_key('policy_template', nullable=True)
-
- # endregion
-
- @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, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- 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(context, 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):
- """
- Used to substitute a single node for the entire deployment.
-
- Usually an instance of a :class:`SubstitutionTemplate`.
-
- :ivar substitution_template: Template from which this substitution was instantiated (optional)
- :vartype substitution_template: :class:`SubstitutionTemplate`
- :ivar node_type: Exposed node type
- :vartype node_type: :class:`Type`
- :ivar mappings: Requirement and capability mappings
- :vartype mappings: {basestring: :class:`SubstitutionTemplate`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'substitution'
-
- @declared_attr
- def substitution_template(cls):
- return cls.many_to_one_relationship('substitution_template')
-
- @declared_attr
- def node_type(cls):
- return cls.many_to_one_relationship('type')
-
- @declared_attr
- def mappings(cls):
- return cls.one_to_many_relationship('substitution_mapping', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['node_type_fk',
- 'substitution_template_fk']
-
- # Substitution many-to-one to Type
- @declared_attr
- def node_type_fk(cls):
- return cls.foreign_key('type')
-
- # Substitution many-to-one to SubstitutionTemplate
- @declared_attr
- def substitution_template_fk(cls):
- return cls.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, context):
- utils.validate_dict_values(context, self.mappings)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.mappings, report_issues)
-
- def dump(self, context):
- console.puts('Substitution:')
- with context.style.indent:
- console.puts('Node type: {0}'.format(context.style.type(self.node_type.name)))
- utils.dump_dict_values(context, self.mappings, 'Mappings')
-
-
-class SubstitutionMappingBase(InstanceModelMixin):
- """
- Used by :class:`Substitution` to map a capability or a requirement to a node.
-
- Only one of `capability_template` and `requirement_template` can be set.
-
- Usually an instance of a :class:`SubstitutionTemplate`.
-
- :ivar name: Exposed capability or requirement name
- :vartype name: basestring
- :ivar node: Node
- :vartype node: :class:`Node`
- :ivar capability: Capability in the node
- :vartype capability: :class:`Capability`
- :ivar requirement_template: Requirement template in the node template
- :vartype requirement_template: :class:`RequirementTemplate`
-
- :ivar substitution: Containing substitution
- :vartype substitution: :class:`Substitution`
- """
-
- __tablename__ = 'substitution_mapping'
-
- @declared_attr
- def node(cls):
- return cls.one_to_one_relationship('node')
-
- @declared_attr
- def capability(cls):
- return cls.one_to_one_relationship('capability')
-
- @declared_attr
- def requirement_template(cls):
- return cls.one_to_one_relationship('requirement_template')
-
- # region foreign keys
-
- __private_fields__ = ['substitution_fk',
- 'node_fk',
- 'capability_fk',
- 'requirement_template_fk']
-
- # Substitution one-to-many to SubstitutionMapping
- @declared_attr
- def substitution_fk(cls):
- return cls.foreign_key('substitution')
-
- # Substitution one-to-one to NodeTemplate
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Substitution one-to-one to Capability
- @declared_attr
- def capability_fk(cls):
- return cls.foreign_key('capability', nullable=True)
-
- # Substitution one-to-one to RequirementTemplate
- @declared_attr
- def requirement_template_fk(cls):
- return cls.foreign_key('requirement_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name)))
-
- def validate(self, context):
- 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):
- console.puts('{0} -> {1}.{2}'.format(
- context.style.node(self.name),
- context.style.node(self.node.name),
- context.style.node(self.capability.name
- if self.capability
- else self.requirement_template.name)))
-
-
-class RelationshipBase(InstanceModelMixin):
- """
- Connects :class:`Node` to a capability in another node.
-
- Might be an instance of a :class:`RelationshipTemplate`.
-
- :ivar name: Name (usually the name of the requirement at the source node template)
- :vartype name: basestring
- :ivar relationship_template: Template from which this relationship was instantiated (optional)
- :vartype relationship_template: :class:`RelationshipTemplate`
- :ivar requirement_template: Template from which this relationship was instantiated (optional)
- :vartype requirement_template: :class:`RequirementTemplate`
- :ivar type: Relationship type
- :vartype type: :class:`Type`
- :ivar target_capability: Capability at the target node (optional)
- :vartype target_capability: :class:`Capability`
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interfaces`}
-
- :ivar source_position: ??
- :vartype source_position: int
- :ivar target_position: ??
- :vartype target_position: int
-
- :ivar source_node: Source node
- :vartype source_node: :class:`Node`
- :ivar target_node: Target node
- :vartype target_node: :class:`Node`
- :ivar tasks: Tasks on this node
- :vartype tasks: [:class:`Task`]
- """
-
- __tablename__ = 'relationship'
-
- @declared_attr
- def relationship_template(cls):
- return cls.many_to_one_relationship('relationship_template')
-
- @declared_attr
- def requirement_template(cls):
- return cls.many_to_one_relationship('requirement_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- @declared_attr
- def target_capability(cls):
- return cls.one_to_one_relationship('capability')
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- # region orchestration
-
- source_position = Column(Integer) # ???
- target_position = Column(Integer) # ???
-
- # endregion
-
- # region foreign keys
-
- __private_fields__ = ['type_fk',
- 'source_node_fk',
- 'target_node_fk',
- 'target_capability_fk',
- 'requirement_template_fk',
- 'relationship_template_fk']
-
- # Relationship many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type', nullable=True)
-
- # Node one-to-many to Relationship
- @declared_attr
- def source_node_fk(cls):
- return cls.foreign_key('node')
-
- # Node one-to-many to Relationship
- @declared_attr
- def target_node_fk(cls):
- return cls.foreign_key('node')
-
- # Relationship one-to-one to Capability
- @declared_attr
- def target_capability_fk(cls):
- return cls.foreign_key('capability', nullable=True)
-
- # Relationship many-to-one to RequirementTemplate
- @declared_attr
- def requirement_template_fk(cls):
- return cls.foreign_key('requirement_template', nullable=True)
-
- # Relationship many-to-one to RelationshipTemplate
- @declared_attr
- def relationship_template_fk(cls):
- return cls.foreign_key('relationship_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('target_node_id', self.target_node.name),
- ('type_name', self.type_name),
- ('template_name', self.template_name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
- def dump(self, context):
- 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(context, self.properties, 'Properties')
- utils.dump_interfaces(context, self.interfaces, 'Interfaces')
-
-
-class CapabilityBase(InstanceModelMixin):
- """
- A capability of a :class:`Node`.
-
- Usually an instance of a :class:`CapabilityTemplate`.
-
- :ivar name: Name (unique for the node)
- :vartype name: basestring
- :ivar capability_template: Template from which this capability was instantiated (optional)
- :vartype capability_template: :class:`capabilityTemplate`
- :ivar type: Capability type
- :vartype type: :class:`Type`
- :ivar min_occurrences: Minimum number of requirement matches required
- :vartype min_occurrences: int
- :ivar max_occurrences: Maximum number of requirement matches allowed
- :vartype min_occurrences: int
- :ivar occurrences: Actual number of requirement matches
- :vartype occurrences: int
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- :ivar relationship: Available when we are the target of a relationship
- :vartype relationship: :class:`Relationship`
- :ivar substitution_mapping: Our contribution to service substitution
- :vartype substitution_mapping: :class:`SubstitutionMapping`
- """
-
- __tablename__ = 'capability'
-
- @declared_attr
- def capability_template(cls):
- return cls.many_to_one_relationship('capability_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- min_occurrences = Column(Integer, default=None)
- max_occurrences = Column(Integer, default=None)
- occurrences = Column(Integer, default=0)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['capability_fk',
- 'node_fk',
- 'capability_template_fk']
-
- # Capability many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Capability
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Capability many-to-one to CapabilityTemplate
- @declared_attr
- def capability_template_fk(cls):
- return cls.foreign_key('capability_template', nullable=True)
-
- # endregion
-
- @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, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- 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(context, self.properties, 'Properties')
-
-
-class InterfaceBase(InstanceModelMixin):
- """
- A typed set of :class:`Operation`.
-
- Usually an instance of :class:`InterfaceTemplate`.
-
- :ivar name: Name (unique for the node, group, or relationship)
- :vartype name: basestring
- :ivar interface_template: Template from which this interface was instantiated (optional)
- :vartype interface_template: :class:`InterfaceTemplate`
- :ivar type: Interface type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar inputs: Parameters that can be used by all operations in the interface
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar operations: Operations
- :vartype operations: {basestring: :class:`Operation`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- :ivar group: Containing group
- :vartype group: :class:`Group`
- :ivar relationship: Containing relationship
- :vartype relationship: :class:`Relationship`
- """
-
- __tablename__ = 'interface'
-
- @declared_attr
- def interface_template(cls):
- return cls.many_to_one_relationship('interface_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- @declared_attr
- def operations(cls):
- return cls.one_to_many_relationship('operation', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'node_fk',
- 'group_fk',
- 'relationship_fk',
- 'interface_template_fk']
-
- # Interface many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Interface
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node', nullable=True)
-
- # Group one-to-many to Interface
- @declared_attr
- def group_fk(cls):
- return cls.foreign_key('group', nullable=True)
-
- # Relationship one-to-many to Interface
- @declared_attr
- def relationship_fk(cls):
- return cls.foreign_key('relationship', nullable=True)
-
- # Interface many-to-one to InterfaceTemplate
- @declared_attr
- def interface_template_fk(cls):
- return cls.foreign_key('interface_template', nullable=True)
-
- # endregion
-
- @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, context):
- utils.validate_dict_values(context, self.inputs)
- utils.validate_dict_values(context, self.operations)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.operations, report_issues)
-
- def dump(self, context):
- 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(context, self.inputs, 'Inputs')
- utils.dump_dict_values(context, self.operations, 'Operations')
-
-
-class OperationBase(InstanceModelMixin):
- """
- An operation in a :class:`Interface`.
-
- Might be an instance of :class:`OperationTemplate`.
-
- :ivar name: Name (unique for the interface or service)
- :vartype name: basestring
- :ivar operation_template: Template from which this operation was instantiated (optional)
- :vartype operation_template: :class:`OperationTemplate`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar plugin: Associated plugin
- :vartype plugin: :class:`Plugin`
- :ivar implementation: Implementation string (interpreted by the plugin)
- :vartype implementation: basestring
- :ivar dependencies: Dependency strings (interpreted by the plugin)
- :vartype dependencies: [basestring]
- :ivar inputs: Parameters that can be used by this operation
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar executor: Executor name
- :vartype executor: basestring
- :ivar max_retries: Maximum number of retries allowed in case of failure
- :vartype max_retries: int
- :ivar retry_interval: Interval between retries (in seconds)
- :vartype retry_interval: int
-
- :ivar interface: Containing interface
- :vartype interface: :class:`Interface`
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'operation'
-
- @declared_attr
- def operation_template(cls):
- return cls.many_to_one_relationship('operation_template')
-
- description = Column(Text)
-
- @declared_attr
- def plugin(cls):
- return cls.one_to_one_relationship('plugin')
-
- implementation = Column(Text)
- dependencies = Column(modeling_types.StrictList(item_cls=basestring))
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- executor = Column(Text)
- max_retries = Column(Integer)
- retry_interval = Column(Integer)
-
- # region foreign_keys
-
- __private_fields__ = ['service_fk',
- 'interface_fk',
- 'plugin_fk',
- 'operation_template_fk']
-
- # Service one-to-many to Operation
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service', nullable=True)
-
- # Interface one-to-many to Operation
- @declared_attr
- def interface_fk(cls):
- return cls.foreign_key('interface', nullable=True)
-
- # Operation one-to-one to Plugin
- @declared_attr
- def plugin_fk(cls):
- return cls.foreign_key('plugin', nullable=True)
-
- # Operation many-to-one to OperationTemplate
- @declared_attr
- def operation_template_fk(cls):
- return cls.foreign_key('operation_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('implementation', self.implementation),
- ('dependencies', self.dependencies),
- ('executor', self.executor),
- ('max_retries', self.max_retries),
- ('retry_interval', self.retry_interval),
- ('inputs', formatting.as_raw_dict(self.inputs))))
-
- def validate(self, context):
- # TODO must be associated with interface or service
- utils.validate_dict_values(context, self.inputs)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
-
- def dump(self, context):
- 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))))
- if self.executor is not None:
- console.puts('Executor: {0}'.format(context.style.literal(self.executor)))
- if self.max_retries is not None:
- console.puts('Max retries: {0}'.format(context.style.literal(self.max_retries)))
- if self.retry_interval is not None:
- console.puts('Retry interval: {0}'.format(
- context.style.literal(self.retry_interval)))
- utils.dump_dict_values(context, self.inputs, 'Inputs')
-
-
-class ArtifactBase(InstanceModelMixin):
- """
- A file associated with a :class:`Node`.
-
- Usually an instance of :class:`ArtifactTemplate`.
-
- :ivar name: Name (unique for the node)
- :vartype name: basestring
- :ivar artifact_template: Template from which this artifact was instantiated (optional)
- :vartype artifact_template: :class:`ArtifactTemplate`
- :ivar type: Artifact type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar source_path: Source path (CSAR or repository)
- :vartype source_path: basestring
- :ivar target_path: Path at destination machine
- :vartype target_path: basestring
- :ivar repository_url: Repository URL
- :vartype repository_path: basestring
- :ivar repository_credential: Credentials for accessing the repository
- :vartype repository_credential: {basestring: basestring}
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- """
-
- __tablename__ = 'artifact'
-
- @declared_attr
- def artifact_template(cls):
- return cls.many_to_one_relationship('artifact_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
- source_path = Column(Text)
- target_path = Column(Text)
- repository_url = Column(Text)
- repository_credential = Column(modeling_types.StrictDict(basestring, basestring))
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'node_fk',
- 'artifact_template_fk']
-
- # Artifact many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Artifact
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Artifact many-to-one to ArtifactTemplate
- @declared_attr
- def artifact_template_fk(cls):
- return cls.foreign_key('artifact_template', nullable=True)
-
- # endregion
-
- @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, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- 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(context, self.properties, 'Properties')
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_changes.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py
new file mode 100644
index 0000000..b83a376
--- /dev/null
+++ b/aria/modeling/service_changes.py
@@ -0,0 +1,219 @@
+# 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.
+
+"""
+classes:
+ * ServiceUpdate - service update implementation model.
+ * ServiceUpdateStep - service update step implementation model.
+ * ServiceModification - service modification implementation model.
+"""
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+from collections import namedtuple
+
+from sqlalchemy import (
+ Column,
+ Text,
+ DateTime,
+ Enum,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from .types import (List, Dict)
+from .mixins import ModelMixin
+
+__all__ = (
+ 'ServiceUpdateBase',
+ 'ServiceUpdateStepBase',
+ 'ServiceModificationBase'
+)
+
+
+class ServiceUpdateBase(ModelMixin):
+ """
+ Deployment update model representation.
+ """
+
+ steps = None
+
+ __tablename__ = 'service_update'
+
+ _private_fields = ['execution_fk',
+ 'service_fk']
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ service_plan = Column(Dict, nullable=False)
+ service_update_nodes = Column(Dict)
+ service_update_service = Column(Dict)
+ service_update_node_templates = Column(List)
+ modified_entity_ids = Column(Dict)
+ state = Column(Text)
+
+ @declared_attr
+ def execution(cls):
+ return cls._create_many_to_one_relationship('execution')
+
+ @declared_attr
+ def execution_name(cls):
+ return association_proxy('execution', cls.name_column_name())
+
+ @declared_attr
+ def service(cls):
+ return cls._create_many_to_one_relationship('service',
+ backreference='updates')
+
+ @declared_attr
+ def service_name(cls):
+ return association_proxy('service', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_fk',
+ 'execution_fk']
+
+ @declared_attr
+ def execution_fk(cls):
+ return cls._create_foreign_key('execution', nullable=True)
+
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # endregion
+
+ def to_dict(self, suppress_error=False, **kwargs):
+ dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error) #pylint: disable=no-member
+ # Taking care of the fact the DeploymentSteps are _BaseModels
+ dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
+ return dep_update_dict
+
+
+class ServiceUpdateStepBase(ModelMixin):
+ """
+ Deployment update step model representation.
+ """
+
+ __tablename__ = 'service_update_step'
+
+ _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
+ ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
+
+ _entity_types = namedtuple(
+ 'ENTITY_TYPES',
+ 'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, PLUGIN')
+ ENTITY_TYPES = _entity_types(
+ NODE='node',
+ RELATIONSHIP='relationship',
+ PROPERTY='property',
+ OPERATION='operation',
+ WORKFLOW='workflow',
+ OUTPUT='output',
+ DESCRIPTION='description',
+ GROUP='group',
+ PLUGIN='plugin'
+ )
+
+ action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False)
+ entity_id = Column(Text, nullable=False)
+ entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
+
+ @declared_attr
+ def service_update(cls):
+ return cls._create_many_to_one_relationship('service_update',
+ backreference='steps')
+
+ @declared_attr
+ def service_update_name(cls):
+ return association_proxy('service_update', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_update_fk']
+
+ @declared_attr
+ def service_update_fk(cls):
+ return cls._create_foreign_key('service_update')
+
+ # endregion
+
+ def __hash__(self):
+ return hash((getattr(self, self.id_column_name()), self.entity_id))
+
+ def __lt__(self, other):
+ """
+ the order is 'remove' < 'modify' < 'add'
+ :param other:
+ :return:
+ """
+ if not isinstance(other, self.__class__):
+ return not self >= other
+
+ if self.action != other.action:
+ if self.action == 'remove':
+ return_value = True
+ elif self.action == 'add':
+ return_value = False
+ else:
+ return_value = other.action == 'add'
+ return return_value
+
+ if self.action == 'add':
+ return self.entity_type == 'node' and other.entity_type == 'relationship'
+ if self.action == 'remove':
+ return self.entity_type == 'relationship' and other.entity_type == 'node'
+ return False
+
+
+class ServiceModificationBase(ModelMixin):
+ """
+ Deployment modification model representation.
+ """
+
+ __tablename__ = 'service_modification'
+
+ STARTED = 'started'
+ FINISHED = 'finished'
+ ROLLEDBACK = 'rolledback'
+
+ STATES = [STARTED, FINISHED, ROLLEDBACK]
+ END_STATES = [FINISHED, ROLLEDBACK]
+
+ context = Column(Dict)
+ created_at = Column(DateTime, nullable=False, index=True)
+ ended_at = Column(DateTime, index=True)
+ modified_node_templates = Column(Dict)
+ nodes = Column(Dict)
+ status = Column(Enum(*STATES, name='service_modification_status'))
+
+ @declared_attr
+ def service(cls):
+ return cls._create_many_to_one_relationship('service',
+ backreference='modifications')
+
+ @declared_attr
+ def service_name(cls):
+ return association_proxy('service', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_fk']
+
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
new file mode 100644
index 0000000..b3535a6
--- /dev/null
+++ b/aria/modeling/service_common.py
@@ -0,0 +1,270 @@
+# 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.
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+import cPickle as pickle
+import logging
+
+from sqlalchemy import (
+ Column,
+ Text,
+ Binary,
+)
+from sqlalchemy.ext.declarative import declared_attr
+
+from ..storage import exceptions
+from ..utils import collections, formatting, console
+from .mixins import InstanceModelMixin, TemplateModelMixin
+from .types import List
+from . import utils
+
+
+class ParameterBase(TemplateModelMixin):
+ """
+ Represents a typed value.
+
+ This model is used by both service template and service instance elements.
+
+ :ivar name: Name
+ :ivar type_name: Type name
+ :ivar value: Value
+ :ivar description: Description
+ """
+
+ __tablename__ = 'parameter'
+
+ name = Column(Text)
+ type_name = Column(Text)
+
+ # Check: value type
+ _value = Column(Binary, name='value')
+ description = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('type_name', self.type_name),
+ ('value', self.value),
+ ('description', self.description)))
+
+ @property
+ def value(self):
+ if self._value is None:
+ return None
+ try:
+ return pickle.loads(self._value)
+ except BaseException:
+ raise exceptions.StorageError('bad format for parameter of type "{0}": {1}'.format(
+ self.type_name, self._value))
+
+ @value.setter
+ def value(self, value):
+ if value is None:
+ self._value = None
+ else:
+ try:
+ self._value = pickle.dumps(value)
+ except (pickle.PicklingError, TypeError):
+ logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
+ .format(self.type_name, value))
+ self._value = pickle.dumps(str(value))
+
+ def instantiate(self, context, container):
+ from . import models
+ return models.Parameter(name=self.name,
+ type_name=self.type_name,
+ _value=self._value,
+ description=self.description)
+
+ def coerce_values(self, context, container, report_issues):
+ if self.value is not None:
+ self.value = utils.coerce_value(context, container, self.value,
+ report_issues)
+
+ def dump(self, context):
+ if self.type_name is not None:
+ console.puts('{0}: {1} ({2})'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value),
+ context.style.type(self.type_name)))
+ else:
+ console.puts('{0}: {1}'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value)))
+ if self.description:
+ console.puts(context.style.meta(self.description))
+
+
+class TypeBase(InstanceModelMixin):
+ """
+ Represents a type and its children.
+ """
+
+ __tablename__ = 'type'
+
+ variant = Column(Text, nullable=False)
+ description = Column(Text)
+ _role = Column(Text, name='role')
+
+ @declared_attr
+ def parent(cls):
+ return cls._create_relationship_to_self('parent_type_fk')
+
+ @declared_attr
+ def children(cls):
+ return cls._create_one_to_many_relationship_to_self('parent_type_fk')
+
+ # region foreign keys
+
+ __private_fields__ = ['parent_type_fk']
+
+ # Type one-to-many to Type
+ @declared_attr
+ def parent_type_fk(cls):
+ return cls._create_foreign_key('type', nullable=True)
+
+ # endregion
+
+ @property
+ def role(self):
+ def get_role(the_type):
+ if the_type is None:
+ return None
+ elif the_type._role is None:
+ return get_role(the_type.parent)
+ return the_type._role
+
+ return get_role(self)
+
+ @role.setter
+ def role(self, value):
+ self._role = value
+
+ def is_descendant(self, base_name, name):
+ base = self.get_descendant(base_name)
+ if base is not None:
+ if base.get_descendant(name) is not None:
+ return True
+ return False
+
+ def get_descendant(self, name):
+ if self.name == name:
+ return self
+ for child in self.children:
+ found = child.get_descendant(name)
+ if found is not None:
+ return found
+ return None
+
+ def iter_descendants(self):
+ for child in self.children:
+ yield child
+ for descendant in child.iter_descendants():
+ yield descendant
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('role', self.role)))
+
+ @property
+ def as_raw_all(self):
+ types = []
+ self._append_raw_children(types)
+ return types
+
+ def dump(self, context):
+ if self.name:
+ console.puts(context.style.type(self.name))
+ with context.style.indent:
+ for child in self.children:
+ child.dump(context)
+
+ def _append_raw_children(self, types):
+ for child in self.children:
+ raw_child = formatting.as_raw(child)
+ raw_child['parent'] = self.name
+ types.append(raw_child)
+ child._append_raw_children(types)
+
+
+class MetadataBase(TemplateModelMixin):
+ """
+ Custom values associated with the service.
+
+ This model is used by both service template and service instance elements.
+
+ :ivar name: Name
+ :ivar value: Value
+ """
+
+ __tablename__ = 'metadata'
+
+ value = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('value', self.value)))
+
+ def instantiate(self, context, container):
+ from . import models
+ return models.Metadata(name=self.name,
+ value=self.value)
+
+ def dump(self, context):
+ console.puts('{0}: {1}'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value)))
+
+
+class PluginSpecificationBase(InstanceModelMixin):
+ """
+ Plugin specification model representation.
+ """
+
+ __tablename__ = 'plugin_specification'
+
+ archive_name = Column(Text, nullable=False, index=True)
+ distribution = Column(Text)
+ distribution_release = Column(Text)
+ distribution_version = Column(Text)
+ package_name = Column(Text, nullable=False, index=True)
+ package_source = Column(Text)
+ package_version = Column(Text)
+ supported_platform = Column(Text)
+ supported_py_versions = Column(List)
+
+ # region foreign keys
+
+ __private_fields__ = ['service_template_fk']
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls._create_foreign_key('service_template', nullable=True)
+
+ # endregion
+
+ def find_plugin(self, plugins):
+ # TODO: this should check versions/distribution and other specification
+ for plugin in plugins:
+ if plugin.name == self.name:
+ return plugin
+ return None
[8/8] incubator-ariatosca git commit: Unified_coerce
Posted by mx...@apache.org.
Unified_coerce
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/367b18bc
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/367b18bc
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/367b18bc
Branch: refs/heads/Unified_coerce
Commit: 367b18bc0cc719c13fb19f3c2f486bdd8e4e1785
Parents: dd5bfa9
Author: max-orlov <ma...@gigaspaces.com>
Authored: Wed Mar 8 17:02:34 2017 +0200
Committer: max-orlov <ma...@gigaspaces.com>
Committed: Sun Mar 12 11:01:27 2017 +0200
----------------------------------------------------------------------
aria/modeling/mixins.py | 13 ++++++++-
aria/modeling/service_instance.py | 45 -------------------------------
aria/modeling/service_template.py | 49 ----------------------------------
aria/modeling/utils.py | 13 ---------
4 files changed, 12 insertions(+), 108 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/367b18bc/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index 04497b5..c050fa1 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -388,7 +388,18 @@ class InstanceModelMixin(ModelMixin):
pass
def coerce_values(self, context, container, report_issues):
- pass
+ if container is None:
+ return
+ for value in vars(container).values():
+ if value is not None and isinstance(value, ModelMixin):
+ if isinstance(value, dict):
+ value = value.values()
+
+ if isinstance(value, list):
+ for item in value:
+ item.coerce_values(context, container, report_issues)
+ else:
+ utils.coerce_value(context, container, value, report_issues)
def dump(self, context):
pass
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/367b18bc/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
index ba18f73..dd579b3 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -234,17 +234,6 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
utils.validate_dict_values(context, self.outputs)
utils.validate_dict_values(context, self.workflows)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.meta_data, report_issues)
- utils.coerce_list_values(context, container, self.nodes, report_issues)
- utils.coerce_list_values(context, container, self.groups, report_issues)
- utils.coerce_list_values(context, container, self.policies, report_issues)
- if self.substitution is not None:
- self.substitution.coerce_values(context, container, report_issues)
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.outputs, report_issues)
- utils.coerce_dict_values(context, container, self.workflows, report_issues)
-
def dump(self, context):
if self.description is not None:
console.puts(context.style.meta(self.description))
@@ -553,13 +542,6 @@ class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
utils.validate_dict_values(context, self.capabilities)
utils.validate_list_values(context, self.outbound_relationships)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interfaces, report_issues)
- utils.coerce_dict_values(context, self, self.artifacts, report_issues)
- utils.coerce_dict_values(context, self, self.capabilities, report_issues)
- utils.coerce_list_values(context, self, self.outbound_relationships, report_issues)
-
def dump(self, context):
console.puts('Node: {0}'.format(context.style.node(self.name)))
with context.style.indent:
@@ -656,10 +638,6 @@ class GroupBase(InstanceModelMixin):
utils.validate_dict_values(context, self.properties)
utils.validate_dict_values(context, self.interfaces)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
def dump(self, context):
console.puts('Group: {0}'.format(context.style.node(self.name)))
with context.style.indent:
@@ -754,9 +732,6 @@ class PolicyBase(InstanceModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
def dump(self, context):
console.puts('Policy: {0}'.format(context.style.node(self.name)))
with context.style.indent:
@@ -831,9 +806,6 @@ class SubstitutionBase(InstanceModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.mappings)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.mappings, report_issues)
-
def dump(self, context):
console.puts('Substitution:')
with context.style.indent:
@@ -1050,10 +1022,6 @@ class RelationshipBase(InstanceModelMixin):
utils.validate_dict_values(context, self.properties)
utils.validate_dict_values(context, self.interfaces)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
def dump(self, context):
if self.name:
console.puts('{0} ->'.format(context.style.node(self.name)))
@@ -1167,9 +1135,6 @@ class CapabilityBase(InstanceModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
with context.style.indent:
@@ -1279,10 +1244,6 @@ class InterfaceBase(InstanceModelMixin):
utils.validate_dict_values(context, self.inputs)
utils.validate_dict_values(context, self.operations)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.operations, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
@@ -1395,9 +1356,6 @@ class OperationBase(InstanceModelMixin):
# TODO must be associated with interface or service
utils.validate_dict_values(context, self.inputs)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
@@ -1508,9 +1466,6 @@ class ArtifactBase(InstanceModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/367b18bc/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index c9a02eb..9debc1c 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -328,17 +328,6 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
if self.artifact_types is not None:
self.artifact_types.validate(context)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.meta_data, report_issues)
- utils.coerce_list_values(context, container, self.node_templates, report_issues)
- utils.coerce_list_values(context, container, self.group_templates, report_issues)
- utils.coerce_list_values(context, container, self.policy_templates, report_issues)
- if self.substitution_template is not None:
- self.substitution_template.coerce_values(context, container, report_issues)
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.outputs, report_issues)
- utils.coerce_dict_values(context, container, self.workflow_templates, report_issues)
-
def dump(self, context):
if self.description is not None:
console.puts(context.style.meta(self.description))
@@ -526,13 +515,6 @@ class NodeTemplateBase(TemplateModelMixin):
utils.validate_dict_values(context, self.capability_templates)
utils.validate_list_values(context, self.requirement_templates)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interface_templates, report_issues)
- utils.coerce_dict_values(context, self, self.artifact_templates, report_issues)
- utils.coerce_dict_values(context, self, self.capability_templates, report_issues)
- utils.coerce_list_values(context, self, self.requirement_templates, report_issues)
-
def dump(self, context):
console.puts('Node template: {0}'.format(context.style.node(self.name)))
if self.description:
@@ -643,10 +625,6 @@ class GroupTemplateBase(TemplateModelMixin):
utils.validate_dict_values(context, self.properties)
utils.validate_dict_values(context, self.interface_templates)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interface_templates, report_issues)
-
def dump(self, context):
console.puts('Group template: {0}'.format(context.style.node(self.name)))
if self.description:
@@ -748,9 +726,6 @@ class PolicyTemplateBase(TemplateModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
-
def dump(self, context):
console.puts('Policy template: {0}'.format(context.style.node(self.name)))
if self.description:
@@ -819,9 +794,6 @@ class SubstitutionTemplateBase(TemplateModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.mappings)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.mappings, report_issues)
-
def dump(self, context):
console.puts('Substitution template:')
with context.style.indent:
@@ -1089,10 +1061,6 @@ class RequirementTemplateBase(TemplateModelMixin):
if self.relationship_template:
self.relationship_template.validate(context)
- def coerce_values(self, context, container, report_issues):
- if self.relationship_template is not None:
- self.relationship_template.coerce_values(context, container, report_issues)
-
def dump(self, context):
if self.name:
console.puts(context.style.node(self.name))
@@ -1200,10 +1168,6 @@ class RelationshipTemplateBase(TemplateModelMixin):
utils.validate_dict_values(context, self.properties)
utils.validate_dict_values(context, self.interface_templates)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interface_templates, report_issues)
-
def dump(self, context):
if self.type is not None:
console.puts('Relationship type: {0}'.format(context.style.type(self.type.name)))
@@ -1330,9 +1294,6 @@ class CapabilityTemplateBase(TemplateModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
@@ -1446,10 +1407,6 @@ class InterfaceTemplateBase(TemplateModelMixin):
utils.validate_dict_values(context, self.inputs)
utils.validate_dict_values(context, self.operation_templates)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.operation_templates, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
@@ -1565,9 +1522,6 @@ class OperationTemplateBase(TemplateModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.inputs)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
@@ -1678,9 +1632,6 @@ class ArtifactTemplateBase(TemplateModelMixin):
def validate(self, context):
utils.validate_dict_values(context, self.properties)
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
def dump(self, context):
console.puts(context.style.node(self.name))
if self.description:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/367b18bc/aria/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index a071d69..857e81e 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -56,19 +56,6 @@ def validate_list_values(context, the_list):
value.validate(context)
-def coerce_dict_values(context, container, the_dict, report_issues=False):
- if not the_dict:
- return
- coerce_list_values(context, container, the_dict.itervalues(), report_issues)
-
-
-def coerce_list_values(context, container, the_list, report_issues=False):
- if not the_list:
- return
- for value in the_list:
- value.coerce_values(context, container, report_issues)
-
-
def instantiate_dict(context, container, the_dict, from_dict):
if not from_dict:
return
[7/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
Separate plugin specification form plugin; move dry support to CLI; various renames and refactorings
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/dd5bfa93
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/dd5bfa93
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/dd5bfa93
Branch: refs/heads/Unified_coerce
Commit: dd5bfa930dede8a81ce382e65cad8fe924d4e99d
Parents: aa01cd4
Author: Tal Liron <ta...@gmail.com>
Authored: Fri Mar 10 16:57:17 2017 -0600
Committer: Tal Liron <ta...@gmail.com>
Committed: Fri Mar 10 16:57:17 2017 -0600
----------------------------------------------------------------------
aria/cli/args_parser.py | 5 +
aria/cli/commands.py | 4 +
aria/cli/dry.py | 75 +
aria/modeling/__init__.py | 18 +-
aria/modeling/bases.py | 405 -----
aria/modeling/misc.py | 234 ---
aria/modeling/mixins.py | 405 +++++
aria/modeling/models.py | 91 +-
aria/modeling/orchestration.py | 235 +--
aria/modeling/service.py | 1529 ------------------
aria/modeling/service_changes.py | 219 +++
aria/modeling/service_common.py | 270 ++++
aria/modeling/service_instance.py | 1529 ++++++++++++++++++
aria/modeling/service_template.py | 260 +--
aria/orchestrator/workflows/api/task.py | 44 +-
.../workflows/builtin/execute_operation.py | 3 +-
aria/orchestrator/workflows/builtin/utils.py | 13 +-
.../orchestrator/workflows/builtin/workflows.py | 60 +-
aria/orchestrator/workflows/core/task.py | 13 +-
aria/orchestrator/workflows/dry.py | 53 -
.../simple_v1_0/modeling/__init__.py | 47 +-
tests/end2end/test_orchestrator.py | 3 +
tests/mock/models.py | 16 +
tests/modeling/__init__.py | 34 +
tests/modeling/test_mixins.py | 219 +++
tests/modeling/test_model_storage.py | 102 ++
tests/modeling/test_models.py | 835 ++++++++++
tests/orchestrator/context/test_operation.py | 15 +-
tests/orchestrator/context/test_serialize.py | 7 +-
tests/orchestrator/context/test_toolbelt.py | 2 +-
tests/orchestrator/workflows/api/test_task.py | 33 +-
.../workflows/builtin/test_execute_operation.py | 2 +-
tests/orchestrator/workflows/core/test_task.py | 15 +-
.../tosca-simple-1.0/node-cellar/workflows.py | 3 +-
tests/storage/__init__.py | 22 +-
tests/storage/test_instrumentation.py | 8 +-
tests/storage/test_model_storage.py | 102 --
tests/storage/test_models.py | 833 ----------
tests/storage/test_structures.py | 220 ---
39 files changed, 4069 insertions(+), 3914 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/args_parser.py
----------------------------------------------------------------------
diff --git a/aria/cli/args_parser.py b/aria/cli/args_parser.py
index e092ee6..1d18145 100644
--- a/aria/cli/args_parser.py
+++ b/aria/cli/args_parser.py
@@ -137,6 +137,11 @@ def add_workflow_parser(workflow):
'-w', '--workflow',
default='install',
help='The workflow name')
+ workflow.add_flag_argument(
+ 'dry',
+ default=True,
+ help_true='dry run',
+ help_false='wet run')
@sub_parser_decorator(
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index 52d4f14..a9079c5 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -50,6 +50,7 @@ from ..utils.collections import OrderedDict
from ..orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS
from ..orchestrator.runner import Runner
from ..orchestrator.workflows.builtin import BUILTIN_WORKFLOWS
+from .dry import convert_to_dry
from .exceptions import (
AriaCliFormatInputsError,
@@ -212,6 +213,7 @@ class WorkflowCommand(BaseCommand):
context = self._parse(args_namespace.uri)
workflow_fn, inputs = self._get_workflow(context, args_namespace.workflow)
+ self._dry = args_namespace.dry
self._run(context, args_namespace.workflow, workflow_fn, inputs)
def _parse(self, uri):
@@ -265,6 +267,8 @@ class WorkflowCommand(BaseCommand):
def _run(self, context, workflow_name, workflow_fn, inputs):
# Storage
def _initialize_storage(model_storage):
+ if self._dry:
+ convert_to_dry(context.modeling.instance)
context.modeling.store(model_storage)
# Create runner
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/cli/dry.py
----------------------------------------------------------------------
diff --git a/aria/cli/dry.py b/aria/cli/dry.py
new file mode 100644
index 0000000..98b7217
--- /dev/null
+++ b/aria/cli/dry.py
@@ -0,0 +1,75 @@
+# 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 threading import RLock
+
+from ..modeling import models
+from ..orchestrator.decorators import operation
+from ..utils.collections import OrderedDict
+from ..utils.console import puts, Colored
+from ..utils.formatting import safe_repr
+
+
+_TERMINAL_LOCK = RLock()
+
+
+def convert_to_dry(service):
+ for workflow in service.workflows:
+ convert_operation_to_dry(workflow)
+
+ for node in service.nodes:
+ for interface in node.interfaces.itervalues():
+ for oper in interface.operations.itervalues():
+ convert_operation_to_dry(oper)
+ for relationship in node.outbound_relationships:
+ for interface in relationship.interfaces.itervalues():
+ for oper in interface.operations.itervalues():
+ convert_operation_to_dry(oper)
+
+
+def convert_operation_to_dry(oper):
+ plugin = oper.plugin_specification.name \
+ if oper.plugin_specification is not None else None
+ if oper.inputs is None:
+ oper.inputs = OrderedDict()
+ oper.inputs['_implementation'] = models.Parameter(name='_implementation',
+ type_name='string',
+ value=oper.implementation)
+ oper.inputs['_plugin'] = models.Parameter(name='_plugin',
+ type_name='string',
+ value=plugin)
+ oper.implementation = '{0}.{1}'.format(__name__, 'dry_operation')
+ oper.plugin_specification = None
+
+
+@operation
+def dry_operation(ctx, _plugin, _implementation, **kwargs):
+ with _TERMINAL_LOCK:
+ print ctx.name
+ if hasattr(ctx, 'relationship'):
+ puts('> Relationship: {0} -> {1}'.format(
+ Colored.red(ctx.relationship.source_node.name),
+ Colored.red(ctx.relationship.target_node.name)))
+ else:
+ puts('> Node: {0}'.format(Colored.red(ctx.node.name)))
+ puts(' Operation: {0}'.format(Colored.green(ctx.name)))
+ _dump_implementation(_plugin, _implementation)
+
+
+def _dump_implementation(plugin, implementation):
+ if plugin:
+ puts(' Plugin: {0}'.format(Colored.magenta(plugin, bold=True)))
+ if implementation:
+ puts(' Implementation: {0}'.format(Colored.magenta(safe_repr(implementation))))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/modeling/__init__.py b/aria/modeling/__init__.py
index 67b5703..4dfc39d 100644
--- a/aria/modeling/__init__.py
+++ b/aria/modeling/__init__.py
@@ -16,26 +16,32 @@
from collections import namedtuple
from . import (
- bases,
+ mixins,
types,
models,
service_template as _service_template_bases,
- service as _service_bases,
- orchestration as _orchestration_bases,
+ service_instance as _service_instance_bases,
+ service_changes as _service_changes_bases,
+ service_common as _service_common_bases,
+ orchestration as _orchestration_bases
)
_ModelBasesCls = namedtuple('ModelBase', 'service_template,'
- 'service,'
+ 'service_instance,'
+ 'service_changes,'
+ 'service_common,'
'orchestration')
model_bases = _ModelBasesCls(service_template=_service_template_bases,
- service=_service_bases,
+ service_instance=_service_instance_bases,
+ service_changes=_service_changes_bases,
+ service_common=_service_common_bases,
orchestration=_orchestration_bases)
__all__ = (
- 'bases',
+ 'mixins',
'types',
'models',
'model_bases',
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/bases.py
----------------------------------------------------------------------
diff --git a/aria/modeling/bases.py b/aria/modeling/bases.py
deleted file mode 100644
index efcb968..0000000
--- a/aria/modeling/bases.py
+++ /dev/null
@@ -1,405 +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's storage.structures module
-Path: aria.storage.structures
-
-models module holds ARIA's models.
-
-classes:
- * ModelMixin - abstract model implementation.
- * ModelIDMixin - abstract model implementation with IDs.
-"""
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.orm.collections import attribute_mapped_collection
-from sqlalchemy.ext import associationproxy
-from sqlalchemy import (
- Column,
- ForeignKey,
- Integer,
- Text,
- Table,
-)
-
-from . import utils
-from ..utils import formatting
-
-
-class ModelMixin(object):
-
- @utils.classproperty
- def __modelname__(cls): # pylint: disable=no-self-argument
- return getattr(cls, '__mapiname__', cls.__tablename__)
-
- @classmethod
- def id_column_name(cls):
- raise NotImplementedError
-
- @classmethod
- def name_column_name(cls):
- raise NotImplementedError
-
- @classmethod
- def foreign_key(cls, parent_table, nullable=False):
- """
- Return a ForeignKey object.
-
- :param parent_table: Parent table name
- :param nullable: Should the column be allowed to remain empty
- """
- return Column(Integer,
- ForeignKey('{table}.id'.format(table=parent_table),
- ondelete='CASCADE'),
- nullable=nullable)
-
- @classmethod
- def relationship_to_self(cls,
- column_name,
- relationship_kwargs=None):
- relationship_kwargs = relationship_kwargs or {}
-
- remote_side = '{cls}.{remote_column}'.format(
- cls=cls.__name__,
- remote_column=cls.id_column_name()
- )
-
- primaryjoin = '{remote_side} == {cls}.{column}'.format(
- remote_side=remote_side,
- cls=cls.__name__,
- column=column_name
- )
-
- return relationship(
- cls._get_cls_by_tablename(cls.__tablename__).__name__,
- primaryjoin=primaryjoin,
- remote_side=remote_side,
- post_update=True,
- **relationship_kwargs
- )
-
- @classmethod
- def one_to_many_relationship_to_self(cls,
- key,
- dict_key=None,
- relationship_kwargs=None):
- relationship_kwargs = relationship_kwargs or {}
-
- relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
- cls=cls.__name__,
- remote_column=key
- ))
-
- return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
- backreference='', dict_key=dict_key)
-
- @classmethod
- def one_to_one_relationship(cls,
- other_table,
- key=None,
- foreign_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
- backref_kwargs = backref_kwargs or {}
- backref_kwargs.setdefault('uselist', False)
-
- return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, foreign_key=foreign_key)
-
- @classmethod
- def one_to_many_relationship(cls,
- child_table,
- key=None,
- foreign_key=None,
- dict_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
- backref_kwargs = backref_kwargs or {}
- backref_kwargs.setdefault('uselist', False)
-
- return cls._create_relationship(child_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, foreign_key=foreign_key,
- dict_key=dict_key)
-
- @classmethod
- def many_to_one_relationship(cls,
- parent_table,
- key=None,
- foreign_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
- """
- Return a one-to-many SQL relationship object
- Meant to be used from inside the *child* object
-
- :param parent_table: Name of the parent table
- :param foreign_key: The column of the foreign key (from the child table)
- :param backreference: The name to give to the reference to the child (on the parent table)
- """
-
- if backreference is None:
- backreference = formatting.pluralize(cls.__tablename__)
-
- backref_kwargs = backref_kwargs or {}
- backref_kwargs.setdefault('uselist', True)
- backref_kwargs.setdefault('lazy', 'dynamic')
- # The following line make sure that when the *parent* is deleted, all its connected children
- # are deleted as well
- backref_kwargs.setdefault('cascade', 'all')
-
- return cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, foreign_key=foreign_key)
-
- @classmethod
- def many_to_many_relationship(cls,
- other_table,
- table_prefix=None,
- key=None,
- dict_key=None,
- backreference=None,
- backref_kwargs=None,
- relationship_kwargs=None):
- """
- Return a many-to-many SQL relationship object
-
- Notes:
-
- 1. The backreference name is the current table's table name
- 2. This method creates a new helper table in the DB
-
- :param cls: The class of the table we're connecting from
- :param other_table: The class of the table we're connecting to
- :param table_prefix: Custom prefix for the helper table name and the backreference name
- :param dict_key: If provided, will use a dict class with this column as the key
- """
-
- this_table = cls.__tablename__
- this_column_name = '{0}_id'.format(this_table)
- this_foreign_key = '{0}.id'.format(this_table)
-
- other_column_name = '{0}_id'.format(other_table)
- other_foreign_key = '{0}.id'.format(other_table)
-
- helper_table = '{0}_{1}'.format(this_table, other_table)
-
- if backreference is None:
- backreference = formatting.pluralize(this_table)
- if table_prefix:
- helper_table = '{0}_{1}'.format(table_prefix, helper_table)
- backreference = '{0}_{1}'.format(table_prefix, backreference)
-
- backref_kwargs = backref_kwargs or {}
- backref_kwargs.setdefault('uselist', True)
-
- relationship_kwargs = relationship_kwargs or {}
- relationship_kwargs.setdefault('secondary', cls._get_secondary_table(
- cls.metadata,
- helper_table,
- this_column_name,
- other_column_name,
- this_foreign_key,
- other_foreign_key
- ))
-
- return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
- backreference, key=key, dict_key=dict_key)
-
- def to_dict(self, fields=None, suppress_error=False):
- """
- Return a dict representation of the model
-
- :param suppress_error: If set to True, sets `None` to attributes that
- it's unable to retrieve (e.g., if a relationship wasn't established
- yet, and so it's impossible to access a property through it)
- """
-
- res = dict()
- fields = fields or self.fields()
- for field in fields:
- try:
- field_value = getattr(self, field)
- except AttributeError:
- if suppress_error:
- field_value = None
- else:
- raise
- if isinstance(field_value, list):
- field_value = list(field_value)
- elif isinstance(field_value, dict):
- field_value = dict(field_value)
- elif isinstance(field_value, ModelMixin):
- field_value = field_value.to_dict()
- res[field] = field_value
-
- return res
-
- @classmethod
- def _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
- key=None, foreign_key=None, dict_key=None):
- relationship_kwargs = relationship_kwargs or {}
-
- if key:
- relationship_kwargs.setdefault('foreign_keys',
- lambda: getattr(
- cls._get_cls_by_tablename(cls.__tablename__),
- key))
-
- elif foreign_key:
- relationship_kwargs.setdefault('foreign_keys',
- lambda: getattr(
- cls._get_cls_by_tablename(table),
- foreign_key))
-
- if dict_key:
- relationship_kwargs.setdefault('collection_class',
- attribute_mapped_collection(dict_key))
-
- if backreference == '':
- return relationship(
- lambda: cls._get_cls_by_tablename(table),
- **relationship_kwargs
- )
- else:
- if backreference is None:
- backreference = cls.__tablename__
- backref_kwargs = backref_kwargs or {}
- return relationship(
- lambda: cls._get_cls_by_tablename(table),
- backref=backref(backreference, **backref_kwargs),
- **relationship_kwargs
- )
-
- @staticmethod
- def _get_secondary_table(metadata,
- helper_table,
- first_column,
- second_column,
- first_foreign_key,
- second_foreign_key):
- """
- Create a helper table for a many-to-many relationship
-
- :param helper_table: The name of the table
- :param first_column_name: The name of the first column in the table
- :param second_column_name: The name of the second column in the table
- :param first_foreign_key: The string representing the first foreign key,
- for example `blueprint.storage_id`, or `tenants.id`
- :param second_foreign_key: The string representing the second foreign key
- :return: A Table object
- """
-
- return Table(
- helper_table,
- metadata,
- Column(
- first_column,
- Integer,
- ForeignKey(first_foreign_key)
- ),
- Column(
- second_column,
- Integer,
- ForeignKey(second_foreign_key)
- )
- )
-
- @classmethod
- def _association_proxies(cls):
- for col, value in vars(cls).items():
- if isinstance(value, associationproxy.AssociationProxy):
- yield col
-
- @classmethod
- def fields(cls):
- """
- Return the list of field names for this table
-
- Mostly for backwards compatibility in the code (that uses `fields`)
- """
-
- fields = set(cls._association_proxies())
- fields.update(cls.__table__.columns.keys())
- return fields - set(getattr(cls, '__private_fields__', []))
-
- @classmethod
- def _get_cls_by_tablename(cls, tablename):
- """
- Return class reference mapped to table.
-
- :param tablename: String with name of table.
- :return: Class reference or None.
- """
-
- if tablename in (cls.__name__, cls.__tablename__):
- return cls
-
- for table_cls in cls._decl_class_registry.values():
- if tablename == getattr(table_cls, '__tablename__', None):
- return table_cls
-
- def __repr__(self):
- return '<{cls} id=`{id}`>'.format(
- cls=self.__class__.__name__,
- id=getattr(self, self.name_column_name()))
-
-
-class ModelIDMixin(object):
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(Text, index=True)
-
- @classmethod
- def id_column_name(cls):
- return 'id'
-
- @classmethod
- def name_column_name(cls):
- return 'name'
-
-
-class InstanceModelMixin(ModelMixin):
- """
- Mixin for :class:`ServiceInstance` models.
-
- All models support validation, diagnostic dumping, and representation as
- raw data (which can be translated into JSON or YAML) via :code:`as_raw`.
- """
-
- @property
- def as_raw(self):
- raise NotImplementedError
-
- def validate(self, context):
- pass
-
- def coerce_values(self, context, container, report_issues):
- pass
-
- def dump(self, context):
- pass
-
-
-class TemplateModelMixin(InstanceModelMixin):
- """
- Mixin for :class:`ServiceTemplate` models.
-
- All model models can be instantiated into :class:`ServiceInstance` models.
- """
-
- def instantiate(self, context, container):
- raise NotImplementedError
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/misc.py
----------------------------------------------------------------------
diff --git a/aria/modeling/misc.py b/aria/modeling/misc.py
deleted file mode 100644
index 105876a..0000000
--- a/aria/modeling/misc.py
+++ /dev/null
@@ -1,234 +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.
-
-# pylint: disable=no-self-argument, no-member, abstract-method
-
-import cPickle as pickle
-import logging
-
-from sqlalchemy import (
- Column,
- Text,
- Binary
-)
-from sqlalchemy.ext.declarative import declared_attr
-
-from ..storage import exceptions
-from ..utils import collections, formatting, console
-from .bases import InstanceModelMixin, TemplateModelMixin
-from . import utils
-
-
-class ParameterBase(TemplateModelMixin):
- """
- Represents a typed value.
-
- This class is used by both service template and service instance elements.
-
- :ivar name: Name
- :ivar type_name: Type name
- :ivar value: Value
- :ivar description: Description
- """
-
- __tablename__ = 'parameter'
-
- name = Column(Text)
- type_name = Column(Text)
-
- # Check: value type
- _value = Column(Binary, name='value')
- description = Column(Text)
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type_name),
- ('value', self.value),
- ('description', self.description)))
-
- @property
- def value(self):
- if self._value is None:
- return None
- try:
- return pickle.loads(self._value)
- except BaseException:
- raise exceptions.StorageError('bad format for parameter of type "{0}": {1}'.format(
- self.type_name, self._value))
-
- @value.setter
- def value(self, value):
- if value is None:
- self._value = None
- else:
- try:
- self._value = pickle.dumps(value)
- except (pickle.PicklingError, TypeError):
- logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
- .format(self.type_name, value))
- self._value = pickle.dumps(str(value))
-
- def instantiate(self, context, container):
- from . import models
- return models.Parameter(name=self.name,
- type_name=self.type_name,
- _value=self._value,
- description=self.description)
-
- def coerce_values(self, context, container, report_issues):
- if self.value is not None:
- self.value = utils.coerce_value(context, container, self.value,
- report_issues)
-
- def dump(self, context):
- if self.type_name is not None:
- console.puts('{0}: {1} ({2})'.format(
- context.style.property(self.name),
- context.style.literal(self.value),
- context.style.type(self.type_name)))
- else:
- console.puts('{0}: {1}'.format(
- context.style.property(self.name),
- context.style.literal(self.value)))
- if self.description:
- console.puts(context.style.meta(self.description))
-
-
-class TypeBase(InstanceModelMixin):
- """
- Represents a type and its children.
- """
-
- __tablename__ = 'type'
-
- variant = Column(Text, nullable=False)
- description = Column(Text)
- _role = Column(Text, name='role')
-
- @declared_attr
- def parent(cls):
- return cls.relationship_to_self('parent_type_fk')
-
- @declared_attr
- def children(cls):
- return cls.one_to_many_relationship_to_self('parent_type_fk')
-
- # region foreign keys
-
- __private_fields__ = ['parent_type_fk']
-
- # Type one-to-many to Type
- @declared_attr
- def parent_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
-
- # endregion
-
- @property
- def role(self):
- def get_role(the_type):
- if the_type is None:
- return None
- elif the_type._role is None:
- return get_role(the_type.parent)
- return the_type._role
-
- return get_role(self)
-
- @role.setter
- def role(self, value):
- self._role = value
-
- def is_descendant(self, base_name, name):
- base = self.get_descendant(base_name)
- if base is not None:
- if base.get_descendant(name) is not None:
- return True
- return False
-
- def get_descendant(self, name):
- if self.name == name:
- return self
- for child in self.children:
- found = child.get_descendant(name)
- if found is not None:
- return found
- return None
-
- def iter_descendants(self):
- for child in self.children:
- yield child
- for descendant in child.iter_descendants():
- yield descendant
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('role', self.role)))
-
- @property
- def as_raw_all(self):
- types = []
- self._append_raw_children(types)
- return types
-
- def dump(self, context):
- if self.name:
- console.puts(context.style.type(self.name))
- with context.style.indent:
- for child in self.children:
- child.dump(context)
-
- def _append_raw_children(self, types):
- for child in self.children:
- raw_child = formatting.as_raw(child)
- raw_child['parent'] = self.name
- types.append(raw_child)
- child._append_raw_children(types)
-
-
-class MetadataBase(TemplateModelMixin):
- """
- Custom values associated with the service.
-
- This class is used by both service template and service instance elements.
-
- :ivar name: Name
- :ivar value: Value
- """
-
- __tablename__ = 'metadata'
-
- value = Column(Text)
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('value', self.value)))
-
- def instantiate(self, context, container):
- from . import models
- return models.Metadata(name=self.name,
- value=self.value)
-
- def dump(self, context):
- console.puts('{0}: {1}'.format(
- context.style.property(self.name),
- context.style.literal(self.value)))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/mixins.py
----------------------------------------------------------------------
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
new file mode 100644
index 0000000..04497b5
--- /dev/null
+++ b/aria/modeling/mixins.py
@@ -0,0 +1,405 @@
+# 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's storage.structures module
+Path: aria.storage.structures
+
+models module holds ARIA's models.
+
+classes:
+ * ModelMixin - abstract model implementation.
+ * ModelIDMixin - abstract model implementation with IDs.
+"""
+
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.orm.collections import attribute_mapped_collection
+from sqlalchemy.ext import associationproxy
+from sqlalchemy import (
+ Column,
+ ForeignKey,
+ Integer,
+ Text,
+ Table,
+)
+
+from . import utils
+from ..utils import formatting
+
+
+class ModelMixin(object):
+
+ @utils.classproperty
+ def __modelname__(cls): # pylint: disable=no-self-argument
+ return getattr(cls, '__mapiname__', cls.__tablename__)
+
+ @classmethod
+ def id_column_name(cls):
+ raise NotImplementedError
+
+ @classmethod
+ def name_column_name(cls):
+ raise NotImplementedError
+
+ def to_dict(self, fields=None, suppress_error=False):
+ """
+ Return a dict representation of the model
+
+ :param suppress_error: If set to True, sets `None` to attributes that
+ it's unable to retrieve (e.g., if a relationship wasn't established
+ yet, and so it's impossible to access a property through it)
+ """
+
+ res = dict()
+ fields = fields or self.fields()
+ for field in fields:
+ try:
+ field_value = getattr(self, field)
+ except AttributeError:
+ if suppress_error:
+ field_value = None
+ else:
+ raise
+ if isinstance(field_value, list):
+ field_value = list(field_value)
+ elif isinstance(field_value, dict):
+ field_value = dict(field_value)
+ elif isinstance(field_value, ModelMixin):
+ field_value = field_value.to_dict()
+ res[field] = field_value
+
+ return res
+
+ @classmethod
+ def fields(cls):
+ """
+ Return the list of field names for this table
+
+ Mostly for backwards compatibility in the code (that uses `fields`)
+ """
+
+ fields = set(cls._iter_association_proxies())
+ fields.update(cls.__table__.columns.keys())
+ return fields - set(getattr(cls, '__private_fields__', []))
+
+ @classmethod
+ def _create_foreign_key(cls, parent_table, nullable=False):
+ """
+ Return a ForeignKey object.
+
+ :param parent_table: Parent table name
+ :param nullable: Should the column be allowed to remain empty
+ """
+ return Column(Integer,
+ ForeignKey('{table}.id'.format(table=parent_table),
+ ondelete='CASCADE'),
+ nullable=nullable)
+
+ @classmethod
+ def _create_relationship_to_self(cls,
+ column_name,
+ relationship_kwargs=None):
+ relationship_kwargs = relationship_kwargs or {}
+
+ remote_side = '{cls}.{remote_column}'.format(
+ cls=cls.__name__,
+ remote_column=cls.id_column_name()
+ )
+
+ primaryjoin = '{remote_side} == {cls}.{column}'.format(
+ remote_side=remote_side,
+ cls=cls.__name__,
+ column=column_name
+ )
+
+ return relationship(
+ cls._get_cls_by_tablename(cls.__tablename__).__name__,
+ primaryjoin=primaryjoin,
+ remote_side=remote_side,
+ post_update=True,
+ **relationship_kwargs
+ )
+
+ @classmethod
+ def _create_one_to_many_relationship_to_self(cls,
+ key,
+ dict_key=None,
+ relationship_kwargs=None):
+ relationship_kwargs = relationship_kwargs or {}
+
+ relationship_kwargs.setdefault('remote_side', '{cls}.{remote_column}'.format(
+ cls=cls.__name__,
+ remote_column=key
+ ))
+
+ return cls._create_relationship(cls.__tablename__, None, relationship_kwargs,
+ backreference='', dict_key=dict_key)
+
+ @classmethod
+ def _create_one_to_one_relationship(cls,
+ other_table,
+ key=None,
+ foreign_key=None,
+ backreference=None,
+ backref_kwargs=None,
+ relationship_kwargs=None):
+ backref_kwargs = backref_kwargs or {}
+ backref_kwargs.setdefault('uselist', False)
+
+ return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
+ backreference, key=key, foreign_key=foreign_key)
+
+ @classmethod
+ def _create_one_to_many_relationship(cls,
+ child_table,
+ key=None,
+ foreign_key=None,
+ dict_key=None,
+ backreference=None,
+ backref_kwargs=None,
+ relationship_kwargs=None):
+ backref_kwargs = backref_kwargs or {}
+ backref_kwargs.setdefault('uselist', False)
+
+ return cls._create_relationship(child_table, backref_kwargs, relationship_kwargs,
+ backreference, key=key, foreign_key=foreign_key,
+ dict_key=dict_key)
+
+ @classmethod
+ def _create_many_to_one_relationship(cls,
+ parent_table,
+ key=None,
+ foreign_key=None,
+ backreference=None,
+ backref_kwargs=None,
+ relationship_kwargs=None):
+ """
+ Return a one-to-many SQL relationship object
+ Meant to be used from inside the *child* object
+
+ :param parent_table: Name of the parent table
+ :param foreign_key: The column of the foreign key (from the child table)
+ :param backreference: The name to give to the reference to the child (on the parent table)
+ """
+
+ if backreference is None:
+ backreference = formatting.pluralize(cls.__tablename__)
+
+ backref_kwargs = backref_kwargs or {}
+ backref_kwargs.setdefault('uselist', True)
+ backref_kwargs.setdefault('lazy', 'dynamic')
+ # The following line make sure that when the *parent* is deleted, all its connected children
+ # are deleted as well
+ backref_kwargs.setdefault('cascade', 'all')
+
+ return cls._create_relationship(parent_table, backref_kwargs, relationship_kwargs,
+ backreference, key=key, foreign_key=foreign_key)
+
+ @classmethod
+ def _create_many_to_many_relationship(cls,
+ other_table,
+ table_prefix=None,
+ key=None,
+ dict_key=None,
+ backreference=None,
+ backref_kwargs=None,
+ relationship_kwargs=None):
+ """
+ Return a many-to-many SQL relationship object
+
+ Notes:
+
+ 1. The backreference name is the current table's table name
+ 2. This method creates a new helper table in the DB
+
+ :param cls: The class of the table we're connecting from
+ :param other_table: The class of the table we're connecting to
+ :param table_prefix: Custom prefix for the helper table name and the backreference name
+ :param dict_key: If provided, will use a dict class with this column as the key
+ """
+
+ this_table = cls.__tablename__
+ this_column_name = '{0}_id'.format(this_table)
+ this_foreign_key = '{0}.id'.format(this_table)
+
+ other_column_name = '{0}_id'.format(other_table)
+ other_foreign_key = '{0}.id'.format(other_table)
+
+ helper_table = '{0}_{1}'.format(this_table, other_table)
+
+ if backreference is None:
+ backreference = formatting.pluralize(this_table)
+ if table_prefix:
+ helper_table = '{0}_{1}'.format(table_prefix, helper_table)
+ backreference = '{0}_{1}'.format(table_prefix, backreference)
+
+ backref_kwargs = backref_kwargs or {}
+ backref_kwargs.setdefault('uselist', True)
+
+ relationship_kwargs = relationship_kwargs or {}
+ relationship_kwargs.setdefault('secondary', cls._get_secondary_table(
+ cls.metadata,
+ helper_table,
+ this_column_name,
+ other_column_name,
+ this_foreign_key,
+ other_foreign_key
+ ))
+
+ return cls._create_relationship(other_table, backref_kwargs, relationship_kwargs,
+ backreference, key=key, dict_key=dict_key)
+
+ @classmethod
+ def _create_relationship(cls, table, backref_kwargs, relationship_kwargs, backreference,
+ key=None, foreign_key=None, dict_key=None):
+ relationship_kwargs = relationship_kwargs or {}
+
+ if key:
+ relationship_kwargs.setdefault('foreign_keys',
+ lambda: getattr(
+ cls._get_cls_by_tablename(cls.__tablename__),
+ key))
+
+ elif foreign_key:
+ relationship_kwargs.setdefault('foreign_keys',
+ lambda: getattr(
+ cls._get_cls_by_tablename(table),
+ foreign_key))
+
+ if dict_key:
+ relationship_kwargs.setdefault('collection_class',
+ attribute_mapped_collection(dict_key))
+
+ if backreference == '':
+ return relationship(
+ lambda: cls._get_cls_by_tablename(table),
+ **relationship_kwargs
+ )
+ else:
+ if backreference is None:
+ backreference = cls.__tablename__
+ backref_kwargs = backref_kwargs or {}
+ return relationship(
+ lambda: cls._get_cls_by_tablename(table),
+ backref=backref(backreference, **backref_kwargs),
+ **relationship_kwargs
+ )
+
+ @staticmethod
+ def _get_secondary_table(metadata,
+ helper_table,
+ first_column,
+ second_column,
+ first_foreign_key,
+ second_foreign_key):
+ """
+ Create a helper table for a many-to-many relationship
+
+ :param helper_table: The name of the table
+ :param first_column_name: The name of the first column in the table
+ :param second_column_name: The name of the second column in the table
+ :param first_foreign_key: The string representing the first foreign key,
+ for example `blueprint.storage_id`, or `tenants.id`
+ :param second_foreign_key: The string representing the second foreign key
+ :return: A Table object
+ """
+
+ return Table(
+ helper_table,
+ metadata,
+ Column(
+ first_column,
+ Integer,
+ ForeignKey(first_foreign_key)
+ ),
+ Column(
+ second_column,
+ Integer,
+ ForeignKey(second_foreign_key)
+ )
+ )
+
+ @classmethod
+ def _iter_association_proxies(cls):
+ for col, value in vars(cls).items():
+ if isinstance(value, associationproxy.AssociationProxy):
+ yield col
+
+ @classmethod
+ def _get_cls_by_tablename(cls, tablename):
+ """
+ Return class reference mapped to table.
+
+ :param tablename: String with name of table.
+ :return: Class reference or None.
+ """
+
+ if tablename in (cls.__name__, cls.__tablename__):
+ return cls
+
+ for table_cls in cls._decl_class_registry.values():
+ if tablename == getattr(table_cls, '__tablename__', None):
+ return table_cls
+
+ def __repr__(self):
+ return '<{cls} id=`{id}`>'.format(
+ cls=self.__class__.__name__,
+ id=getattr(self, self.name_column_name()))
+
+
+class ModelIDMixin(object):
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ name = Column(Text, index=True)
+
+ @classmethod
+ def id_column_name(cls):
+ return 'id'
+
+ @classmethod
+ def name_column_name(cls):
+ return 'name'
+
+
+class InstanceModelMixin(ModelMixin):
+ """
+ Mixin for :class:`ServiceInstance` models.
+
+ All models support validation, diagnostic dumping, and representation as
+ raw data (which can be translated into JSON or YAML) via :code:`as_raw`.
+ """
+
+ @property
+ def as_raw(self):
+ raise NotImplementedError
+
+ def validate(self, context):
+ pass
+
+ def coerce_values(self, context, container, report_issues):
+ pass
+
+ def dump(self, context):
+ pass
+
+
+class TemplateModelMixin(InstanceModelMixin):
+ """
+ Mixin for :class:`ServiceTemplate` models.
+
+ All model models can be instantiated into :class:`ServiceInstance` models.
+ """
+
+ def instantiate(self, context, container):
+ raise NotImplementedError
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/models.py
----------------------------------------------------------------------
diff --git a/aria/modeling/models.py b/aria/modeling/models.py
index fc2c669..90238e6 100644
--- a/aria/modeling/models.py
+++ b/aria/modeling/models.py
@@ -19,14 +19,15 @@ from sqlalchemy.ext.declarative import declarative_base
from . import (
service_template,
- service,
+ service_instance,
+ service_changes,
+ service_common,
orchestration,
- misc,
- bases,
+ mixins,
)
-aria_declarative_base = declarative_base(cls=bases.ModelIDMixin)
+aria_declarative_base = declarative_base(cls=mixins.ModelIDMixin)
# region service template models
@@ -84,91 +85,99 @@ class ArtifactTemplate(aria_declarative_base, service_template.ArtifactTemplateB
# region service instance models
-class Service(aria_declarative_base, service.ServiceBase):
+class Service(aria_declarative_base, service_instance.ServiceBase):
pass
-class Node(aria_declarative_base, service.NodeBase):
+class Node(aria_declarative_base, service_instance.NodeBase):
pass
-class Group(aria_declarative_base, service.GroupBase):
+class Group(aria_declarative_base, service_instance.GroupBase):
pass
-class Policy(aria_declarative_base, service.PolicyBase):
+class Policy(aria_declarative_base, service_instance.PolicyBase):
pass
-class Substitution(aria_declarative_base, service.SubstitutionBase):
+class Substitution(aria_declarative_base, service_instance.SubstitutionBase):
pass
-class SubstitutionMapping(aria_declarative_base, service.SubstitutionMappingBase):
+class SubstitutionMapping(aria_declarative_base, service_instance.SubstitutionMappingBase):
pass
-class Relationship(aria_declarative_base, service.RelationshipBase):
+class Relationship(aria_declarative_base, service_instance.RelationshipBase):
pass
-class Capability(aria_declarative_base, service.CapabilityBase):
+class Capability(aria_declarative_base, service_instance.CapabilityBase):
pass
-class Interface(aria_declarative_base, service.InterfaceBase):
+class Interface(aria_declarative_base, service_instance.InterfaceBase):
pass
-class Operation(aria_declarative_base, service.OperationBase):
+class Operation(aria_declarative_base, service_instance.OperationBase):
pass
-class Artifact(aria_declarative_base, service.ArtifactBase):
+class Artifact(aria_declarative_base, service_instance.ArtifactBase):
pass
# endregion
-# region orchestration models
+# region service changes models
-class Execution(aria_declarative_base, orchestration.Execution):
+class ServiceUpdate(aria_declarative_base, service_changes.ServiceUpdateBase):
pass
-class ServiceUpdate(aria_declarative_base, orchestration.ServiceUpdateBase):
+class ServiceUpdateStep(aria_declarative_base, service_changes.ServiceUpdateStepBase):
pass
-class ServiceUpdateStep(aria_declarative_base, orchestration.ServiceUpdateStepBase):
+class ServiceModification(aria_declarative_base, service_changes.ServiceModificationBase):
pass
+# endregion
+
+
+# region common service models
-class ServiceModification(aria_declarative_base, orchestration.ServiceModificationBase):
+class Parameter(aria_declarative_base, service_common.ParameterBase):
pass
-class Plugin(aria_declarative_base, orchestration.PluginBase):
+class Type(aria_declarative_base, service_common.TypeBase):
pass
-class Task(aria_declarative_base, orchestration.TaskBase):
+class Metadata(aria_declarative_base, service_common.MetadataBase):
+ pass
+
+
+class PluginSpecification(aria_declarative_base, service_common.PluginSpecificationBase):
pass
# endregion
-# region misc models
+# region orchestration models
-class Parameter(aria_declarative_base, misc.ParameterBase):
+class Execution(aria_declarative_base, orchestration.ExecutionBase):
pass
-class Type(aria_declarative_base, misc.TypeBase):
+class Plugin(aria_declarative_base, orchestration.PluginBase):
pass
-class Metadata(aria_declarative_base, misc.MetadataBase):
+class Task(aria_declarative_base, orchestration.TaskBase):
pass
# endregion
@@ -202,18 +211,21 @@ models_to_register = [
Operation,
Artifact,
- # Orchestration models
- Execution,
+ # Service changes models
ServiceUpdate,
ServiceUpdateStep,
ServiceModification,
- Plugin,
- Task,
- # Misc models
+ # Common service models
Parameter,
Type,
- Metadata
+ Metadata,
+ PluginSpecification,
+
+ # Orchestration models
+ Execution,
+ Plugin,
+ Task
]
__all__ = (
@@ -247,16 +259,19 @@ __all__ = (
'Operation',
'Artifact',
- # Orchestration models
- 'Execution',
+ # Service changes models
'ServiceUpdate',
'ServiceUpdateStep',
'ServiceModification',
- 'Plugin',
- 'Task',
- # Misc models
+ # Common service models
'Parameter',
'Type',
- 'Metadata'
+ 'Metadata',
+ 'PluginSpecification',
+
+ # Orchestration models
+ 'Execution',
+ 'Plugin',
+ 'Task'
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/orchestration.py
----------------------------------------------------------------------
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index c842c07..d8bdd3c 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -14,23 +14,14 @@
# limitations under the License.
"""
-ARIA's storage.models module
-Path: aria.storage.models
-
-models module holds ARIA's models.
-
classes:
* Execution - execution implementation model.
- * ServiceUpdate - service update implementation model.
- * ServiceUpdateStep - service update step implementation model.
- * ServiceModification - service modification implementation model.
* Plugin - plugin implementation model.
* Task - a task
"""
# pylint: disable=no-self-argument, no-member, abstract-method
-from collections import namedtuple
from datetime import datetime
from sqlalchemy import (
@@ -49,19 +40,16 @@ from sqlalchemy.ext.declarative import declared_attr
from ..orchestrator.exceptions import (TaskAbortException, TaskRetryException)
from .types import (List, Dict)
-from .bases import ModelMixin
+from .mixins import ModelMixin
__all__ = (
- 'Execution',
- 'ServiceUpdateBase',
- 'ServiceUpdateStepBase',
- 'ServiceModificationBase',
+ 'ExecutionBase',
'PluginBase',
'TaskBase'
)
-class Execution(ModelMixin):
+class ExecutionBase(ModelMixin):
"""
Execution model representation.
"""
@@ -117,7 +105,7 @@ class Execution(ModelMixin):
@declared_attr
def service(cls):
- return cls.many_to_one_relationship('service')
+ return cls._create_many_to_one_relationship('service')
@declared_attr
def service_name(cls):
@@ -133,7 +121,7 @@ class Execution(ModelMixin):
@declared_attr
def service_fk(cls):
- return cls.foreign_key('service')
+ return cls._create_foreign_key('service')
# endregion
@@ -145,182 +133,6 @@ class Execution(ModelMixin):
)
-class ServiceUpdateBase(ModelMixin):
- """
- Deployment update model representation.
- """
-
- steps = None
-
- __tablename__ = 'service_update'
-
- _private_fields = ['execution_fk',
- 'service_fk']
-
- created_at = Column(DateTime, nullable=False, index=True)
- service_plan = Column(Dict, nullable=False)
- service_update_nodes = Column(Dict)
- service_update_service = Column(Dict)
- service_update_node_templates = Column(List)
- modified_entity_ids = Column(Dict)
- state = Column(Text)
-
- @declared_attr
- def execution(cls):
- return cls.many_to_one_relationship('execution')
-
- @declared_attr
- def execution_name(cls):
- return association_proxy('execution', cls.name_column_name())
-
- @declared_attr
- def service(cls):
- return cls.many_to_one_relationship('service',
- backreference='updates')
-
- @declared_attr
- def service_name(cls):
- return association_proxy('service', cls.name_column_name())
-
- # region foreign keys
-
- __private_fields__ = ['service_fk',
- 'execution_fk']
-
- @declared_attr
- def execution_fk(cls):
- return cls.foreign_key('execution', nullable=True)
-
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # endregion
-
- def to_dict(self, suppress_error=False, **kwargs):
- dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error) #pylint: disable=no-member
- # Taking care of the fact the DeploymentSteps are _BaseModels
- dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
- return dep_update_dict
-
-
-class ServiceUpdateStepBase(ModelMixin):
- """
- Deployment update step model representation.
- """
-
- __tablename__ = 'service_update_step'
-
- _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
- ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
-
- _entity_types = namedtuple(
- 'ENTITY_TYPES',
- 'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, PLUGIN')
- ENTITY_TYPES = _entity_types(
- NODE='node',
- RELATIONSHIP='relationship',
- PROPERTY='property',
- OPERATION='operation',
- WORKFLOW='workflow',
- OUTPUT='output',
- DESCRIPTION='description',
- GROUP='group',
- PLUGIN='plugin'
- )
-
- action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False)
- entity_id = Column(Text, nullable=False)
- entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
-
- @declared_attr
- def service_update(cls):
- return cls.many_to_one_relationship('service_update',
- backreference='steps')
-
- @declared_attr
- def service_update_name(cls):
- return association_proxy('service_update', cls.name_column_name())
-
- # region foreign keys
-
- __private_fields__ = ['service_update_fk']
-
- @declared_attr
- def service_update_fk(cls):
- return cls.foreign_key('service_update')
-
- # endregion
-
- def __hash__(self):
- return hash((getattr(self, self.id_column_name()), self.entity_id))
-
- def __lt__(self, other):
- """
- the order is 'remove' < 'modify' < 'add'
- :param other:
- :return:
- """
- if not isinstance(other, self.__class__):
- return not self >= other
-
- if self.action != other.action:
- if self.action == 'remove':
- return_value = True
- elif self.action == 'add':
- return_value = False
- else:
- return_value = other.action == 'add'
- return return_value
-
- if self.action == 'add':
- return self.entity_type == 'node' and other.entity_type == 'relationship'
- if self.action == 'remove':
- return self.entity_type == 'relationship' and other.entity_type == 'node'
- return False
-
-
-class ServiceModificationBase(ModelMixin):
- """
- Deployment modification model representation.
- """
-
- __tablename__ = 'service_modification'
-
- STARTED = 'started'
- FINISHED = 'finished'
- ROLLEDBACK = 'rolledback'
-
- STATES = [STARTED, FINISHED, ROLLEDBACK]
- END_STATES = [FINISHED, ROLLEDBACK]
-
- context = Column(Dict)
- created_at = Column(DateTime, nullable=False, index=True)
- ended_at = Column(DateTime, index=True)
- modified_node_templates = Column(Dict)
- nodes = Column(Dict)
- status = Column(Enum(*STATES, name='service_modification_status'))
-
- @declared_attr
- def service(cls):
- return cls.many_to_one_relationship('service',
- backreference='modifications')
-
- @declared_attr
- def service_name(cls):
- return association_proxy('service', cls.name_column_name())
-
- # region foreign keys
-
- __private_fields__ = ['service_fk']
-
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # endregion
-
-
class PluginBase(ModelMixin):
"""
Plugin model representation.
@@ -340,16 +152,6 @@ class PluginBase(ModelMixin):
uploaded_at = Column(DateTime, nullable=False, index=True)
wheels = Column(List, nullable=False)
- # region foreign keys
-
- __private_fields__ = ['service_template_fk']
-
- @declared_attr
- def service_template_fk(cls):
- return cls.foreign_key('service_template', nullable=True)
-
- # endregion
-
class TaskBase(ModelMixin):
"""
@@ -389,7 +191,7 @@ class TaskBase(ModelMixin):
@declared_attr
def node(cls):
- return cls.many_to_one_relationship('node')
+ return cls._create_many_to_one_relationship('node')
@declared_attr
def relationship_name(cls):
@@ -397,15 +199,15 @@ class TaskBase(ModelMixin):
@declared_attr
def relationship(cls):
- return cls.many_to_one_relationship('relationship')
+ return cls._create_many_to_one_relationship('relationship')
@declared_attr
def plugin(cls):
- return cls.many_to_one_relationship('plugin')
+ return cls._create_many_to_one_relationship('plugin')
@declared_attr
def execution(cls):
- return cls.many_to_one_relationship('execution')
+ return cls._create_many_to_one_relationship('execution')
@declared_attr
def execution_name(cls):
@@ -413,8 +215,8 @@ class TaskBase(ModelMixin):
@declared_attr
def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
status = Column(Enum(*STATES, name='status'), default=PENDING)
@@ -428,9 +230,6 @@ class TaskBase(ModelMixin):
# Operation specific fields
implementation = Column(String)
- # This is unrelated to the plugin of the task. This field is related to the plugin name
- # received from the blueprint.
- plugin_name = Column(String)
_runs_on = Column(Enum(*RUNS_ON, name='runs_on'), name='runs_on')
@property
@@ -468,28 +267,28 @@ class TaskBase(ModelMixin):
@declared_attr
def node_fk(cls):
- return cls.foreign_key('node', nullable=True)
+ return cls._create_foreign_key('node', nullable=True)
@declared_attr
def relationship_fk(cls):
- return cls.foreign_key('relationship', nullable=True)
+ return cls._create_foreign_key('relationship', nullable=True)
@declared_attr
def plugin_fk(cls):
- return cls.foreign_key('plugin', nullable=True)
+ return cls._create_foreign_key('plugin', nullable=True)
@declared_attr
def execution_fk(cls):
- return cls.foreign_key('execution', nullable=True)
+ return cls._create_foreign_key('execution', nullable=True)
# endregion
@classmethod
- def as_node_task(cls, instance, runs_on, **kwargs):
+ def for_node(cls, instance, runs_on, **kwargs):
return cls(node=instance, _runs_on=runs_on, **kwargs)
@classmethod
- def as_relationship_task(cls, instance, runs_on, **kwargs):
+ def for_relationship(cls, instance, runs_on, **kwargs):
return cls(relationship=instance, _runs_on=runs_on, **kwargs)
@staticmethod
[5/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_instance.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
new file mode 100644
index 0000000..ba18f73
--- /dev/null
+++ b/aria/modeling/service_instance.py
@@ -0,0 +1,1529 @@
+# 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.
+
+# pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
+
+from sqlalchemy import (
+ Column,
+ Text,
+ Integer
+)
+from sqlalchemy import DateTime
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from .mixins import InstanceModelMixin
+from ..parser import validation
+from ..utils import collections, formatting, console
+
+from . import (
+ utils,
+ types as modeling_types
+)
+
+
+class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
+ """
+ A service is usually an instance of a :class:`ServiceTemplate`.
+
+ You will usually not create it programmatically, but instead instantiate it from a service
+ template.
+
+ :ivar name: Name (unique for this ARIA installation)
+ :vartype name: basestring
+ :ivar service_template: Template from which this service was instantiated (optional)
+ :vartype service_template: :class:`ServiceTemplate`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar meta_data: Custom annotations
+ :vartype meta_data: {basestring: :class:`Metadata`}
+ :ivar node: Nodes
+ :vartype node: [:class:`Node`]
+ :ivar groups: Groups of nodes
+ :vartype groups: [:class:`Group`]
+ :ivar policies: Policies
+ :vartype policies: [:class:`Policy`]
+ :ivar substitution: The entire service can appear as a node
+ :vartype substitution: :class:`Substitution`
+ :ivar inputs: Externally provided parameters
+ :vartype inputs: {basestring: :class:`Parameter`}
+ :ivar outputs: These parameters are filled in after service installation
+ :vartype outputs: {basestring: :class:`Parameter`}
+ :ivar workflows: Custom workflows that can be performed on the service
+ :vartype workflows: {basestring: :class:`Operation`}
+ :ivar plugin_specifications: Plugins required to be installed
+ :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
+ :ivar created_at: Creation timestamp
+ :vartype created_at: :class:`datetime.datetime`
+ :ivar updated_at: Update timestamp
+ :vartype updated_at: :class:`datetime.datetime`
+
+ :ivar permalink: ??
+ :vartype permalink: basestring
+ :ivar scaling_groups: ??
+ :vartype scaling_groups: {}
+
+ :ivar modifications: Modifications of this service
+ :vartype modifications: [:class:`ServiceModification`]
+ :ivar updates: Updates of this service
+ :vartype updates: [:class:`ServiceUpdate`]
+ :ivar executions: Executions on this service
+ :vartype executions: [:class:`Execution`]
+ """
+
+ __tablename__ = 'service'
+
+ @declared_attr
+ def service_template(cls):
+ return cls._create_many_to_one_relationship('service_template')
+
+ description = Column(Text)
+
+ @declared_attr
+ def meta_data(cls):
+ # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy!
+ return cls._create_many_to_many_relationship('metadata', dict_key='name')
+
+ @declared_attr
+ def nodes(cls):
+ return cls._create_one_to_many_relationship('node')
+
+ @declared_attr
+ def groups(cls):
+ return cls._create_one_to_many_relationship('group')
+
+ @declared_attr
+ def policies(cls):
+ return cls._create_one_to_many_relationship('policy')
+
+ @declared_attr
+ def substitution(cls):
+ return cls._create_one_to_one_relationship('substitution')
+
+ @declared_attr
+ def inputs(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
+
+ @declared_attr
+ def outputs(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='outputs',
+ dict_key='name')
+
+ @declared_attr
+ def workflows(cls):
+ return cls._create_one_to_many_relationship('operation', dict_key='name')
+
+ @declared_attr
+ def plugin_specifications(cls):
+ return cls._create_many_to_many_relationship('plugin_specification')
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ updated_at = Column(DateTime)
+
+ # region orchestration
+
+ permalink = Column(Text)
+ scaling_groups = Column(modeling_types.Dict)
+
+ # endregion
+
+ # region foreign keys
+
+ __private_fields__ = ['substituion_fk',
+ 'service_template_fk']
+
+ # Service one-to-one to Substitution
+ @declared_attr
+ def substitution_fk(cls):
+ return cls._create_foreign_key('substitution', nullable=True)
+
+ # Service many-to-one to ServiceTemplate
+ @declared_attr
+ def service_template_fk(cls):
+ return cls._create_foreign_key('service_template', nullable=True)
+
+ # endregion
+
+ def satisfy_requirements(self, context):
+ satisfied = True
+ for node in self.nodes:
+ if not node.satisfy_requirements(context):
+ satisfied = False
+ return satisfied
+
+ def validate_capabilities(self, context):
+ satisfied = True
+ for node in self.nodes:
+ if not node.validate_capabilities(context):
+ satisfied = False
+ return satisfied
+
+ def find_nodes(self, node_template_name):
+ nodes = []
+ for node in self.nodes:
+ if node.node_template.name == node_template_name:
+ nodes.append(node)
+ return collections.FrozenList(nodes)
+
+ def get_node_ids(self, node_template_name):
+ return collections.FrozenList((node.name for node in self.find_nodes(node_template_name)))
+
+ def find_groups(self, group_template_name):
+ groups = []
+ for group in self.groups:
+ if group.template_name == group_template_name:
+ groups.append(group)
+ return collections.FrozenList(groups)
+
+ def get_group_ids(self, group_template_name):
+ return collections.FrozenList((group.name
+ for group in self.find_groups(group_template_name)))
+
+ def is_node_a_target(self, context, target_node):
+ for node in self.nodes:
+ if self._is_node_a_target(context, node, target_node):
+ return True
+ return False
+
+ def _is_node_a_target(self, context, source_node, target_node):
+ if source_node.relationships:
+ for relationship in source_node.relationships:
+ if relationship.target_node_id == target_node.name:
+ return True
+ else:
+ node = context.modeling.instance.nodes.get(relationship.target_node_id)
+ if node is not None:
+ if self._is_node_a_target(context, 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, context):
+ utils.validate_dict_values(context, self.meta_data)
+ utils.validate_list_values(context, self.nodes)
+ utils.validate_list_values(context, self.groups)
+ utils.validate_list_values(context, self.policies)
+ if self.substitution is not None:
+ self.substitution.validate(context)
+ utils.validate_dict_values(context, self.inputs)
+ utils.validate_dict_values(context, self.outputs)
+ utils.validate_dict_values(context, self.workflows)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.meta_data, report_issues)
+ utils.coerce_list_values(context, container, self.nodes, report_issues)
+ utils.coerce_list_values(context, container, self.groups, report_issues)
+ utils.coerce_list_values(context, container, self.policies, report_issues)
+ if self.substitution is not None:
+ self.substitution.coerce_values(context, container, report_issues)
+ utils.coerce_dict_values(context, container, self.inputs, report_issues)
+ utils.coerce_dict_values(context, container, self.outputs, report_issues)
+ utils.coerce_dict_values(context, container, self.workflows, report_issues)
+
+ def dump(self, context):
+ if self.description is not None:
+ console.puts(context.style.meta(self.description))
+ utils.dump_dict_values(context, self.meta_data, 'Metadata')
+ for node in self.nodes:
+ node.dump(context)
+ for group in self.groups:
+ group.dump(context)
+ for policy in self.policies:
+ policy.dump(context)
+ if self.substitution is not None:
+ self.substitution.dump(context)
+ utils.dump_dict_values(context, self.inputs, 'Inputs')
+ utils.dump_dict_values(context, self.outputs, 'Outputs')
+ utils.dump_dict_values(context, self.workflows, 'Workflows')
+
+ def dump_graph(self, context):
+ for node in self.nodes.itervalues():
+ if not self.is_node_a_target(context, node):
+ self._dump_graph_node(context, node)
+
+ def _dump_graph_node(self, context, node):
+ console.puts(context.style.node(node.name))
+ if node.relationships:
+ with context.style.indent:
+ for relationship in node.relationships:
+ relationship_name = (context.style.node(relationship.template_name)
+ if relationship.template_name is not None
+ else context.style.type(relationship.type_name))
+ capability_name = (context.style.node(relationship.target_capability_name)
+ if relationship.target_capability_name is not None
+ else None)
+ if capability_name is not None:
+ console.puts('-> {0} {1}'.format(relationship_name, capability_name))
+ else:
+ console.puts('-> {0}'.format(relationship_name))
+ target_node = self.nodes.get(relationship.target_node_id)
+ with console.indent(3):
+ self._dump_graph_node(context, target_node)
+
+
+class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
+ """
+ Usually an instance of a :class:`NodeTemplate`.
+
+ Nodes may have zero or more :class:`Relationship` instances to other nodes.
+
+ :ivar name: Name (unique for this service)
+ :vartype name: basestring
+ :ivar node_template: Template from which this node was instantiated (optional)
+ :vartype node_template: :class:`NodeTemplate`
+ :ivar type: Node type
+ :vartype type: :class:`Type`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+ :ivar interfaces: Bundles of operations
+ :vartype interfaces: {basestring: :class:`Interface`}
+ :ivar artifacts: Associated files
+ :vartype artifacts: {basestring: :class:`Artifact`}
+ :ivar capabilities: Exposed capabilities
+ :vartype capabilities: {basestring: :class:`Capability`}
+ :ivar outbound_relationships: Relationships to other nodes
+ :vartype outbound_relationships: [:class:`Relationship`]
+ :ivar inbound_relationships: Relationships from other nodes
+ :vartype inbound_relationships: [:class:`Relationship`]
+ :ivar plugin_specifications: Plugins required to be installed on the node's host
+ :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
+ :ivar host: Host node (can be self)
+ :vartype host: :class:`Node`
+
+ :ivar runtime_properties: TODO: should be replaced with attributes
+ :vartype runtime_properties: {}
+ :ivar scaling_groups: ??
+ :vartype scaling_groups: []
+ :ivar state: ??
+ :vartype state: basestring
+ :ivar version: ??
+ :vartype version: int
+
+ :ivar service: Containing service
+ :vartype service: :class:`Service`
+ :ivar groups: We are a member of these groups
+ :vartype groups: [:class:`Group`]
+ :ivar policies: Policies enacted on this node
+ :vartype policies: [:class:`Policy`]
+ :ivar substitution_mapping: Our contribution to service substitution
+ :vartype substitution_mapping: :class:`SubstitutionMapping`
+ :ivar tasks: Tasks on this node
+ :vartype tasks: [:class:`Task`]
+ """
+
+ __tablename__ = 'node'
+
+ @declared_attr
+ def node_template(cls):
+ return cls._create_many_to_one_relationship('node_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ description = Column(Text)
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ @declared_attr
+ def interfaces(cls):
+ return cls._create_one_to_many_relationship('interface', dict_key='name')
+
+ @declared_attr
+ def artifacts(cls):
+ return cls._create_one_to_many_relationship('artifact', dict_key='name')
+
+ @declared_attr
+ def capabilities(cls):
+ return cls._create_one_to_many_relationship('capability', dict_key='name')
+
+ @declared_attr
+ def outbound_relationships(cls):
+ return cls._create_one_to_many_relationship('relationship',
+ foreign_key='source_node_fk',
+ backreference='source_node')
+
+ @declared_attr
+ def inbound_relationships(cls):
+ return cls._create_one_to_many_relationship('relationship',
+ foreign_key='target_node_fk',
+ backreference='target_node')
+
+ @declared_attr
+ def plugin_specifications(cls):
+ return cls._create_many_to_many_relationship('plugin_specification')
+
+ @declared_attr
+ def host(cls):
+ return cls._create_relationship_to_self('host_fk')
+
+ # region orchestration
+
+ runtime_properties = Column(modeling_types.Dict)
+ scaling_groups = Column(modeling_types.List)
+ state = Column(Text, nullable=False)
+ version = Column(Integer, default=1)
+
+ @declared_attr
+ def service_name(cls):
+ return association_proxy('service', 'name')
+
+ @property
+ def ip(self):
+ # TODO: totally broken
+ if not self.host_fk:
+ return None
+ host_node = self.host
+ if 'ip' in host_node.runtime_properties: # pylint: disable=no-member
+ return host_node.runtime_properties['ip'] # pylint: disable=no-member
+ host_node = host_node.node_template # pylint: disable=no-member
+ host_ip_property = host_node.properties.get('ip')
+ if host_ip_property:
+ return host_ip_property.value
+ return None
+
+ # endregion
+
+ # region foreign_keys
+
+ __private_fields__ = ['type_fk',
+ 'host_fk',
+ 'service_fk',
+ 'node_template_fk']
+
+ # Node many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Node one-to-one to Node
+ @declared_attr
+ def host_fk(cls):
+ return cls._create_foreign_key('node', nullable=True)
+
+ # Service one-to-many to Node
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # Node many-to-one to NodeTemplate
+ @declared_attr
+ def node_template_fk(cls):
+ return cls._create_foreign_key('node_template', nullable=True)
+
+ # endregion
+
+ def satisfy_requirements(self, context):
+ 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(context, node_template)
+ if target_node_template is not None:
+ satisfied = self._satisfy_capability(context,
+ target_node_capability,
+ target_node_template,
+ requirement_template)
+ else:
+ 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, context, target_node_capability, target_node_template,
+ requirement_template):
+ from . import models
+ # Find target nodes
+ target_nodes = context.modeling.instance.find_nodes(target_node_template.name)
+ 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:
+ target_capability = node.capabilities.get(target_node_capability.name)
+ if target_capability.relate():
+ target_node = node
+ 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 = \
+ requirement_template.relationship_template.instantiate(context, self)
+ else:
+ relationship = models.Relationship(target_capability=target_capability)
+ relationship.name = requirement_template.name
+ relationship.requirement_template = requirement_template
+ relationship.target_node = target_node
+ self.outbound_relationships.append(relationship)
+ 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):
+ 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
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('type_name', self.type_name),
+ ('properties', 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):
+ 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(context, self.properties)
+ utils.validate_dict_values(context, self.interfaces)
+ utils.validate_dict_values(context, self.artifacts)
+ utils.validate_dict_values(context, self.capabilities)
+ utils.validate_list_values(context, self.outbound_relationships)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, self, self.properties, report_issues)
+ utils.coerce_dict_values(context, self, self.interfaces, report_issues)
+ utils.coerce_dict_values(context, self, self.artifacts, report_issues)
+ utils.coerce_dict_values(context, self, self.capabilities, report_issues)
+ utils.coerce_list_values(context, self, self.outbound_relationships, report_issues)
+
+ def dump(self, context):
+ 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(context, self.properties, 'Properties')
+ utils.dump_interfaces(context, self.interfaces)
+ utils.dump_dict_values(context, self.artifacts, 'Artifacts')
+ utils.dump_dict_values(context, self.capabilities, 'Capabilities')
+ utils.dump_list_values(context, self.outbound_relationships, 'Relationships')
+
+
+class GroupBase(InstanceModelMixin):
+ """
+ Usually an instance of a :class:`GroupTemplate`.
+
+ :ivar name: Name (unique for this service)
+ :vartype name: basestring
+ :ivar group_template: Template from which this group was instantiated (optional)
+ :vartype group_template: :class:`GroupTemplate`
+ :ivar type: Group type
+ :vartype type: :class:`Type`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar nodes: Members of this group
+ :vartype nodes: [:class:`Node`]
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+ :ivar interfaces: Bundles of operations
+ :vartype interfaces: {basestring: :class:`Interface`}
+
+ :ivar service: Containing service
+ :vartype service: :class:`Service`
+ :ivar policies: Policies enacted on this group
+ :vartype policies: [:class:`Policy`]
+ """
+
+ __tablename__ = 'group'
+
+ @declared_attr
+ def group_template(cls):
+ return cls._create_many_to_one_relationship('group_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ description = Column(Text)
+
+ @declared_attr
+ def nodes(cls):
+ return cls._create_many_to_many_relationship('node')
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ @declared_attr
+ def interfaces(cls):
+ return cls._create_one_to_many_relationship('interface', dict_key='name')
+
+ # region foreign_keys
+
+ __private_fields__ = ['type_fk',
+ 'service_fk',
+ 'group_template_fk']
+
+ # Group many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Service one-to-many to Group
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # Group many-to-one to GroupTemplate
+ @declared_attr
+ def group_template_fk(cls):
+ return cls._create_foreign_key('group_template', nullable=True)
+
+ # endregion
+
+ @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, context):
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.interfaces)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.properties, report_issues)
+ utils.coerce_dict_values(context, container, self.interfaces, report_issues)
+
+ def dump(self, context):
+ 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(context, self.properties, 'Properties')
+ utils.dump_interfaces(context, 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):
+ """
+ Usually an instance of a :class:`PolicyTemplate`.
+
+ :ivar name: Name (unique for this service)
+ :vartype name: basestring
+ :ivar policy_template: Template from which this policy was instantiated (optional)
+ :vartype policy_template: :class:`PolicyTemplate`
+ :ivar type: Policy type
+ :vartype type: :class:`Type`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar nodes: Policy will be enacted on all these nodes
+ :vartype nodes: [:class:`Node`]
+ :ivar groups: Policy will be enacted on all nodes in these groups
+ :vartype groups: [:class:`Group`]
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+
+ :ivar service: Containing service
+ :vartype service: :class:`Service`
+ """
+
+ __tablename__ = 'policy'
+
+ @declared_attr
+ def policy_template(cls):
+ return cls._create_many_to_one_relationship('policy_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ description = Column(Text)
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ @declared_attr
+ def nodes(cls):
+ return cls._create_many_to_many_relationship('node')
+
+ @declared_attr
+ def groups(cls):
+ return cls._create_many_to_many_relationship('group')
+
+ # region foreign_keys
+
+ __private_fields__ = ['type_fk',
+ 'service_fk',
+ 'policy_template_fk']
+
+ # Policy many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Service one-to-many to Policy
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # Policy many-to-one to PolicyTemplate
+ @declared_attr
+ def policy_template_fk(cls):
+ return cls._create_foreign_key('policy_template', nullable=True)
+
+ # endregion
+
+ @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, context):
+ utils.validate_dict_values(context, self.properties)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+ def dump(self, context):
+ 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(context, 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):
+ """
+ Used to substitute a single node for the entire deployment.
+
+ Usually an instance of a :class:`SubstitutionTemplate`.
+
+ :ivar substitution_template: Template from which this substitution was instantiated (optional)
+ :vartype substitution_template: :class:`SubstitutionTemplate`
+ :ivar node_type: Exposed node type
+ :vartype node_type: :class:`Type`
+ :ivar mappings: Requirement and capability mappings
+ :vartype mappings: {basestring: :class:`SubstitutionTemplate`}
+
+ :ivar service: Containing service
+ :vartype service: :class:`Service`
+ """
+
+ __tablename__ = 'substitution'
+
+ @declared_attr
+ def substitution_template(cls):
+ return cls._create_many_to_one_relationship('substitution_template')
+
+ @declared_attr
+ def node_type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ @declared_attr
+ def mappings(cls):
+ return cls._create_one_to_many_relationship('substitution_mapping', dict_key='name')
+
+ # region foreign_keys
+
+ __private_fields__ = ['node_type_fk',
+ 'substitution_template_fk']
+
+ # Substitution many-to-one to Type
+ @declared_attr
+ def node_type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Substitution many-to-one to SubstitutionTemplate
+ @declared_attr
+ def substitution_template_fk(cls):
+ return cls._create_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, context):
+ utils.validate_dict_values(context, self.mappings)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.mappings, report_issues)
+
+ def dump(self, context):
+ console.puts('Substitution:')
+ with context.style.indent:
+ console.puts('Node type: {0}'.format(context.style.type(self.node_type.name)))
+ utils.dump_dict_values(context, self.mappings, 'Mappings')
+
+
+class SubstitutionMappingBase(InstanceModelMixin):
+ """
+ Used by :class:`Substitution` to map a capability or a requirement to a node.
+
+ Only one of `capability_template` and `requirement_template` can be set.
+
+ Usually an instance of a :class:`SubstitutionTemplate`.
+
+ :ivar name: Exposed capability or requirement name
+ :vartype name: basestring
+ :ivar node: Node
+ :vartype node: :class:`Node`
+ :ivar capability: Capability in the node
+ :vartype capability: :class:`Capability`
+ :ivar requirement_template: Requirement template in the node template
+ :vartype requirement_template: :class:`RequirementTemplate`
+
+ :ivar substitution: Containing substitution
+ :vartype substitution: :class:`Substitution`
+ """
+
+ __tablename__ = 'substitution_mapping'
+
+ @declared_attr
+ def node(cls):
+ return cls._create_one_to_one_relationship('node')
+
+ @declared_attr
+ def capability(cls):
+ return cls._create_one_to_one_relationship('capability')
+
+ @declared_attr
+ def requirement_template(cls):
+ return cls._create_one_to_one_relationship('requirement_template')
+
+ # region foreign keys
+
+ __private_fields__ = ['substitution_fk',
+ 'node_fk',
+ 'capability_fk',
+ 'requirement_template_fk']
+
+ # Substitution one-to-many to SubstitutionMapping
+ @declared_attr
+ def substitution_fk(cls):
+ return cls._create_foreign_key('substitution')
+
+ # Substitution one-to-one to NodeTemplate
+ @declared_attr
+ def node_fk(cls):
+ return cls._create_foreign_key('node')
+
+ # Substitution one-to-one to Capability
+ @declared_attr
+ def capability_fk(cls):
+ return cls._create_foreign_key('capability', nullable=True)
+
+ # Substitution one-to-one to RequirementTemplate
+ @declared_attr
+ def requirement_template_fk(cls):
+ return cls._create_foreign_key('requirement_template', nullable=True)
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name)))
+
+ def validate(self, context):
+ 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):
+ console.puts('{0} -> {1}.{2}'.format(
+ context.style.node(self.name),
+ context.style.node(self.node.name),
+ context.style.node(self.capability.name
+ if self.capability
+ else self.requirement_template.name)))
+
+
+class RelationshipBase(InstanceModelMixin):
+ """
+ Connects :class:`Node` to a capability in another node.
+
+ Might be an instance of a :class:`RelationshipTemplate`.
+
+ :ivar name: Name (usually the name of the requirement at the source node template)
+ :vartype name: basestring
+ :ivar relationship_template: Template from which this relationship was instantiated (optional)
+ :vartype relationship_template: :class:`RelationshipTemplate`
+ :ivar requirement_template: Template from which this relationship was instantiated (optional)
+ :vartype requirement_template: :class:`RequirementTemplate`
+ :ivar type: Relationship type
+ :vartype type: :class:`Type`
+ :ivar target_capability: Capability at the target node (optional)
+ :vartype target_capability: :class:`Capability`
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+ :ivar interfaces: Bundles of operations
+ :vartype interfaces: {basestring: :class:`Interfaces`}
+
+ :ivar source_position: ??
+ :vartype source_position: int
+ :ivar target_position: ??
+ :vartype target_position: int
+
+ :ivar source_node: Source node
+ :vartype source_node: :class:`Node`
+ :ivar target_node: Target node
+ :vartype target_node: :class:`Node`
+ :ivar tasks: Tasks on this node
+ :vartype tasks: [:class:`Task`]
+ """
+
+ __tablename__ = 'relationship'
+
+ @declared_attr
+ def relationship_template(cls):
+ return cls._create_many_to_one_relationship('relationship_template')
+
+ @declared_attr
+ def requirement_template(cls):
+ return cls._create_many_to_one_relationship('requirement_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ @declared_attr
+ def target_capability(cls):
+ return cls._create_one_to_one_relationship('capability')
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ @declared_attr
+ def interfaces(cls):
+ return cls._create_one_to_many_relationship('interface', dict_key='name')
+
+ # region orchestration
+
+ source_position = Column(Integer) # ???
+ target_position = Column(Integer) # ???
+
+ # endregion
+
+ # region foreign keys
+
+ __private_fields__ = ['type_fk',
+ 'source_node_fk',
+ 'target_node_fk',
+ 'target_capability_fk',
+ 'requirement_template_fk',
+ 'relationship_template_fk']
+
+ # Relationship many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type', nullable=True)
+
+ # Node one-to-many to Relationship
+ @declared_attr
+ def source_node_fk(cls):
+ return cls._create_foreign_key('node')
+
+ # Node one-to-many to Relationship
+ @declared_attr
+ def target_node_fk(cls):
+ return cls._create_foreign_key('node')
+
+ # Relationship one-to-one to Capability
+ @declared_attr
+ def target_capability_fk(cls):
+ return cls._create_foreign_key('capability', nullable=True)
+
+ # Relationship many-to-one to RequirementTemplate
+ @declared_attr
+ def requirement_template_fk(cls):
+ return cls._create_foreign_key('requirement_template', nullable=True)
+
+ # Relationship many-to-one to RelationshipTemplate
+ @declared_attr
+ def relationship_template_fk(cls):
+ return cls._create_foreign_key('relationship_template', nullable=True)
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('target_node_id', self.target_node.name),
+ ('type_name', self.type_name),
+ ('template_name', self.template_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('interfaces', formatting.as_raw_list(self.interfaces))))
+
+ def validate(self, context):
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.interfaces)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.properties, report_issues)
+ utils.coerce_dict_values(context, container, self.interfaces, report_issues)
+
+ def dump(self, context):
+ 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(context, self.properties, 'Properties')
+ utils.dump_interfaces(context, self.interfaces, 'Interfaces')
+
+
+class CapabilityBase(InstanceModelMixin):
+ """
+ A capability of a :class:`Node`.
+
+ Usually an instance of a :class:`CapabilityTemplate`.
+
+ :ivar name: Name (unique for the node)
+ :vartype name: basestring
+ :ivar capability_template: Template from which this capability was instantiated (optional)
+ :vartype capability_template: :class:`capabilityTemplate`
+ :ivar type: Capability type
+ :vartype type: :class:`Type`
+ :ivar min_occurrences: Minimum number of requirement matches required
+ :vartype min_occurrences: int
+ :ivar max_occurrences: Maximum number of requirement matches allowed
+ :vartype min_occurrences: int
+ :ivar occurrences: Actual number of requirement matches
+ :vartype occurrences: int
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+
+ :ivar node: Containing node
+ :vartype node: :class:`Node`
+ :ivar relationship: Available when we are the target of a relationship
+ :vartype relationship: :class:`Relationship`
+ :ivar substitution_mapping: Our contribution to service substitution
+ :vartype substitution_mapping: :class:`SubstitutionMapping`
+ """
+
+ __tablename__ = 'capability'
+
+ @declared_attr
+ def capability_template(cls):
+ return cls._create_many_to_one_relationship('capability_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ min_occurrences = Column(Integer, default=None)
+ max_occurrences = Column(Integer, default=None)
+ occurrences = Column(Integer, default=0)
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ # region foreign_keys
+
+ __private_fields__ = ['capability_fk',
+ 'node_fk',
+ 'capability_template_fk']
+
+ # Capability many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Node one-to-many to Capability
+ @declared_attr
+ def node_fk(cls):
+ return cls._create_foreign_key('node')
+
+ # Capability many-to-one to CapabilityTemplate
+ @declared_attr
+ def capability_template_fk(cls):
+ return cls._create_foreign_key('capability_template', nullable=True)
+
+ # endregion
+
+ @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, context):
+ utils.validate_dict_values(context, self.properties)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+ def dump(self, context):
+ 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(context, self.properties, 'Properties')
+
+
+class InterfaceBase(InstanceModelMixin):
+ """
+ A typed set of :class:`Operation`.
+
+ Usually an instance of :class:`InterfaceTemplate`.
+
+ :ivar name: Name (unique for the node, group, or relationship)
+ :vartype name: basestring
+ :ivar interface_template: Template from which this interface was instantiated (optional)
+ :vartype interface_template: :class:`InterfaceTemplate`
+ :ivar type: Interface type
+ :vartype type: :class:`Type`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar inputs: Parameters that can be used by all operations in the interface
+ :vartype inputs: {basestring: :class:`Parameter`}
+ :ivar operations: Operations
+ :vartype operations: {basestring: :class:`Operation`}
+
+ :ivar node: Containing node
+ :vartype node: :class:`Node`
+ :ivar group: Containing group
+ :vartype group: :class:`Group`
+ :ivar relationship: Containing relationship
+ :vartype relationship: :class:`Relationship`
+ """
+
+ __tablename__ = 'interface'
+
+ @declared_attr
+ def interface_template(cls):
+ return cls._create_many_to_one_relationship('interface_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ description = Column(Text)
+
+ @declared_attr
+ def inputs(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
+
+ @declared_attr
+ def operations(cls):
+ return cls._create_one_to_many_relationship('operation', dict_key='name')
+
+ # region foreign_keys
+
+ __private_fields__ = ['type_fk',
+ 'node_fk',
+ 'group_fk',
+ 'relationship_fk',
+ 'interface_template_fk']
+
+ # Interface many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Node one-to-many to Interface
+ @declared_attr
+ def node_fk(cls):
+ return cls._create_foreign_key('node', nullable=True)
+
+ # Group one-to-many to Interface
+ @declared_attr
+ def group_fk(cls):
+ return cls._create_foreign_key('group', nullable=True)
+
+ # Relationship one-to-many to Interface
+ @declared_attr
+ def relationship_fk(cls):
+ return cls._create_foreign_key('relationship', nullable=True)
+
+ # Interface many-to-one to InterfaceTemplate
+ @declared_attr
+ def interface_template_fk(cls):
+ return cls._create_foreign_key('interface_template', nullable=True)
+
+ # endregion
+
+ @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, context):
+ utils.validate_dict_values(context, self.inputs)
+ utils.validate_dict_values(context, self.operations)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.inputs, report_issues)
+ utils.coerce_dict_values(context, container, self.operations, report_issues)
+
+ def dump(self, context):
+ 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(context, self.inputs, 'Inputs')
+ utils.dump_dict_values(context, self.operations, 'Operations')
+
+
+class OperationBase(InstanceModelMixin):
+ """
+ An operation in a :class:`Interface`.
+
+ Might be an instance of :class:`OperationTemplate`.
+
+ :ivar name: Name (unique for the interface or service)
+ :vartype name: basestring
+ :ivar operation_template: Template from which this operation was instantiated (optional)
+ :vartype operation_template: :class:`OperationTemplate`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar plugin_specification: Associated plugin
+ :vartype plugin_specification: :class:`PluginSpecification`
+ :ivar implementation: Implementation string (interpreted by the plugin)
+ :vartype implementation: basestring
+ :ivar dependencies: Dependency strings (interpreted by the plugin)
+ :vartype dependencies: [basestring]
+ :ivar inputs: Parameters that can be used by this operation
+ :vartype inputs: {basestring: :class:`Parameter`}
+ :ivar executor: Executor name
+ :vartype executor: basestring
+ :ivar max_retries: Maximum number of retries allowed in case of failure
+ :vartype max_retries: int
+ :ivar retry_interval: Interval between retries (in seconds)
+ :vartype retry_interval: int
+
+ :ivar interface: Containing interface
+ :vartype interface: :class:`Interface`
+ :ivar service: Containing service
+ :vartype service: :class:`Service`
+ """
+
+ __tablename__ = 'operation'
+
+ @declared_attr
+ def operation_template(cls):
+ return cls._create_many_to_one_relationship('operation_template')
+
+ description = Column(Text)
+
+ @declared_attr
+ def plugin_specification(cls):
+ return cls._create_one_to_one_relationship('plugin_specification')
+
+ implementation = Column(Text)
+ dependencies = Column(modeling_types.StrictList(item_cls=basestring))
+
+ @declared_attr
+ def inputs(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
+
+ executor = Column(Text)
+ max_retries = Column(Integer)
+ retry_interval = Column(Integer)
+
+ # region foreign_keys
+
+ __private_fields__ = ['service_fk',
+ 'interface_fk',
+ 'plugin_fk',
+ 'operation_template_fk']
+
+ # Service one-to-many to Operation
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service', nullable=True)
+
+ # Interface one-to-many to Operation
+ @declared_attr
+ def interface_fk(cls):
+ return cls._create_foreign_key('interface', nullable=True)
+
+ # Operation one-to-one to PluginSpecification
+ @declared_attr
+ def plugin_specification_fk(cls):
+ return cls._create_foreign_key('plugin_specification', nullable=True)
+
+ # Operation many-to-one to OperationTemplate
+ @declared_attr
+ def operation_template_fk(cls):
+ return cls._create_foreign_key('operation_template', nullable=True)
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('implementation', self.implementation),
+ ('dependencies', self.dependencies),
+ ('executor', self.executor),
+ ('max_retries', self.max_retries),
+ ('retry_interval', self.retry_interval),
+ ('inputs', formatting.as_raw_dict(self.inputs))))
+
+ def validate(self, context):
+ # TODO must be associated with interface or service
+ utils.validate_dict_values(context, self.inputs)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.inputs, report_issues)
+
+ def dump(self, context):
+ 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))))
+ if self.executor is not None:
+ console.puts('Executor: {0}'.format(context.style.literal(self.executor)))
+ if self.max_retries is not None:
+ console.puts('Max retries: {0}'.format(context.style.literal(self.max_retries)))
+ if self.retry_interval is not None:
+ console.puts('Retry interval: {0}'.format(
+ context.style.literal(self.retry_interval)))
+ utils.dump_dict_values(context, self.inputs, 'Inputs')
+
+
+class ArtifactBase(InstanceModelMixin):
+ """
+ A file associated with a :class:`Node`.
+
+ Usually an instance of :class:`ArtifactTemplate`.
+
+ :ivar name: Name (unique for the node)
+ :vartype name: basestring
+ :ivar artifact_template: Template from which this artifact was instantiated (optional)
+ :vartype artifact_template: :class:`ArtifactTemplate`
+ :ivar type: Artifact type
+ :vartype type: :class:`Type`
+ :ivar description: Human-readable description
+ :vartype description: string
+ :ivar source_path: Source path (CSAR or repository)
+ :vartype source_path: basestring
+ :ivar target_path: Path at destination machine
+ :vartype target_path: basestring
+ :ivar repository_url: Repository URL
+ :vartype repository_path: basestring
+ :ivar repository_credential: Credentials for accessing the repository
+ :vartype repository_credential: {basestring: basestring}
+ :ivar properties: Associated parameters
+ :vartype properties: {basestring: :class:`Parameter`}
+
+ :ivar node: Containing node
+ :vartype node: :class:`Node`
+ """
+
+ __tablename__ = 'artifact'
+
+ @declared_attr
+ def artifact_template(cls):
+ return cls._create_many_to_one_relationship('artifact_template')
+
+ @declared_attr
+ def type(cls):
+ return cls._create_many_to_one_relationship('type')
+
+ description = Column(Text)
+ source_path = Column(Text)
+ target_path = Column(Text)
+ repository_url = Column(Text)
+ repository_credential = Column(modeling_types.StrictDict(basestring, basestring))
+
+ @declared_attr
+ def properties(cls):
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
+
+ # region foreign_keys
+
+ __private_fields__ = ['type_fk',
+ 'node_fk',
+ 'artifact_template_fk']
+
+ # Artifact many-to-one to Type
+ @declared_attr
+ def type_fk(cls):
+ return cls._create_foreign_key('type')
+
+ # Node one-to-many to Artifact
+ @declared_attr
+ def node_fk(cls):
+ return cls._create_foreign_key('node')
+
+ # Artifact many-to-one to ArtifactTemplate
+ @declared_attr
+ def artifact_template_fk(cls):
+ return cls._create_foreign_key('artifact_template', nullable=True)
+
+ # endregion
+
+ @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, context):
+ utils.validate_dict_values(context, self.properties)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.properties, report_issues)
+
+ def dump(self, context):
+ 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(context, self.properties, 'Properties')
[4/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_template.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index 092de51..c9a02eb 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -30,7 +30,7 @@ from sqlalchemy.ext.declarative import declared_attr
from ..parser import validation
from ..utils import collections, formatting, console
-from .bases import TemplateModelMixin
+from .mixins import TemplateModelMixin
from . import (
utils,
types as modeling_types
@@ -64,10 +64,10 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
:vartype inputs: {basestring: :class:`Parameter`}
:ivar outputs: These parameters are filled in after service installation
:vartype outputs: {basestring: :class:`Parameter`}
- :ivar operation_templates: Custom operations that can be performed on the service
- :vartype operation_templates: {basestring: :class:`OperationTemplate`}
- :ivar plugins: Plugins required by services
- :vartype plugins: {basestring: :class:`Plugin`}
+ :ivar workflow_templates: Custom workflows that can be performed on the service
+ :vartype workflow_templates: {basestring: :class:`OperationTemplate`}
+ :ivar plugin_specifications: Plugins required by services
+ :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
:ivar node_types: Base for the node type hierarchy
:vartype node_types: :class:`Type`
:ivar group_types: Base for the group type hierarchy
@@ -82,8 +82,8 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
:vartype interface_types: :class:`Type`
:ivar artifact_types: Base for the artifact type hierarchy
:vartype artifact_types: :class:`Type`
- :ivar plugins: Plugins required to be installed
- :vartype plugins: {basestring: :class:`Plugin`}
+ :ivar plugin_specifications: Plugins required to be installed
+ :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
:ivar created_at: Creation timestamp
:vartype created_at: :class:`datetime.datetime`
:ivar updated_at: Update timestamp
@@ -101,69 +101,73 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
@declared_attr
def meta_data(cls):
# Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy!
- return cls.many_to_many_relationship('metadata', dict_key='name')
+ return cls._create_many_to_many_relationship('metadata', dict_key='name')
@declared_attr
def node_templates(cls):
- return cls.one_to_many_relationship('node_template')
+ return cls._create_one_to_many_relationship('node_template')
@declared_attr
def group_templates(cls):
- return cls.one_to_many_relationship('group_template')
+ return cls._create_one_to_many_relationship('group_template')
@declared_attr
def policy_templates(cls):
- return cls.one_to_many_relationship('policy_template')
+ return cls._create_one_to_many_relationship('policy_template')
@declared_attr
def substitution_template(cls):
- return cls.one_to_one_relationship('substitution_template')
+ return cls._create_one_to_one_relationship('substitution_template')
@declared_attr
def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
@declared_attr
def outputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='outputs',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='outputs',
+ dict_key='name')
@declared_attr
- def operation_templates(cls):
- return cls.one_to_many_relationship('operation_template', dict_key='name')
+ def workflow_templates(cls):
+ return cls._create_one_to_many_relationship('operation_template', dict_key='name')
@declared_attr
- def plugins(cls):
- return cls.one_to_many_relationship('plugin')
+ def plugin_specifications(cls):
+ return cls._create_one_to_many_relationship('plugin_specification')
@declared_attr
def node_types(cls):
- return cls.one_to_one_relationship('type', key='node_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='node_type_fk', backreference='')
@declared_attr
def group_types(cls):
- return cls.one_to_one_relationship('type', key='group_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='group_type_fk', backreference='')
@declared_attr
def policy_types(cls):
- return cls.one_to_one_relationship('type', key='policy_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='policy_type_fk', backreference='')
@declared_attr
def relationship_types(cls):
- return cls.one_to_one_relationship('type', key='relationship_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='relationship_type_fk',
+ backreference='')
@declared_attr
def capability_types(cls):
- return cls.one_to_one_relationship('type', key='capability_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='capability_type_fk',
+ backreference='')
@declared_attr
def interface_types(cls):
- return cls.one_to_one_relationship('type', key='interface_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='interface_type_fk',
+ backreference='')
@declared_attr
def artifact_types(cls):
- return cls.one_to_one_relationship('type', key='artifact_type_fk', backreference='')
+ return cls._create_one_to_one_relationship('type', key='artifact_type_fk',
+ backreference='')
# region orchestration
@@ -186,42 +190,42 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
# ServiceTemplate one-to-one to SubstitutionTemplate
@declared_attr
def substitution_template_fk(cls):
- return cls.foreign_key('substitution_template', nullable=True)
+ return cls._create_foreign_key('substitution_template', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def node_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def group_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def policy_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def relationship_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def capability_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def interface_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# ServiceTemplate one-to-one to Type
@declared_attr
def artifact_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# endregion
@@ -250,7 +254,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
('substitution_template', formatting.as_raw(self.substitution_template)),
('inputs', formatting.as_raw_dict(self.inputs)),
('outputs', formatting.as_raw_dict(self.outputs)),
- ('operation_templates', formatting.as_raw_list(self.operation_templates))))
+ ('workflow_templates', formatting.as_raw_list(self.workflow_templates))))
@property
def types_as_raw(self):
@@ -283,7 +287,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
utils.instantiate_list(context, self, service.groups, self.group_templates)
utils.instantiate_list(context, self, service.policies, self.policy_templates)
- utils.instantiate_dict(context, self, service.operations, self.operation_templates)
+ utils.instantiate_dict(context, self, service.workflows, self.workflow_templates)
if self.substitution_template is not None:
service.substitution = self.substitution_template.instantiate(context, container)
@@ -308,7 +312,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
self.substitution_template.validate(context)
utils.validate_dict_values(context, self.inputs)
utils.validate_dict_values(context, self.outputs)
- utils.validate_dict_values(context, self.operation_templates)
+ utils.validate_dict_values(context, self.workflow_templates)
if self.node_types is not None:
self.node_types.validate(context)
if self.group_types is not None:
@@ -333,7 +337,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
self.substitution_template.coerce_values(context, container, report_issues)
utils.coerce_dict_values(context, container, self.inputs, report_issues)
utils.coerce_dict_values(context, container, self.outputs, report_issues)
- utils.coerce_dict_values(context, container, self.operation_templates, report_issues)
+ utils.coerce_dict_values(context, container, self.workflow_templates, report_issues)
def dump(self, context):
if self.description is not None:
@@ -349,7 +353,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public
self.substitution_template.dump(context)
utils.dump_dict_values(context, self.inputs, 'Inputs')
utils.dump_dict_values(context, self.outputs, 'Outputs')
- utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+ utils.dump_dict_values(context, self.workflow_templates, 'Workflow templates')
def dump_types(self, context):
if self.node_types.children:
@@ -407,8 +411,8 @@ class NodeTemplateBase(TemplateModelMixin):
:vartype requirement_templates: [:class:`RequirementTemplate`]
:ivar target_node_template_constraints: Constraints for filtering relationship targets
:vartype target_node_template_constraints: [:class:`FunctionType`]
- :ivar plugins: Plugins required to be installed on the node's host
- :vartype plugins: {basestring: :class:`Plugin`}
+ :ivar plugin_specifications: Plugins required to be installed on the node's host
+ :vartype plugin_specifications: {basestring: :class:`PluginSpecification`}
:ivar service_template: Containing service template
:vartype service_template: :class:`ServiceTemplate`
@@ -426,7 +430,7 @@ class NodeTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
default_instances = Column(Integer, default=1)
@@ -435,32 +439,32 @@ class NodeTemplateBase(TemplateModelMixin):
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
@declared_attr
def interface_templates(cls):
- return cls.one_to_many_relationship('interface_template', dict_key='name')
+ return cls._create_one_to_many_relationship('interface_template', dict_key='name')
@declared_attr
def artifact_templates(cls):
- return cls.one_to_many_relationship('artifact_template', dict_key='name')
+ return cls._create_one_to_many_relationship('artifact_template', dict_key='name')
@declared_attr
def capability_templates(cls):
- return cls.one_to_many_relationship('capability_template', dict_key='name')
+ return cls._create_one_to_many_relationship('capability_template', dict_key='name')
@declared_attr
def requirement_templates(cls):
- return cls.one_to_many_relationship('requirement_template',
- foreign_key='node_template_fk',
- backreference='node_template')
+ return cls._create_one_to_many_relationship('requirement_template',
+ foreign_key='node_template_fk',
+ backreference='node_template')
target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
@declared_attr
- def plugins(cls):
- return cls.many_to_many_relationship('plugin')
+ def plugin_specifications(cls):
+ return cls._create_many_to_many_relationship('plugin_specification')
# region foreign_keys
@@ -470,12 +474,12 @@ class NodeTemplateBase(TemplateModelMixin):
# NodeTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# ServiceTemplate one-to-many to NodeTemplate
@declared_attr
def service_template_fk(cls):
- return cls.foreign_key('service_template')
+ return cls._create_foreign_key('service_template')
# endregion
@@ -579,22 +583,22 @@ class GroupTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
@declared_attr
def node_templates(cls):
- return cls.many_to_many_relationship('node_template')
+ return cls._create_many_to_many_relationship('node_template')
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
@declared_attr
def interface_templates(cls):
- return cls.one_to_many_relationship('interface_template', dict_key='name')
+ return cls._create_one_to_many_relationship('interface_template', dict_key='name')
# region foreign keys
@@ -604,12 +608,12 @@ class GroupTemplateBase(TemplateModelMixin):
# GroupTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# ServiceTemplate one-to-many to GroupTemplate
@declared_attr
def service_template_fk(cls):
- return cls.foreign_key('service_template')
+ return cls._create_foreign_key('service_template')
# endregion
@@ -684,22 +688,22 @@ class PolicyTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
@declared_attr
def node_templates(cls):
- return cls.many_to_many_relationship('node_template')
+ return cls._create_many_to_many_relationship('node_template')
@declared_attr
def group_templates(cls):
- return cls.many_to_many_relationship('group_template')
+ return cls._create_many_to_many_relationship('group_template')
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
# region foreign keys
@@ -709,12 +713,12 @@ class PolicyTemplateBase(TemplateModelMixin):
# PolicyTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# ServiceTemplate one-to-many to PolicyTemplate
@declared_attr
def service_template_fk(cls):
- return cls.foreign_key('service_template')
+ return cls._create_foreign_key('service_template')
# endregion
@@ -781,11 +785,12 @@ class SubstitutionTemplateBase(TemplateModelMixin):
@declared_attr
def node_type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
@declared_attr
def mappings(cls):
- return cls.one_to_many_relationship('substitution_template_mapping', dict_key='name')
+ return cls._create_one_to_many_relationship('substitution_template_mapping',
+ dict_key='name')
# region foreign keys
@@ -794,7 +799,7 @@ class SubstitutionTemplateBase(TemplateModelMixin):
# SubstitutionTemplate many-to-one to Type
@declared_attr
def node_type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# endregion
@@ -847,15 +852,15 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
@declared_attr
def node_template(cls):
- return cls.one_to_one_relationship('node_template')
+ return cls._create_one_to_one_relationship('node_template')
@declared_attr
def capability_template(cls):
- return cls.one_to_one_relationship('capability_template')
+ return cls._create_one_to_one_relationship('capability_template')
@declared_attr
def requirement_template(cls):
- return cls.one_to_one_relationship('requirement_template')
+ return cls._create_one_to_one_relationship('requirement_template')
# region foreign keys
@@ -867,22 +872,22 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin):
# SubstitutionTemplate one-to-many to SubstitutionTemplateMapping
@declared_attr
def substitution_template_fk(cls):
- return cls.foreign_key('substitution_template')
+ return cls._create_foreign_key('substitution_template')
# SubstitutionTemplate one-to-one to NodeTemplate
@declared_attr
def node_template_fk(cls):
- return cls.foreign_key('node_template')
+ return cls._create_foreign_key('node_template')
# SubstitutionTemplate one-to-one to CapabilityTemplate
@declared_attr
def capability_template_fk(cls):
- return cls.foreign_key('capability_template', nullable=True)
+ return cls._create_foreign_key('capability_template', nullable=True)
# SubstitutionTemplate one-to-one to RequirementTemplate
@declared_attr
def requirement_template_fk(cls):
- return cls.foreign_key('requirement_template', nullable=True)
+ return cls._create_foreign_key('requirement_template', nullable=True)
# endregion
@@ -965,24 +970,25 @@ class RequirementTemplateBase(TemplateModelMixin):
@declared_attr
def target_node_type(cls):
- return cls.many_to_one_relationship('type', key='target_node_type_fk', backreference='')
+ return cls._create_many_to_one_relationship('type', key='target_node_type_fk',
+ backreference='')
@declared_attr
def target_node_template(cls):
- return cls.one_to_one_relationship('node_template', key='target_node_template_fk',
- backreference='')
+ return cls._create_one_to_one_relationship('node_template', key='target_node_template_fk',
+ backreference='')
@declared_attr
def target_capability_type(cls):
- return cls.one_to_one_relationship('type', key='target_capability_type_fk',
- backreference='')
+ return cls._create_one_to_one_relationship('type', key='target_capability_type_fk',
+ backreference='')
target_capability_name = Column(Text)
target_node_template_constraints = Column(modeling_types.StrictList(FunctionType))
@declared_attr
def relationship_template(cls):
- return cls.one_to_one_relationship('relationship_template')
+ return cls._create_one_to_one_relationship('relationship_template')
# region foreign keys
@@ -995,27 +1001,27 @@ class RequirementTemplateBase(TemplateModelMixin):
# RequirementTemplate many-to-one to Type
@declared_attr
def target_node_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# RequirementTemplate one-to-one to NodeTemplate
@declared_attr
def target_node_template_fk(cls):
- return cls.foreign_key('node_template', nullable=True)
+ return cls._create_foreign_key('node_template', nullable=True)
# RequirementTemplate one-to-one to NodeTemplate
@declared_attr
def target_capability_type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# NodeTemplate one-to-many to RequirementTemplate
@declared_attr
def node_template_fk(cls):
- return cls.foreign_key('node_template')
+ return cls._create_foreign_key('node_template')
# RequirementTemplate one-to-one to RelationshipTemplate
@declared_attr
def relationship_template_fk(cls):
- return cls.foreign_key('relationship_template', nullable=True)
+ return cls._create_foreign_key('relationship_template', nullable=True)
# endregion
@@ -1146,18 +1152,18 @@ class RelationshipTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
@declared_attr
def interface_templates(cls):
- return cls.one_to_many_relationship('interface_template', dict_key='name')
+ return cls._create_one_to_many_relationship('interface_template', dict_key='name')
# region foreign keys
@@ -1166,7 +1172,7 @@ class RelationshipTemplateBase(TemplateModelMixin):
# RelationshipTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type', nullable=True)
+ return cls._create_foreign_key('type', nullable=True)
# endregion
@@ -1243,7 +1249,7 @@ class CapabilityTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
min_occurrences = Column(Integer, default=None) # optional
@@ -1251,12 +1257,12 @@ class CapabilityTemplateBase(TemplateModelMixin):
@declared_attr
def valid_source_node_types(cls):
- return cls.many_to_many_relationship('type', table_prefix='valid_sources')
+ return cls._create_many_to_many_relationship('type', table_prefix='valid_sources')
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
# region foreign keys
@@ -1266,12 +1272,12 @@ class CapabilityTemplateBase(TemplateModelMixin):
# CapabilityTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# NodeTemplate one-to-many to CapabilityTemplate
@declared_attr
def node_template_fk(cls):
- return cls.foreign_key('node_template')
+ return cls._create_foreign_key('node_template')
# endregion
@@ -1375,17 +1381,17 @@ class InterfaceTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
@declared_attr
def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
@declared_attr
def operation_templates(cls):
- return cls.one_to_many_relationship('operation_template', dict_key='name')
+ return cls._create_one_to_many_relationship('operation_template', dict_key='name')
# region foreign keys
@@ -1397,22 +1403,22 @@ class InterfaceTemplateBase(TemplateModelMixin):
# InterfaceTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# NodeTemplate one-to-many to InterfaceTemplate
@declared_attr
def node_template_fk(cls):
- return cls.foreign_key('node_template', nullable=True)
+ return cls._create_foreign_key('node_template', nullable=True)
# GroupTemplate one-to-many to InterfaceTemplate
@declared_attr
def group_template_fk(cls):
- return cls.foreign_key('group_template', nullable=True)
+ return cls._create_foreign_key('group_template', nullable=True)
# RelationshipTemplate one-to-many to InterfaceTemplate
@declared_attr
def relationship_template_fk(cls):
- return cls.foreign_key('relationship_template', nullable=True)
+ return cls._create_foreign_key('relationship_template', nullable=True)
# endregion
@@ -1458,14 +1464,14 @@ class OperationTemplateBase(TemplateModelMixin):
"""
An operation in a :class:`InterfaceTemplate`.
- Operations are executed by an associated :class:`Plugin` via an executor.
+ Operations are executed by an associated :class:`PluginSpecification` via an executor.
:ivar name: Name (unique for the interface or service template)
:vartype name: basestring
:ivar description: Human-readable description
:vartype description: basestring
- :ivar plugin: Associated plugin
- :vartype plugin: :class:`Plugin`
+ :ivar plugin_specification: Associated plugin
+ :vartype plugin_specification: :class:`PluginSpecification`
:ivar implementation: Implementation string (interpreted by the plugin)
:vartype implementation: basestring
:ivar dependencies: Dependency strings (interpreted by the plugin)
@@ -1492,16 +1498,16 @@ class OperationTemplateBase(TemplateModelMixin):
description = Column(Text)
@declared_attr
- def plugin(cls):
- return cls.one_to_one_relationship('plugin')
+ def plugin_specification(cls):
+ return cls._create_one_to_one_relationship('plugin_specification')
implementation = Column(Text)
dependencies = Column(modeling_types.StrictList(item_cls=basestring))
@declared_attr
def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='inputs',
+ dict_key='name')
executor = Column(Text)
max_retries = Column(Integer)
@@ -1516,17 +1522,17 @@ class OperationTemplateBase(TemplateModelMixin):
# ServiceTemplate one-to-many to OperationTemplate
@declared_attr
def service_template_fk(cls):
- return cls.foreign_key('service_template', nullable=True)
+ return cls._create_foreign_key('service_template', nullable=True)
# InterfaceTemplate one-to-many to OperationTemplate
@declared_attr
def interface_template_fk(cls):
- return cls.foreign_key('interface_template', nullable=True)
+ return cls._create_foreign_key('interface_template', nullable=True)
- # OperationTemplate one-to-one to Plugin
+ # OperationTemplate one-to-one to PluginSpecification
@declared_attr
- def plugin_fk(cls):
- return cls.foreign_key('plugin', nullable=True)
+ def plugin_specification_fk(cls):
+ return cls._create_foreign_key('plugin_specification', nullable=True)
# endregion
@@ -1548,7 +1554,7 @@ class OperationTemplateBase(TemplateModelMixin):
description=utils.deepcopy_with_locators(self.description),
implementation=self.implementation,
dependencies=self.dependencies,
- plugin=self.plugin,
+ plugin_specification=self.plugin_specification,
executor=self.executor,
max_retries=self.max_retries,
retry_interval=self.retry_interval,
@@ -1614,7 +1620,7 @@ class ArtifactTemplateBase(TemplateModelMixin):
@declared_attr
def type(cls):
- return cls.many_to_one_relationship('type')
+ return cls._create_many_to_one_relationship('type')
description = Column(Text)
source_path = Column(Text)
@@ -1624,8 +1630,8 @@ class ArtifactTemplateBase(TemplateModelMixin):
@declared_attr
def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
+ return cls._create_many_to_many_relationship('parameter', table_prefix='properties',
+ dict_key='name')
# region foreign keys
@@ -1635,12 +1641,12 @@ class ArtifactTemplateBase(TemplateModelMixin):
# ArtifactTemplate many-to-one to Type
@declared_attr
def type_fk(cls):
- return cls.foreign_key('type')
+ return cls._create_foreign_key('type')
# NodeTemplate one-to-many to ArtifactTemplate
@declared_attr
def node_template_fk(cls):
- return cls.foreign_key('node_template')
+ return cls._create_foreign_key('node_template')
# endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index d434da8..744d1b4 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -59,10 +59,7 @@ class OperationTask(BaseTask):
Represents an operation task in the task_graph
"""
- SOURCE_OPERATION = 'source'
- TARGET_OPERATION = 'target'
-
- NAME_FORMAT = '{type}:{id}->{interface}/{operation}'
+ NAME_FORMAT = '{interface}:{operation}@{type}:{id}'
def __init__(self,
name,
@@ -73,8 +70,7 @@ class OperationTask(BaseTask):
ignore_failure=None,
inputs=None,
plugin=None,
- runs_on=None,
- dry=False):
+ runs_on=None):
"""
Creates an operation task using the name, details, node instance and any additional kwargs.
@@ -84,12 +80,9 @@ class OperationTask(BaseTask):
"""
assert isinstance(actor, (models.Node, models.Relationship))
+ assert (runs_on is None) or (runs_on in models.Task.RUNS_ON)
super(OperationTask, self).__init__()
- if dry:
- from ..dry import convert_to_dry
- plugin, implementation, inputs = convert_to_dry(plugin, implementation, inputs)
-
# Coerce inputs
if inputs is None:
inputs = {}
@@ -131,13 +124,21 @@ class OperationTask(BaseTask):
'Could not find operation "{0}" on interface "{1}" for node "{2}"'.format(
operation_name, interface_name, node.name))
+ plugin = None
+ if operation.plugin_specification:
+ plugin = cls._find_plugin(operation.plugin_specification, kwargs)
+ if plugin is None:
+ raise exceptions.TaskException(
+ 'Could not find plugin of operation "{0}" on interface "{1}" for node "{2}"'
+ .format(operation_name, interface_name, node.name))
+
return cls(
actor=node,
name=cls.NAME_FORMAT.format(type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name),
- plugin=operation.plugin,
+ plugin=plugin,
implementation=operation.implementation,
inputs=cls._merge_inputs(operation.inputs, inputs),
runs_on=models.Task.RUNS_ON_NODE,
@@ -165,13 +166,21 @@ class OperationTask(BaseTask):
'Could not find operation "{0}" on interface "{1}" for relationship "{2}"'.format(
operation_name, interface_name, relationship.name))
+ plugin = None
+ if operation.plugin_specification:
+ plugin = cls._find_plugin(operation.plugin_specification, kwargs)
+ if plugin is None:
+ raise exceptions.TaskException(
+ 'Could not find plugin of operation "{0}" on interface "{1}" for relationship '
+ '"{2}"'.format(operation_name, interface_name, relationship.name))
+
return cls(
actor=relationship,
name=cls.NAME_FORMAT.format(type='relationship',
- id=relationship.id,
+ id=relationship.name,
interface=interface_name,
operation=operation_name),
- plugin=operation.plugin,
+ plugin=plugin,
implementation=operation.implementation,
inputs=cls._merge_inputs(operation.inputs, inputs),
runs_on=runs_on,
@@ -186,6 +195,13 @@ class OperationTask(BaseTask):
return None
@classmethod
+ def _find_plugin(cls, plugin_specification, kwargs):
+ workflow_context = kwargs.get('ctx') if kwargs else None
+ if workflow_context is None:
+ workflow_context = context.workflow.current.get()
+ return plugin_specification.find_plugin(workflow_context.model.plugin.list())
+
+ @classmethod
def _merge_inputs(cls, operation_inputs, override_inputs=None):
final_inputs = OrderedDict(operation_inputs)
if override_inputs:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/execute_operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py
index ed4ada3..348f47a 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -58,8 +58,7 @@ def execute_operation(
type_names=type_names))
if run_by_dependency_order:
- filtered_node_ids = set(node_instance.id
- for node_instance in filtered_nodes)
+ filtered_node_ids = set(node_instance.id for node_instance in filtered_nodes)
for node in ctx.nodes:
if node.id not in filtered_node_ids:
subgraphs[node.id] = ctx.task_graph(
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/utils.py b/aria/orchestrator/workflows/builtin/utils.py
index 045d47b..84d8293 100644
--- a/aria/orchestrator/workflows/builtin/utils.py
+++ b/aria/orchestrator/workflows/builtin/utils.py
@@ -17,7 +17,7 @@ from ..api.task import OperationTask
from .. import exceptions
-def create_node_task(interface_name, operation_name, node, dry=False):
+def create_node_task(interface_name, operation_name, node):
"""
Returns a new operation task if the operation exists in the node, otherwise returns None.
"""
@@ -25,14 +25,12 @@ def create_node_task(interface_name, operation_name, node, dry=False):
try:
return OperationTask.for_node(node=node,
interface_name=interface_name,
- operation_name=operation_name,
- dry=dry)
+ operation_name=operation_name)
except exceptions.TaskException:
- pass
- return None
+ return None
-def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry=False):
+def create_relationship_tasks(interface_name, operation_name, runs_on, node):
"""
Returns a list of operation tasks for each outbound relationship of the node if the operation
exists there.
@@ -45,8 +43,7 @@ def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry
OperationTask.for_relationship(relationship=relationship,
interface_name=interface_name,
operation_name=operation_name,
- runs_on=runs_on,
- dry=dry))
+ runs_on=runs_on))
except exceptions.TaskException:
pass
return sequence
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/workflows.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py
index f19c031..6065343 100644
--- a/aria/orchestrator/workflows/builtin/workflows.py
+++ b/aria/orchestrator/workflows/builtin/workflows.py
@@ -67,128 +67,108 @@ __all__ = (
@workflow(suffix_template='{node.name}')
def install_node(graph, node, **kwargs):
- dry = kwargs.get('dry', True)
-
sequence = []
# Create
sequence.append(
create_node_task(
NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CREATE,
- node,
- dry))
+ node))
# Configure
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_SOURCE,
Task.RUNS_ON_SOURCE,
- node,
- dry)
+ node)
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_TARGET,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
sequence.append(
create_node_task(
NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CONFIGURE,
- node,
- dry))
+ node))
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_SOURCE,
Task.RUNS_ON_SOURCE,
- node,
- dry)
+ node)
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_TARGET,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
# Start
- sequence += _create_start_tasks(node, dry)
+ sequence += _create_start_tasks(node)
graph.sequence(*sequence)
@workflow(suffix_template='{node.name}')
def uninstall_node(graph, node, **kwargs):
- dry = kwargs.get('dry', True)
-
# Stop
- sequence = _create_stop_tasks(node, dry)
+ sequence = _create_stop_tasks(node)
# Delete
sequence.append(
create_node_task(
NORMATIVE_STANDARD_INTERFACE, NORMATIVE_DELETE,
- node,
- dry))
+ node))
graph.sequence(*sequence)
@workflow(suffix_template='{node.name}')
def start_node(graph, node, **kwargs):
- dry = kwargs.get('dry', True)
- graph.sequence(*_create_start_tasks(node, dry))
+ graph.sequence(*_create_start_tasks(node))
@workflow(suffix_template='{node.name}')
def stop_node(graph, node, **kwargs):
- dry = kwargs.get('dry', True)
- graph.sequence(*_create_stop_tasks(node, dry))
+ graph.sequence(*_create_stop_tasks(node))
-def _create_start_tasks(node, dry):
+def _create_start_tasks(node):
sequence = []
sequence.append(
create_node_task(
NORMATIVE_STANDARD_INTERFACE, NORMATIVE_START,
- node,
- dry))
+ node))
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_SOURCE,
Task.RUNS_ON_SOURCE,
- node,
- dry)
+ node)
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_TARGET,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
return sequence
-def _create_stop_tasks(node, dry):
+def _create_stop_tasks(node):
sequence = []
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_REMOVE_TARGET,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
sequence += \
create_relationship_tasks(
NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED,
Task.RUNS_ON_TARGET,
- node,
- dry)
+ node)
sequence.append(
create_node_task(
NORMATIVE_STANDARD_INTERFACE, NORMATIVE_STOP,
- node,
- dry))
+ node))
return sequence
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/core/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py
index 7d8380c..64f2818 100644
--- a/aria/orchestrator/workflows/core/task.py
+++ b/aria/orchestrator/workflows/core/task.py
@@ -113,15 +113,15 @@ class OperationTask(BaseTask):
base_task_model = model_storage.task.model_cls
if isinstance(api_task.actor, models.Node):
context_cls = operation_context.NodeOperationContext
- task_model_cls = base_task_model.as_node_task
+ create_task_model = base_task_model.for_node
elif isinstance(api_task.actor, models.Relationship):
context_cls = operation_context.RelationshipOperationContext
- task_model_cls = base_task_model.as_relationship_task
+ create_task_model = base_task_model.for_relationship
else:
raise RuntimeError('No operation context could be created for {actor.model_cls}'
.format(actor=api_task.actor))
- operation_task = task_model_cls(
+ task_model = create_task_model(
name=api_task.name,
implementation=api_task.implementation,
instance=api_task.actor,
@@ -131,20 +131,19 @@ class OperationTask(BaseTask):
retry_interval=api_task.retry_interval,
ignore_failure=api_task.ignore_failure,
plugin=plugin,
- plugin_name=plugin.name if plugin is not None else 'execution',
execution=self._workflow_context.execution,
runs_on=api_task.runs_on
)
- self._workflow_context.model.task.put(operation_task)
+ self._workflow_context.model.task.put(task_model)
self._ctx = context_cls(name=api_task.name,
model_storage=self._workflow_context.model,
resource_storage=self._workflow_context.resource,
service_id=self._workflow_context._service_id,
- task_id=operation_task.id,
+ task_id=task_model.id,
actor_id=api_task.actor.id,
workdir=self._workflow_context._workdir)
- self._task_id = operation_task.id
+ self._task_id = task_model.id
self._update_fields = None
@contextmanager
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/dry.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/dry.py b/aria/orchestrator/workflows/dry.py
deleted file mode 100644
index 766ea0c..0000000
--- a/aria/orchestrator/workflows/dry.py
+++ /dev/null
@@ -1,53 +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 threading import RLock
-
-from ..decorators import operation
-from ...utils.collections import OrderedDict
-from ...utils.console import puts, Colored
-from ...utils.formatting import safe_repr
-
-
-_TERMINAL_LOCK = RLock()
-
-
-def convert_to_dry(plugin, implementation, inputs): # pylint: disable=unused-argument
- dry_implementation = '{0}.{1}'.format(__name__, 'dry_operation')
- dry_inputs = OrderedDict()
- dry_inputs['_implementation'] = implementation
- dry_inputs['_plugin'] = plugin.name if plugin is not None else None
- return None, dry_implementation, dry_inputs
-
-
-@operation
-def dry_operation(ctx, _plugin, _implementation, **kwargs):
- with _TERMINAL_LOCK:
- print ctx.name
- if hasattr(ctx, 'relationship'):
- puts('> Relationship: {0} -> {1}'.format(
- Colored.red(ctx.relationship.source_node.name),
- Colored.red(ctx.relationship.target_node.name)))
- else:
- puts('> Node: {0}'.format(Colored.red(ctx.node.name)))
- puts(' Operation: {0}'.format(Colored.green(ctx.name)))
- _dump_implementation(_plugin, _implementation)
-
-
-def _dump_implementation(plugin, implementation):
- if plugin:
- puts(' Plugin: {0}'.format(Colored.magenta(plugin, bold=True)))
- if implementation:
- puts(' Implementation: {0}'.format(Colored.magenta(safe_repr(implementation))))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
index 0206e03..a7b2a11 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -27,7 +27,7 @@ from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate,
RequirementTemplate, RelationshipTemplate, CapabilityTemplate,
GroupTemplate, PolicyTemplate, SubstitutionTemplate,
SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate,
- ArtifactTemplate, Metadata, Parameter, Plugin)
+ ArtifactTemplate, Metadata, Parameter, PluginSpecification)
from ..data_types import coerce_value
@@ -81,12 +81,13 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to
create_parameter_models_from_values(model.outputs,
topology_template._get_output_values(context))
- # Plugins
+ # Plugin specifications
policies = context.presentation.get('service_template', 'topology_template', 'policies')
if policies:
for policy in policies.itervalues():
if model.policy_types.get_descendant(policy.type).role == 'plugin':
- model.plugins.append(create_plugin_model(context, policy))
+ model.plugin_specifications.append(
+ create_plugin_specification_model(context, policy))
# Node templates
node_templates = context.presentation.get('service_template', 'topology_template',
@@ -349,7 +350,7 @@ def create_operation_template_model(context, service_template, operation): # pyl
implementation = operation.implementation
if (implementation is not None) and operation.implementation.primary:
- model.plugin, model.implementation = \
+ model.plugin_specification, model.implementation = \
parse_implementation_string(context, service_template, operation.implementation.primary)
dependencies = implementation.dependencies
@@ -427,7 +428,7 @@ def create_substitution_template_model(context, service_template, substitution_m
return model
-def create_plugin_model(context, policy):
+def create_plugin_specification_model(context, policy):
properties = policy.properties
def get(name):
@@ -436,18 +437,16 @@ def create_plugin_model(context, policy):
now = datetime.now()
- model = Plugin(name=policy._name,
- archive_name=get('archive_name') or '',
- distribution=get('distribution'),
- distribution_release=get('distribution_release'),
- distribution_version=get('distribution_version'),
- package_name=get('package_name') or '',
- package_source=get('package_source'),
- package_version=get('package_version'),
- supported_platform=get('supported_platform'),
- supported_py_versions=get('supported_py_versions'),
- uploaded_at=now,
- wheels=get('wheels') or [])
+ model = PluginSpecification(name=policy._name,
+ archive_name=get('archive_name') or '',
+ distribution=get('distribution'),
+ distribution_release=get('distribution_release'),
+ distribution_version=get('distribution_version'),
+ package_name=get('package_name') or '',
+ package_source=get('package_source'),
+ package_version=get('package_version'),
+ supported_platform=get('supported_platform'),
+ supported_py_versions=get('supported_py_versions'))
return model
@@ -665,15 +664,15 @@ def parse_implementation_string(context, service_template, implementation):
plugin_name = implementation[:index].strip()
if plugin_name == 'execution':
- plugin = None
+ plugin_specification = None
else:
- plugin = None
- for the_plugin in service_template.plugins:
- if the_plugin.name == plugin_name:
- plugin = the_plugin
+ plugin_specification = None
+ for a_plugin_specification in service_template.plugin_specifications:
+ if a_plugin_specification.name == plugin_name:
+ plugin_specification = a_plugin_specification
break
- if plugin is None:
+ if plugin_specification is None:
raise ValueError('unknown plugin: "{0}"'.format(plugin_name))
implementation = implementation[index+1:].strip()
- return plugin, implementation
+ return plugin_specification, implementation
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/end2end/test_orchestrator.py
----------------------------------------------------------------------
diff --git a/tests/end2end/test_orchestrator.py b/tests/end2end/test_orchestrator.py
index fac6207..f25134f 100644
--- a/tests/end2end/test_orchestrator.py
+++ b/tests/end2end/test_orchestrator.py
@@ -19,6 +19,7 @@ from aria.orchestrator.runner import Runner
from aria.orchestrator.workflows.builtin import BUILTIN_WORKFLOWS
from aria.utils.imports import import_fullname
from aria.utils.collections import OrderedDict
+from aria.cli.dry import convert_to_dry
from tests.parser.service_templates import consume_node_cellar
@@ -37,6 +38,8 @@ def test_custom():
def _workflow(workflow_name):
context, _ = consume_node_cellar()
+ convert_to_dry(context.modeling.instance)
+
# TODO: this logic will eventually stabilize and be part of the ARIA API,
# likely somewhere in aria.orchestrator.workflows
if workflow_name in BUILTIN_WORKFLOWS:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 716254e..483da7d 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -191,6 +191,7 @@ def create_execution(service):
def create_plugin(package_name='package', package_version='0.1'):
return models.Plugin(
+ name='test_plugin',
archive_name='archive_name',
distribution='distribution',
distribution_release='dist_release',
@@ -205,5 +206,20 @@ def create_plugin(package_name='package', package_version='0.1'):
)
+def create_plugin_specification(package_name='package', package_version='0.1'):
+ return models.PluginSpecification(
+ name='test_plugin',
+ archive_name='archive_name',
+ distribution='distribution',
+ distribution_release='dist_release',
+ distribution_version='dist_version',
+ package_name=package_name,
+ package_source='source',
+ package_version=package_version,
+ supported_platform='any',
+ supported_py_versions=['python27']
+ )
+
+
def _dictify(item):
return dict(((item.name, item),))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/tests/modeling/__init__.py b/tests/modeling/__init__.py
new file mode 100644
index 0000000..072ef54
--- /dev/null
+++ b/tests/modeling/__init__.py
@@ -0,0 +1,34 @@
+# 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 sqlalchemy import (
+ Column,
+ Text,
+ Integer,
+)
+
+from aria.modeling import (
+ models,
+ types as modeling_types,
+ mixins
+)
+
+
+class MockModel(models.aria_declarative_base, mixins.ModelMixin): #pylint: disable=abstract-method
+ __tablename__ = 'mock_model'
+ model_dict = Column(modeling_types.Dict)
+ model_list = Column(modeling_types.List)
+ value = Column(Integer)
+ name = Column(Text)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_mixins.py
----------------------------------------------------------------------
diff --git a/tests/modeling/test_mixins.py b/tests/modeling/test_mixins.py
new file mode 100644
index 0000000..a60412f
--- /dev/null
+++ b/tests/modeling/test_mixins.py
@@ -0,0 +1,219 @@
+# 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.
+
+import pytest
+
+import sqlalchemy
+
+from aria.storage import (
+ ModelStorage,
+ sql_mapi,
+ exceptions
+)
+from aria import modeling
+
+from ..storage import (
+ release_sqlite_storage,
+ init_inmemory_model_storage
+)
+from . import MockModel
+from ..mock import (
+ models,
+ context as mock_context
+)
+
+
+@pytest.fixture
+def storage():
+ base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+ initiator=init_inmemory_model_storage)
+ base_storage.register(MockModel)
+ yield base_storage
+ release_sqlite_storage(base_storage)
+
+
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+ modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) # pylint: disable=no-member
+
+
+@pytest.fixture
+def context(tmpdir):
+ ctx = mock_context.simple(str(tmpdir))
+ yield ctx
+ release_sqlite_storage(ctx.model)
+
+
+def test_inner_dict_update(storage):
+ inner_dict = {'inner_value': 1}
+
+ mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
+ storage.mock_model.put(mock_model)
+
+ storage_mm = storage.mock_model.get(mock_model.id)
+ assert storage_mm == mock_model
+
+ storage_mm.model_dict['inner_dict']['inner_value'] = 2
+ storage_mm.model_dict['value'] = -1
+ storage.mock_model.update(storage_mm)
+ storage_mm = storage.mock_model.get(storage_mm.id)
+
+ assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
+ assert storage_mm.model_dict['value'] == -1
+
+
+def test_inner_list_update(storage):
+ mock_model = MockModel(model_list=[0, [1]])
+ storage.mock_model.put(mock_model)
+
+ storage_mm = storage.mock_model.get(mock_model.id)
+ assert storage_mm == mock_model
+
+ storage_mm.model_list[1][0] = 'new_inner_value'
+ storage_mm.model_list[0] = 'new_value'
+ storage.mock_model.update(storage_mm)
+ storage_mm = storage.mock_model.get(storage_mm.id)
+
+ assert storage_mm.model_list[1][0] == 'new_inner_value'
+ assert storage_mm.model_list[0] == 'new_value'
+
+
+def test_model_to_dict(context):
+ service = context.service
+ service = service.to_dict()
+
+ expected_keys = [
+ 'description',
+ 'created_at',
+ 'permalink',
+ 'scaling_groups',
+ 'updated_at'
+ ]
+
+ for expected_key in expected_keys:
+ assert expected_key in service
+
+
+def test_relationship_model_ordering(context):
+ service = context.model.service.get_by_name(models.SERVICE_NAME)
+ source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
+ target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
+
+ new_node_template = modeling.models.NodeTemplate(
+ name='new_node_template',
+ type=source_node.type,
+ default_instances=1,
+ min_instances=1,
+ max_instances=1,
+ service_template=service.service_template
+ )
+
+ new_node = modeling.models.Node(
+ name='new_node',
+ type=source_node.type,
+ runtime_properties={},
+ service=service,
+ version=None,
+ node_template=new_node_template,
+ state='',
+ scaling_groups=[]
+ )
+
+ source_node.outbound_relationships.append(modeling.models.Relationship(
+ source_node=source_node,
+ target_node=new_node,
+ ))
+
+ new_node.outbound_relationships.append(modeling.models.Relationship( # pylint: disable=no-member
+ source_node=new_node,
+ target_node=target_node,
+ ))
+
+ context.model.node_template.put(new_node_template)
+ context.model.node.put(new_node)
+ context.model.node.refresh(source_node)
+ context.model.node.refresh(target_node)
+
+ def flip_and_assert(node, direction):
+ """
+ Reversed the order of relationships and assert effects took place.
+ :param node: the node instance to operate on
+ :param direction: the type of relationships to flip (inbound/outbound)
+ :return:
+ """
+ assert direction in ('inbound', 'outbound')
+
+ relationships = getattr(node, direction + '_relationships')
+ assert len(relationships) == 2
+
+ reversed_relationship = list(reversed(relationships))
+ assert relationships != reversed_relationship
+
+ relationships[:] = reversed_relationship
+ context.model.node.update(node)
+ assert relationships == reversed_relationship
+
+ flip_and_assert(source_node, 'outbound')
+ flip_and_assert(target_node, 'inbound')
+
+
+class StrictClass(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin):
+ __tablename__ = 'strict_class'
+
+ strict_dict = sqlalchemy.Column(modeling.types.StrictDict(basestring, basestring))
+ strict_list = sqlalchemy.Column(modeling.types.StrictList(basestring))
+
+
+def test_strict_dict():
+
+ strict_class = StrictClass()
+
+ def assert_strict(sc):
+ with pytest.raises(exceptions.StorageError):
+ sc.strict_dict = {'key': 1}
+
+ with pytest.raises(exceptions.StorageError):
+ sc.strict_dict = {1: 'value'}
+
+ with pytest.raises(exceptions.StorageError):
+ sc.strict_dict = {1: 1}
+
+ assert_strict(strict_class)
+ strict_class.strict_dict = {'key': 'value'}
+ assert strict_class.strict_dict == {'key': 'value'}
+
+ assert_strict(strict_class)
+ with pytest.raises(exceptions.StorageError):
+ strict_class.strict_dict['key'] = 1
+ with pytest.raises(exceptions.StorageError):
+ strict_class.strict_dict[1] = 'value'
+ with pytest.raises(exceptions.StorageError):
+ strict_class.strict_dict[1] = 1
+
+
+def test_strict_list():
+ strict_class = StrictClass()
+
+ def assert_strict(sc):
+ with pytest.raises(exceptions.StorageError):
+ sc.strict_list = [1]
+
+ assert_strict(strict_class)
+ strict_class.strict_list = ['item']
+ assert strict_class.strict_list == ['item']
+
+ assert_strict(strict_class)
+ with pytest.raises(exceptions.StorageError):
+ strict_class.strict_list[0] = 1
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/modeling/test_model_storage.py b/tests/modeling/test_model_storage.py
new file mode 100644
index 0000000..bb778d4
--- /dev/null
+++ b/tests/modeling/test_model_storage.py
@@ -0,0 +1,102 @@
+# 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.
+
+import pytest
+
+from aria.storage import (
+ ModelStorage,
+ exceptions,
+ sql_mapi
+)
+from aria import (application_model_storage, modeling)
+from ..storage import (release_sqlite_storage, init_inmemory_model_storage)
+
+from . import MockModel
+
+
+@pytest.fixture
+def storage():
+ base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+ initiator=init_inmemory_model_storage)
+ base_storage.register(MockModel)
+ yield base_storage
+ release_sqlite_storage(base_storage)
+
+
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+ modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member
+
+
+def test_storage_base(storage):
+ with pytest.raises(AttributeError):
+ storage.non_existent_attribute()
+
+
+def test_model_storage(storage):
+ mock_model = MockModel(value=0, name='model_name')
+ storage.mock_model.put(mock_model)
+
+ assert storage.mock_model.get_by_name('model_name') == mock_model
+
+ assert [mm_from_storage for mm_from_storage in storage.mock_model.iter()] == [mock_model]
+ assert [mm_from_storage for mm_from_storage in storage.mock_model] == [mock_model]
+
+ storage.mock_model.delete(mock_model)
+ with pytest.raises(exceptions.StorageError):
+ storage.mock_model.get(mock_model.id)
+
+
+def test_application_storage_factory():
+ storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
+ initiator=init_inmemory_model_storage)
+
+ assert storage.service_template
+ assert storage.node_template
+ assert storage.group_template
+ assert storage.policy_template
+ assert storage.substitution_template
+ assert storage.substitution_template_mapping
+ assert storage.requirement_template
+ assert storage.relationship_template
+ assert storage.capability_template
+ assert storage.interface_template
+ assert storage.operation_template
+ assert storage.artifact_template
+
+ assert storage.service
+ assert storage.node
+ assert storage.group
+ assert storage.policy
+ assert storage.substitution
+ assert storage.substitution_mapping
+ assert storage.relationship
+ assert storage.capability
+ assert storage.interface
+ assert storage.operation
+ assert storage.artifact
+
+ assert storage.execution
+ assert storage.service_update
+ assert storage.service_update_step
+ assert storage.service_modification
+ assert storage.plugin
+ assert storage.task
+
+ assert storage.parameter
+ assert storage.type
+ assert storage.metadata
+
+ release_sqlite_storage(storage)
[2/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/storage/test_models.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py
deleted file mode 100644
index c80659b..0000000
--- a/tests/storage/test_models.py
+++ /dev/null
@@ -1,833 +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 datetime import datetime
-from contextlib import contextmanager
-
-import pytest
-
-from aria import application_model_storage
-from aria.storage import (
- exceptions,
- sql_mapi,
-)
-from aria.modeling.models import (
- ServiceTemplate,
- Service,
- ServiceUpdate,
- ServiceUpdateStep,
- ServiceModification,
- Execution,
- Task,
- Plugin,
- Relationship,
- NodeTemplate,
- Node,
- Parameter,
- Type
-)
-
-from tests import mock
-from ..storage import release_sqlite_storage, init_inmemory_model_storage
-
-
-@contextmanager
-def sql_storage(storage_func):
- storage = None
- try:
- storage = storage_func()
- yield storage
- finally:
- if storage:
- release_sqlite_storage(storage)
-
-
-def _empty_storage():
- return application_model_storage(sql_mapi.SQLAlchemyModelAPI,
- initiator=init_inmemory_model_storage)
-
-
-def _service_template_storage():
- storage = _empty_storage()
- service_template = mock.models.create_service_template()
- storage.service_template.put(service_template)
- storage.type.put(Type(variant='node'))
- return storage
-
-
-def _service_storage():
- storage = _service_template_storage()
- service = mock.models.create_service(
- storage.service_template.get_by_name(mock.models.SERVICE_TEMPLATE_NAME))
- storage.service.put(service)
- return storage
-
-
-def _service_update_storage():
- storage = _service_storage()
- service_update = ServiceUpdate(
- service=storage.service.list()[0],
- created_at=now,
- service_plan={},
- )
- storage.service_update.put(service_update)
- return storage
-
-
-def _node_template_storage():
- storage = _service_storage()
- service_template = storage.service_template.list()[0]
- node_template = mock.models.create_dependency_node_template(service_template)
- storage.node_template.put(node_template)
- return storage
-
-
-def _nodes_storage():
- storage = _nodes_storage() # ???
- service = storage.service.get_by_name(mock.models.SERVICE_NAME)
- dependent_node_template = storage.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- dependency_node_template = storage.node_template.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- dependency_node = mock.models.create_dependency_node(dependency_node_template, service)
- dependent_node = mock.models.create_dependent_node(dependent_node_template, service)
- storage.node.put(dependency_node)
- storage.node.put(dependent_node)
- return storage
-
-
-def _execution_storage():
- storage = _service_storage()
- execution = mock.models.create_execution(storage.service.list()[0])
- plugin = mock.models.create_plugin()
- storage.execution.put(execution)
- storage.plugin.put(plugin)
- return storage
-
-
-@pytest.fixture
-def empty_storage():
- with sql_storage(_empty_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def service_template_storage():
- with sql_storage(_service_template_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def service_storage():
- with sql_storage(_service_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def service_update_storage():
- with sql_storage(_service_update_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def node_template_storage():
- with sql_storage(_node_template_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def nodes_storage():
- with sql_storage(_nodes_storage) as storage:
- yield storage
-
-
-@pytest.fixture
-def execution_storage():
- with sql_storage(_execution_storage) as storage:
- yield storage
-
-
-m_cls = type('MockClass')
-now = datetime.utcnow()
-
-
-def _test_model(is_valid, storage, model_cls, model_kwargs):
- if is_valid:
- model = model_cls(**model_kwargs)
- getattr(storage, model_cls.__modelname__).put(model)
- return model
- else:
- with pytest.raises((exceptions.StorageError, TypeError),):
- getattr(storage, model_cls.__modelname__).put(model_cls(**model_kwargs))
-
-
-class TestServiceTemplate(object):
-
- @pytest.mark.parametrize(
- 'is_valid, description, created_at, updated_at, main_file_name',
- [
- (True, 'description', now, now, '/path'),
- (False, {}, now, now, '/path'),
- (False, 'description', 'error', now, '/path'),
- (False, 'description', now, 'error', '/path'),
- (False, 'description', now, now, {}),
- (True, 'description', now, now, '/path'),
- ]
- )
-
- def test_service_template_model_creation(self, empty_storage, is_valid, description, created_at,
- updated_at, main_file_name):
- _test_model(is_valid=is_valid,
- storage=empty_storage,
- model_cls=ServiceTemplate,
- model_kwargs=dict(
- description=description,
- created_at=created_at,
- updated_at=updated_at,
- main_file_name=main_file_name)
- )
-
-
-class TestService(object):
-
- @pytest.mark.parametrize(
- 'is_valid, name, created_at, description, inputs, permalink, '
- 'outputs, scaling_groups, updated_at',
- [
- (False, m_cls, now, 'desc', {}, 'perlnk', {}, {}, now),
- (False, 'name', m_cls, 'desc', {}, 'perlnk', {}, {}, now),
- (False, 'name', now, m_cls, {}, 'perlnk', {}, {}, now),
- (False, 'name', now, 'desc', {}, m_cls, {}, {}, now),
- (False, 'name', now, 'desc', {}, 'perlnk', m_cls, {}, now),
- (False, 'name', now, 'desc', {}, 'perlnk', {}, m_cls, now),
- (False, 'name', now, 'desc', {}, 'perlnk', {}, {}, m_cls),
-
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
- (True, None, now, 'desc', {}, 'perlnk', {}, {}, now),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
- (True, 'name', now, None, {}, 'perlnk', {}, {}, now),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
- (True, 'name', now, 'desc', {}, None, {}, {}, now),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, None, now),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, None),
- (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
- ]
- )
- def test_service_model_creation(self, service_storage, is_valid, name, created_at, description,
- inputs, permalink, outputs, scaling_groups, updated_at):
- service = _test_model(
- is_valid=is_valid,
- storage=service_storage,
- model_cls=Service,
- model_kwargs=dict(
- name=name,
- service_template=service_storage.service_template.list()[0],
- created_at=created_at,
- description=description,
- inputs=inputs,
- permalink=permalink,
- outputs=outputs,
- scaling_groups=scaling_groups,
- updated_at=updated_at
- ))
- if is_valid:
- assert service.service_template == \
- service_storage.service_template.list()[0]
-
-
-class TestExecution(object):
-
- @pytest.mark.parametrize(
- 'is_valid, created_at, started_at, ended_at, error, is_system_workflow, parameters, '
- 'status, workflow_name',
- [
- (False, m_cls, now, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (False, now, m_cls, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (False, now, now, m_cls, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (False, now, now, now, m_cls, False, {}, Execution.STARTED, 'wf_name'),
- (False, now, now, now, 'error', False, m_cls, Execution.STARTED, 'wf_name'),
- (False, now, now, now, 'error', False, {}, m_cls, 'wf_name'),
- (False, now, now, now, 'error', False, {}, Execution.STARTED, m_cls),
-
- (True, now, now, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (True, now, None, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (True, now, now, None, 'error', False, {}, Execution.STARTED, 'wf_name'),
- (True, now, now, now, None, False, {}, Execution.STARTED, 'wf_name'),
- (True, now, now, now, 'error', False, None, Execution.STARTED, 'wf_name'),
- ]
- )
- def test_execution_model_creation(self, service_storage, is_valid, created_at, started_at,
- ended_at, error, is_system_workflow, parameters, status,
- workflow_name):
- execution = _test_model(
- is_valid=is_valid,
- storage=service_storage,
- model_cls=Execution,
- model_kwargs=dict(
- service=service_storage.service.list()[0],
- created_at=created_at,
- started_at=started_at,
- ended_at=ended_at,
- error=error,
- is_system_workflow=is_system_workflow,
- parameters=parameters,
- status=status,
- workflow_name=workflow_name,
- ))
- if is_valid:
- assert execution.service == service_storage.service.list()[0]
- assert execution.service_template == service_storage.service_template.list()[0]
-
- def test_execution_status_transition(self):
- def create_execution(status):
- execution = Execution(
- id='e_id',
- workflow_name='w_name',
- status=status,
- parameters={},
- created_at=now,
- )
- return execution
-
- valid_transitions = {
- Execution.PENDING: [Execution.STARTED,
- Execution.CANCELLED,
- Execution.PENDING],
- Execution.STARTED: [Execution.FAILED,
- Execution.TERMINATED,
- Execution.CANCELLED,
- Execution.CANCELLING,
- Execution.STARTED],
- Execution.CANCELLING: [Execution.FAILED,
- Execution.TERMINATED,
- Execution.CANCELLED,
- Execution.CANCELLING],
- Execution.FAILED: [Execution.FAILED],
- Execution.TERMINATED: [Execution.TERMINATED],
- Execution.CANCELLED: [Execution.CANCELLED]
- }
-
- invalid_transitions = {
- Execution.PENDING: [Execution.FAILED,
- Execution.TERMINATED,
- Execution.CANCELLING],
- Execution.STARTED: [Execution.PENDING],
- Execution.CANCELLING: [Execution.PENDING,
- Execution.STARTED],
- Execution.FAILED: [Execution.PENDING,
- Execution.STARTED,
- Execution.TERMINATED,
- Execution.CANCELLED,
- Execution.CANCELLING],
- Execution.TERMINATED: [Execution.PENDING,
- Execution.STARTED,
- Execution.FAILED,
- Execution.CANCELLED,
- Execution.CANCELLING],
- Execution.CANCELLED: [Execution.PENDING,
- Execution.STARTED,
- Execution.FAILED,
- Execution.TERMINATED,
- Execution.CANCELLING],
- }
-
- for current_status, valid_transitioned_statues in valid_transitions.items():
- for transitioned_status in valid_transitioned_statues:
- execution = create_execution(current_status)
- execution.status = transitioned_status
-
- for current_status, invalid_transitioned_statues in invalid_transitions.items():
- for transitioned_status in invalid_transitioned_statues:
- execution = create_execution(current_status)
- with pytest.raises(ValueError):
- execution.status = transitioned_status
-
-
-class TestServiceUpdate(object):
- @pytest.mark.parametrize(
- 'is_valid, created_at, service_plan, service_update_nodes, '
- 'service_update_service, service_update_node_templates, '
- 'modified_entity_ids, state',
- [
- (False, m_cls, {}, {}, {}, [], {}, 'state'),
- (False, now, m_cls, {}, {}, [], {}, 'state'),
- (False, now, {}, m_cls, {}, [], {}, 'state'),
- (False, now, {}, {}, m_cls, [], {}, 'state'),
- (False, now, {}, {}, {}, m_cls, {}, 'state'),
- (False, now, {}, {}, {}, [], m_cls, 'state'),
- (False, now, {}, {}, {}, [], {}, m_cls),
-
- (True, now, {}, {}, {}, [], {}, 'state'),
- (True, now, {}, None, {}, [], {}, 'state'),
- (True, now, {}, {}, None, [], {}, 'state'),
- (True, now, {}, {}, {}, None, {}, 'state'),
- (True, now, {}, {}, {}, [], None, 'state'),
- (True, now, {}, {}, {}, [], {}, None),
- ]
- )
- def test_service_update_model_creation(self, service_storage, is_valid, created_at,
- service_plan, service_update_nodes,
- service_update_service, service_update_node_templates,
- modified_entity_ids, state):
- service_update = _test_model(
- is_valid=is_valid,
- storage=service_storage,
- model_cls=ServiceUpdate,
- model_kwargs=dict(
- service=service_storage.service.list()[0],
- created_at=created_at,
- service_plan=service_plan,
- service_update_nodes=service_update_nodes,
- service_update_service=service_update_service,
- service_update_node_templates=service_update_node_templates,
- modified_entity_ids=modified_entity_ids,
- state=state
- ))
- if is_valid:
- assert service_update.service == \
- service_storage.service.list()[0]
-
-
-class TestServiceUpdateStep(object):
-
- @pytest.mark.parametrize(
- 'is_valid, action, entity_id, entity_type',
- [
- (False, m_cls, 'id', ServiceUpdateStep.ENTITY_TYPES.NODE),
- (False, ServiceUpdateStep.ACTION_TYPES.ADD, m_cls,
- ServiceUpdateStep.ENTITY_TYPES.NODE),
- (False, ServiceUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
-
- (True, ServiceUpdateStep.ACTION_TYPES.ADD, 'id',
- ServiceUpdateStep.ENTITY_TYPES.NODE)
- ]
- )
- def test_service_update_step_model_creation(self, service_update_storage, is_valid, action,
- entity_id, entity_type):
- service_update_step = _test_model(
- is_valid=is_valid,
- storage=service_update_storage,
- model_cls=ServiceUpdateStep,
- model_kwargs=dict(
- service_update=
- service_update_storage.service_update.list()[0],
- action=action,
- entity_id=entity_id,
- entity_type=entity_type
- ))
- if is_valid:
- assert service_update_step.service_update == \
- service_update_storage.service_update.list()[0]
-
- def test_service_update_step_order(self):
- add_node = ServiceUpdateStep(
- id='add_step',
- action='add',
- entity_type='node',
- entity_id='node_id')
-
- modify_node = ServiceUpdateStep(
- id='modify_step',
- action='modify',
- entity_type='node',
- entity_id='node_id')
-
- remove_node = ServiceUpdateStep(
- id='remove_step',
- action='remove',
- entity_type='node',
- entity_id='node_id')
-
- for step in (add_node, modify_node, remove_node):
- assert hash((step.id, step.entity_id)) == hash(step)
-
- assert remove_node < modify_node < add_node
- assert not remove_node > modify_node > add_node
-
- add_rel = ServiceUpdateStep(
- id='add_step',
- action='add',
- entity_type='relationship',
- entity_id='relationship_id')
-
- remove_rel = ServiceUpdateStep(
- id='remove_step',
- action='remove',
- entity_type='relationship',
- entity_id='relationship_id')
-
- assert remove_rel < remove_node < add_node < add_rel
- assert not add_node < None
-
-
-class TestServiceModification(object):
- @pytest.mark.parametrize(
- 'is_valid, context, created_at, ended_at, modified_node_templates, nodes, status',
- [
- (False, m_cls, now, now, {}, {}, ServiceModification.STARTED),
- (False, {}, m_cls, now, {}, {}, ServiceModification.STARTED),
- (False, {}, now, m_cls, {}, {}, ServiceModification.STARTED),
- (False, {}, now, now, m_cls, {}, ServiceModification.STARTED),
- (False, {}, now, now, {}, m_cls, ServiceModification.STARTED),
- (False, {}, now, now, {}, {}, m_cls),
-
- (True, {}, now, now, {}, {}, ServiceModification.STARTED),
- (True, {}, now, None, {}, {}, ServiceModification.STARTED),
- (True, {}, now, now, None, {}, ServiceModification.STARTED),
- (True, {}, now, now, {}, None, ServiceModification.STARTED),
- ]
- )
- def test_service_modification_model_creation(self, service_storage, is_valid, context,
- created_at, ended_at, modified_node_templates,
- nodes, status):
- service_modification = _test_model(
- is_valid=is_valid,
- storage=service_storage,
- model_cls=ServiceModification,
- model_kwargs=dict(
- service=service_storage.service.list()[0],
- context=context,
- created_at=created_at,
- ended_at=ended_at,
- modified_node_templates=modified_node_templates,
- nodes=nodes,
- status=status,
- ))
- if is_valid:
- assert service_modification.service == \
- service_storage.service.list()[0]
-
-
-class TestNodeTemplate(object):
- @pytest.mark.parametrize(
- 'is_valid, name, default_instances, max_instances, min_instances, plugins, properties',
- [
- (False, m_cls, 1, 1, 1, [], {}),
- (False, 'name', m_cls, 1, 1, [], {}),
- (False, 'name', 1, m_cls, 1, [], {}),
- (False, 'name', 1, 1, m_cls, [], {}),
- (False, 'name', 1, 1, 1, m_cls, {}),
- (False, 'name', 1, 1, 1, None, {}),
-
- (True, 'name', 1, 1, 1, [], {}),
- ]
- )
- def test_node_template_model_creation(self, service_storage, is_valid, name, default_instances,
- max_instances, min_instances, plugins, properties):
- node_template = _test_model(
- is_valid=is_valid,
- storage=service_storage,
- model_cls=NodeTemplate,
- model_kwargs=dict(
- name=name,
- type=service_storage.type.list()[0],
- default_instances=default_instances,
- max_instances=max_instances,
- min_instances=min_instances,
- plugins=plugins,
- properties=properties,
- service_template=service_storage.service_template.list()[0]
- ))
- if is_valid:
- assert node_template.service_template == \
- service_storage.service_template.list()[0]
-
-
-class TestNode(object):
- @pytest.mark.parametrize(
- 'is_valid, name, runtime_properties, scaling_groups, state, version',
- [
- (False, m_cls, {}, [], 'state', 1),
- (False, 'name', m_cls, [], 'state', 1),
- (False, 'name', {}, m_cls, 'state', 1),
- (False, 'name', {}, [], m_cls, 1),
- (False, m_cls, {}, [], 'state', m_cls),
-
- (True, 'name', {}, [], 'state', 1),
- (True, None, {}, [], 'state', 1),
- (True, 'name', None, [], 'state', 1),
- (True, 'name', {}, None, 'state', 1),
- (True, 'name', {}, [], 'state', None),
- ]
- )
- def test_node_model_creation(self, node_template_storage, is_valid, name, runtime_properties,
- scaling_groups, state, version):
- node = _test_model(
- is_valid=is_valid,
- storage=node_template_storage,
- model_cls=Node,
- model_kwargs=dict(
- node_template=node_template_storage.node_template.list()[0],
- type=node_template_storage.type.list()[0],
- name=name,
- runtime_properties=runtime_properties,
- scaling_groups=scaling_groups,
- state=state,
- version=version,
- service=node_template_storage.service.list()[0]
- ))
- if is_valid:
- assert node.node_template == node_template_storage.node_template.list()[0]
- assert node.service == \
- node_template_storage.service.list()[0]
-
-
-class TestNodeInstanceIP(object):
-
- ip = '1.1.1.1'
-
- def test_ip_on_none_hosted_node(self, service_storage):
- node_template = self._node_template(service_storage, ip='not considered')
- node = self._node(service_storage,
- node_template,
- is_host=False,
- ip='not considered')
- assert node.ip is None
-
- def test_property_ip_on_host_node(self, service_storage):
- node_template = self._node_template(service_storage, ip=self.ip)
- node = self._node(service_storage, node_template, is_host=True, ip=None)
- assert node.ip == self.ip
-
- def test_runtime_property_ip_on_host_node(self, service_storage):
- node_template = self._node_template(service_storage, ip='not considered')
- node = self._node(service_storage, node_template, is_host=True, ip=self.ip)
- assert node.ip == self.ip
-
- def test_no_ip_configured_on_host_node(self, service_storage):
- node_template = self._node_template(service_storage, ip=None)
- node = self._node(service_storage, node_template, is_host=True, ip=None)
- assert node.ip is None
-
- def test_runtime_property_on_hosted_node(self, service_storage):
- host_node_template = self._node_template(service_storage, ip=None)
- host_node = self._node(service_storage,
- host_node_template,
- is_host=True,
- ip=self.ip)
- node_template = self._node_template(service_storage, ip=None)
- node = self._node(service_storage,
- node_template,
- is_host=False,
- ip=None,
- host_fk=host_node.id)
- assert node.ip == self.ip
-
- def _node_template(self, storage, ip):
- kwargs = dict(
- name='node_template',
- type=storage.type.list()[0],
- default_instances=1,
- max_instances=1,
- min_instances=1,
- service_template=storage.service_template.list()[0]
- )
- if ip:
- kwargs['properties'] = {'ip': Parameter(name='ip', type_name='string', value=ip)}
- node = NodeTemplate(**kwargs)
- storage.node_template.put(node)
- return node
-
- def _node(self, storage, node, is_host, ip, host_fk=None):
- kwargs = dict(
- name='node',
- node_template=node,
- type=storage.type.list()[0],
- runtime_properties={},
- state='',
- service=storage.service.list()[0]
- )
- if ip:
- kwargs['runtime_properties']['ip'] = ip
- if is_host:
- kwargs['host_fk'] = 1
- elif host_fk:
- kwargs['host_fk'] = host_fk
- node = Node(**kwargs)
- storage.node.put(node)
- return node
-
-
-@pytest.mark.skip('Should be reworked into relationship')
-class TestRelationship(object):
- def test_relationship_model_creation(self, nodes_storage):
- nodes = nodes_storage.node
- source_node = nodes.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- target_node = nodes.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
-
- relationship = mock.models.create_relationship(
- source=source_node,
- target=nodes_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- )
- nodes_storage.relationship.put(relationship)
-
- relationship_instance = _test_model(
- is_valid=True,
- storage=nodes_storage,
- model_cls=Relationship,
- model_kwargs=dict(
- relationship=relationship,
- source_node=source_node,
- target_node=target_node
- ))
- assert relationship_instance.relationship == relationship
- assert relationship_instance.source_node == source_node
- assert relationship_instance.target_node == target_node
-
-
-class TestPlugin(object):
- @pytest.mark.parametrize(
- 'is_valid, archive_name, distribution, distribution_release, '
- 'distribution_version, package_name, package_source, '
- 'package_version, supported_platform, supported_py_versions, uploaded_at, wheels',
- [
- (False, m_cls, 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', m_cls, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', m_cls, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', m_cls, 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', m_cls, 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', m_cls, 'pak_ver',
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', m_cls,
- 'sup_plat', [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', m_cls, [], now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', m_cls, now, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', [], m_cls, []),
- (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', [], now, m_cls),
-
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (True, 'arc_name', None, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', None, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', None, 'pak_name', 'pak_src', 'pak_ver',
- 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', None, 'pak_ver',
- 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', None,
- 'sup_plat', [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', None, [], now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', None, now, []),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_plat', [], now, []),
- ]
- )
- def test_plugin_model_creation(self, empty_storage, is_valid, archive_name, distribution,
- distribution_release, distribution_version, package_name,
- package_source, package_version, supported_platform,
- supported_py_versions, uploaded_at, wheels):
- _test_model(is_valid=is_valid,
- storage=empty_storage,
- model_cls=Plugin,
- model_kwargs=dict(
- archive_name=archive_name,
- distribution=distribution,
- distribution_release=distribution_release,
- distribution_version=distribution_version,
- package_name=package_name,
- package_source=package_source,
- package_version=package_version,
- supported_platform=supported_platform,
- supported_py_versions=supported_py_versions,
- uploaded_at=uploaded_at,
- wheels=wheels,
- ))
-
-
-class TestTask(object):
-
- @pytest.mark.parametrize(
- 'is_valid, status, due_at, started_at, ended_at, max_attempts, retry_count, '
- 'retry_interval, ignore_failure, name, operation_mapping, inputs, plugin_id',
- [
- (False, m_cls, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, m_cls, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, m_cls, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, now, m_cls, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, now, now, m_cls, 1, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, now, now, 1, m_cls, 1, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, now, now, 1, 1, m_cls, True, 'name', 'map', {}, '1'),
- (False, Task.STARTED, now, now, now, 1, 1, 1, True, m_cls, 'map', {}, '1'),
- (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', m_cls, {}, '1'),
- (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', m_cls, '1'),
- (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, m_cls),
- (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', None, '1'),
-
- (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, None, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, None, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, now, None, 1, 1, 1, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, now, now, 1, None, 1, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, now, now, 1, 1, None, True, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, now, now, 1, 1, 1, None, 'name', 'map', {}, '1'),
- (True, Task.STARTED, now, now, now, 1, 1, 1, True, None, 'map', {}, '1'),
- (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', None, {}, '1'),
- (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, None),
- ]
- )
- def test_task_model_creation(self, execution_storage, is_valid, status, due_at, started_at,
- ended_at, max_attempts, retry_count, retry_interval,
- ignore_failure, name, operation_mapping, inputs, plugin_id):
- task = _test_model(
- is_valid=is_valid,
- storage=execution_storage,
- model_cls=Task,
- model_kwargs=dict(
- status=status,
- execution=execution_storage.execution.list()[0],
- due_at=due_at,
- started_at=started_at,
- ended_at=ended_at,
- max_attempts=max_attempts,
- retry_count=retry_count,
- retry_interval=retry_interval,
- ignore_failure=ignore_failure,
- name=name,
- implementation=operation_mapping,
- inputs=inputs,
- plugin_fk=plugin_id,
- ))
- if is_valid:
- assert task.execution == execution_storage.execution.list()[0]
- if task.plugin:
- assert task.plugin == execution_storage.plugin.list()[0]
-
- def test_task_max_attempts_validation(self):
- def create_task(max_attempts):
- Task(execution_fk='eid',
- name='name',
- implementation='',
- inputs={},
- max_attempts=max_attempts)
- create_task(max_attempts=1)
- create_task(max_attempts=2)
- create_task(max_attempts=Task.INFINITE_RETRIES)
- with pytest.raises(ValueError):
- create_task(max_attempts=0)
- with pytest.raises(ValueError):
- create_task(max_attempts=-2)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/storage/test_structures.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_structures.py b/tests/storage/test_structures.py
deleted file mode 100644
index cacec2e..0000000
--- a/tests/storage/test_structures.py
+++ /dev/null
@@ -1,220 +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.
-
-import pytest
-
-import sqlalchemy
-
-from aria.storage import (
- ModelStorage,
- sql_mapi,
- exceptions
-)
-from aria import modeling
-
-from ..storage import (
- release_sqlite_storage,
- bases,
- init_inmemory_model_storage
-)
-from . import MockModel
-from ..mock import (
- models,
- context as mock_context
-)
-
-
-@pytest.fixture
-def storage():
- base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
- initiator=init_inmemory_model_storage)
- base_storage.register(MockModel)
- yield base_storage
- release_sqlite_storage(base_storage)
-
-
-@pytest.fixture(scope='module', autouse=True)
-def module_cleanup():
- modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) # pylint: disable=no-member
-
-
-@pytest.fixture
-def context(tmpdir):
- ctx = mock_context.simple(str(tmpdir))
- yield ctx
- release_sqlite_storage(ctx.model)
-
-
-def test_inner_dict_update(storage):
- inner_dict = {'inner_value': 1}
-
- mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
- storage.mock_model.put(mock_model)
-
- storage_mm = storage.mock_model.get(mock_model.id)
- assert storage_mm == mock_model
-
- storage_mm.model_dict['inner_dict']['inner_value'] = 2
- storage_mm.model_dict['value'] = -1
- storage.mock_model.update(storage_mm)
- storage_mm = storage.mock_model.get(storage_mm.id)
-
- assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
- assert storage_mm.model_dict['value'] == -1
-
-
-def test_inner_list_update(storage):
- mock_model = MockModel(model_list=[0, [1]])
- storage.mock_model.put(mock_model)
-
- storage_mm = storage.mock_model.get(mock_model.id)
- assert storage_mm == mock_model
-
- storage_mm.model_list[1][0] = 'new_inner_value'
- storage_mm.model_list[0] = 'new_value'
- storage.mock_model.update(storage_mm)
- storage_mm = storage.mock_model.get(storage_mm.id)
-
- assert storage_mm.model_list[1][0] == 'new_inner_value'
- assert storage_mm.model_list[0] == 'new_value'
-
-
-def test_model_to_dict(context):
- service = context.service
- service = service.to_dict()
-
- expected_keys = [
- 'description',
- 'created_at',
- 'permalink',
- 'scaling_groups',
- 'updated_at'
- ]
-
- for expected_key in expected_keys:
- assert expected_key in service
-
-
-def test_relationship_model_ordering(context):
- service = context.model.service.get_by_name(models.SERVICE_NAME)
- source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
- target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
-
- new_node_template = modeling.models.NodeTemplate(
- name='new_node_template',
- type=source_node.type,
- default_instances=1,
- min_instances=1,
- max_instances=1,
- service_template=service.service_template
- )
-
- new_node = modeling.models.Node(
- name='new_node',
- type=source_node.type,
- runtime_properties={},
- service=service,
- version=None,
- node_template=new_node_template,
- state='',
- scaling_groups=[]
- )
-
- source_node.outbound_relationships.append(modeling.models.Relationship(
- source_node=source_node,
- target_node=new_node,
- ))
-
- new_node.outbound_relationships.append(modeling.models.Relationship( # pylint: disable=no-member
- source_node=new_node,
- target_node=target_node,
- ))
-
- context.model.node_template.put(new_node_template)
- context.model.node.put(new_node)
- context.model.node.refresh(source_node)
- context.model.node.refresh(target_node)
-
- def flip_and_assert(node, direction):
- """
- Reversed the order of relationships and assert effects took place.
- :param node: the node instance to operate on
- :param direction: the type of relationships to flip (inbound/outbound)
- :return:
- """
- assert direction in ('inbound', 'outbound')
-
- relationships = getattr(node, direction + '_relationships')
- assert len(relationships) == 2
-
- reversed_relationship = list(reversed(relationships))
- assert relationships != reversed_relationship
-
- relationships[:] = reversed_relationship
- context.model.node.update(node)
- assert relationships == reversed_relationship
-
- flip_and_assert(source_node, 'outbound')
- flip_and_assert(target_node, 'inbound')
-
-
-class StrictClass(modeling.models.aria_declarative_base, bases.ModelMixin):
- __tablename__ = 'strict_class'
-
- strict_dict = sqlalchemy.Column(modeling.types.StrictDict(basestring, basestring))
- strict_list = sqlalchemy.Column(modeling.types.StrictList(basestring))
-
-
-def test_strict_dict():
-
- strict_class = StrictClass()
-
- def assert_strict(sc):
- with pytest.raises(exceptions.StorageError):
- sc.strict_dict = {'key': 1}
-
- with pytest.raises(exceptions.StorageError):
- sc.strict_dict = {1: 'value'}
-
- with pytest.raises(exceptions.StorageError):
- sc.strict_dict = {1: 1}
-
- assert_strict(strict_class)
- strict_class.strict_dict = {'key': 'value'}
- assert strict_class.strict_dict == {'key': 'value'}
-
- assert_strict(strict_class)
- with pytest.raises(exceptions.StorageError):
- strict_class.strict_dict['key'] = 1
- with pytest.raises(exceptions.StorageError):
- strict_class.strict_dict[1] = 'value'
- with pytest.raises(exceptions.StorageError):
- strict_class.strict_dict[1] = 1
-
-
-def test_strict_list():
- strict_class = StrictClass()
-
- def assert_strict(sc):
- with pytest.raises(exceptions.StorageError):
- sc.strict_list = [1]
-
- assert_strict(strict_class)
- strict_class.strict_list = ['item']
- assert strict_class.strict_list == ['item']
-
- assert_strict(strict_class)
- with pytest.raises(exceptions.StorageError):
- strict_class.strict_list[0] = 1
[3/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_models.py
----------------------------------------------------------------------
diff --git a/tests/modeling/test_models.py b/tests/modeling/test_models.py
new file mode 100644
index 0000000..c8d36e0
--- /dev/null
+++ b/tests/modeling/test_models.py
@@ -0,0 +1,835 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from datetime import datetime
+from contextlib import contextmanager
+
+import pytest
+
+from aria import application_model_storage
+from aria.storage import (
+ exceptions,
+ sql_mapi,
+)
+from aria.modeling.models import (
+ ServiceTemplate,
+ Service,
+ ServiceUpdate,
+ ServiceUpdateStep,
+ ServiceModification,
+ Execution,
+ Task,
+ Plugin,
+ Relationship,
+ NodeTemplate,
+ Node,
+ Parameter,
+ Type
+)
+
+from tests import mock
+from ..storage import release_sqlite_storage, init_inmemory_model_storage
+
+
+@contextmanager
+def sql_storage(storage_func):
+ storage = None
+ try:
+ storage = storage_func()
+ yield storage
+ finally:
+ if storage:
+ release_sqlite_storage(storage)
+
+
+def _empty_storage():
+ return application_model_storage(sql_mapi.SQLAlchemyModelAPI,
+ initiator=init_inmemory_model_storage)
+
+
+def _service_template_storage():
+ storage = _empty_storage()
+ service_template = mock.models.create_service_template()
+ storage.service_template.put(service_template)
+ storage.type.put(Type(variant='node'))
+ return storage
+
+
+def _service_storage():
+ storage = _service_template_storage()
+ service = mock.models.create_service(
+ storage.service_template.get_by_name(mock.models.SERVICE_TEMPLATE_NAME))
+ storage.service.put(service)
+ return storage
+
+
+def _service_update_storage():
+ storage = _service_storage()
+ service_update = ServiceUpdate(
+ service=storage.service.list()[0],
+ created_at=now,
+ service_plan={},
+ )
+ storage.service_update.put(service_update)
+ return storage
+
+
+def _node_template_storage():
+ storage = _service_storage()
+ service_template = storage.service_template.list()[0]
+ node_template = mock.models.create_dependency_node_template(service_template)
+ storage.node_template.put(node_template)
+ return storage
+
+
+def _nodes_storage():
+ storage = _nodes_storage() # ???
+ service = storage.service.get_by_name(mock.models.SERVICE_NAME)
+ dependent_node_template = storage.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+ dependency_node_template = storage.node_template.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ dependency_node = mock.models.create_dependency_node(dependency_node_template, service)
+ dependent_node = mock.models.create_dependent_node(dependent_node_template, service)
+ storage.node.put(dependency_node)
+ storage.node.put(dependent_node)
+ return storage
+
+
+def _execution_storage():
+ storage = _service_storage()
+ execution = mock.models.create_execution(storage.service.list()[0])
+ plugin = mock.models.create_plugin()
+ storage.execution.put(execution)
+ storage.plugin.put(plugin)
+ return storage
+
+
+@pytest.fixture
+def empty_storage():
+ with sql_storage(_empty_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def service_template_storage():
+ with sql_storage(_service_template_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def service_storage():
+ with sql_storage(_service_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def service_update_storage():
+ with sql_storage(_service_update_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def node_template_storage():
+ with sql_storage(_node_template_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def nodes_storage():
+ with sql_storage(_nodes_storage) as storage:
+ yield storage
+
+
+@pytest.fixture
+def execution_storage():
+ with sql_storage(_execution_storage) as storage:
+ yield storage
+
+
+m_cls = type('MockClass')
+now = datetime.utcnow()
+
+
+def _test_model(is_valid, storage, model_cls, model_kwargs):
+ if is_valid:
+ model = model_cls(**model_kwargs)
+ getattr(storage, model_cls.__modelname__).put(model)
+ return model
+ else:
+ with pytest.raises((exceptions.StorageError, TypeError),):
+ getattr(storage, model_cls.__modelname__).put(model_cls(**model_kwargs))
+
+
+class TestServiceTemplate(object):
+
+ @pytest.mark.parametrize(
+ 'is_valid, description, created_at, updated_at, main_file_name',
+ [
+ (True, 'description', now, now, '/path'),
+ (False, {}, now, now, '/path'),
+ (False, 'description', 'error', now, '/path'),
+ (False, 'description', now, 'error', '/path'),
+ (False, 'description', now, now, {}),
+ (True, 'description', now, now, '/path'),
+ ]
+ )
+
+ def test_service_template_model_creation(self, empty_storage, is_valid, description, created_at,
+ updated_at, main_file_name):
+ _test_model(is_valid=is_valid,
+ storage=empty_storage,
+ model_cls=ServiceTemplate,
+ model_kwargs=dict(
+ description=description,
+ created_at=created_at,
+ updated_at=updated_at,
+ main_file_name=main_file_name)
+ )
+
+
+class TestService(object):
+
+ @pytest.mark.parametrize(
+ 'is_valid, name, created_at, description, inputs, permalink, '
+ 'outputs, scaling_groups, updated_at',
+ [
+ (False, m_cls, now, 'desc', {}, 'perlnk', {}, {}, now),
+ (False, 'name', m_cls, 'desc', {}, 'perlnk', {}, {}, now),
+ (False, 'name', now, m_cls, {}, 'perlnk', {}, {}, now),
+ (False, 'name', now, 'desc', {}, m_cls, {}, {}, now),
+ (False, 'name', now, 'desc', {}, 'perlnk', m_cls, {}, now),
+ (False, 'name', now, 'desc', {}, 'perlnk', {}, m_cls, now),
+ (False, 'name', now, 'desc', {}, 'perlnk', {}, {}, m_cls),
+
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
+ (True, None, now, 'desc', {}, 'perlnk', {}, {}, now),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
+ (True, 'name', now, None, {}, 'perlnk', {}, {}, now),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
+ (True, 'name', now, 'desc', {}, None, {}, {}, now),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, None, now),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, None),
+ (True, 'name', now, 'desc', {}, 'perlnk', {}, {}, now),
+ ]
+ )
+ def test_service_model_creation(self, service_storage, is_valid, name, created_at, description,
+ inputs, permalink, outputs, scaling_groups, updated_at):
+ service = _test_model(
+ is_valid=is_valid,
+ storage=service_storage,
+ model_cls=Service,
+ model_kwargs=dict(
+ name=name,
+ service_template=service_storage.service_template.list()[0],
+ created_at=created_at,
+ description=description,
+ inputs=inputs,
+ permalink=permalink,
+ outputs=outputs,
+ scaling_groups=scaling_groups,
+ updated_at=updated_at
+ ))
+ if is_valid:
+ assert service.service_template == \
+ service_storage.service_template.list()[0]
+
+
+class TestExecution(object):
+
+ @pytest.mark.parametrize(
+ 'is_valid, created_at, started_at, ended_at, error, is_system_workflow, parameters, '
+ 'status, workflow_name',
+ [
+ (False, m_cls, now, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (False, now, m_cls, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (False, now, now, m_cls, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (False, now, now, now, m_cls, False, {}, Execution.STARTED, 'wf_name'),
+ (False, now, now, now, 'error', False, m_cls, Execution.STARTED, 'wf_name'),
+ (False, now, now, now, 'error', False, {}, m_cls, 'wf_name'),
+ (False, now, now, now, 'error', False, {}, Execution.STARTED, m_cls),
+
+ (True, now, now, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (True, now, None, now, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (True, now, now, None, 'error', False, {}, Execution.STARTED, 'wf_name'),
+ (True, now, now, now, None, False, {}, Execution.STARTED, 'wf_name'),
+ (True, now, now, now, 'error', False, None, Execution.STARTED, 'wf_name'),
+ ]
+ )
+ def test_execution_model_creation(self, service_storage, is_valid, created_at, started_at,
+ ended_at, error, is_system_workflow, parameters, status,
+ workflow_name):
+ execution = _test_model(
+ is_valid=is_valid,
+ storage=service_storage,
+ model_cls=Execution,
+ model_kwargs=dict(
+ service=service_storage.service.list()[0],
+ created_at=created_at,
+ started_at=started_at,
+ ended_at=ended_at,
+ error=error,
+ is_system_workflow=is_system_workflow,
+ parameters=parameters,
+ status=status,
+ workflow_name=workflow_name,
+ ))
+ if is_valid:
+ assert execution.service == service_storage.service.list()[0]
+ assert execution.service_template == service_storage.service_template.list()[0]
+
+ def test_execution_status_transition(self):
+ def create_execution(status):
+ execution = Execution(
+ id='e_id',
+ workflow_name='w_name',
+ status=status,
+ parameters={},
+ created_at=now,
+ )
+ return execution
+
+ valid_transitions = {
+ Execution.PENDING: [Execution.STARTED,
+ Execution.CANCELLED,
+ Execution.PENDING],
+ Execution.STARTED: [Execution.FAILED,
+ Execution.TERMINATED,
+ Execution.CANCELLED,
+ Execution.CANCELLING,
+ Execution.STARTED],
+ Execution.CANCELLING: [Execution.FAILED,
+ Execution.TERMINATED,
+ Execution.CANCELLED,
+ Execution.CANCELLING],
+ Execution.FAILED: [Execution.FAILED],
+ Execution.TERMINATED: [Execution.TERMINATED],
+ Execution.CANCELLED: [Execution.CANCELLED]
+ }
+
+ invalid_transitions = {
+ Execution.PENDING: [Execution.FAILED,
+ Execution.TERMINATED,
+ Execution.CANCELLING],
+ Execution.STARTED: [Execution.PENDING],
+ Execution.CANCELLING: [Execution.PENDING,
+ Execution.STARTED],
+ Execution.FAILED: [Execution.PENDING,
+ Execution.STARTED,
+ Execution.TERMINATED,
+ Execution.CANCELLED,
+ Execution.CANCELLING],
+ Execution.TERMINATED: [Execution.PENDING,
+ Execution.STARTED,
+ Execution.FAILED,
+ Execution.CANCELLED,
+ Execution.CANCELLING],
+ Execution.CANCELLED: [Execution.PENDING,
+ Execution.STARTED,
+ Execution.FAILED,
+ Execution.TERMINATED,
+ Execution.CANCELLING],
+ }
+
+ for current_status, valid_transitioned_statues in valid_transitions.items():
+ for transitioned_status in valid_transitioned_statues:
+ execution = create_execution(current_status)
+ execution.status = transitioned_status
+
+ for current_status, invalid_transitioned_statues in invalid_transitions.items():
+ for transitioned_status in invalid_transitioned_statues:
+ execution = create_execution(current_status)
+ with pytest.raises(ValueError):
+ execution.status = transitioned_status
+
+
+class TestServiceUpdate(object):
+ @pytest.mark.parametrize(
+ 'is_valid, created_at, service_plan, service_update_nodes, '
+ 'service_update_service, service_update_node_templates, '
+ 'modified_entity_ids, state',
+ [
+ (False, m_cls, {}, {}, {}, [], {}, 'state'),
+ (False, now, m_cls, {}, {}, [], {}, 'state'),
+ (False, now, {}, m_cls, {}, [], {}, 'state'),
+ (False, now, {}, {}, m_cls, [], {}, 'state'),
+ (False, now, {}, {}, {}, m_cls, {}, 'state'),
+ (False, now, {}, {}, {}, [], m_cls, 'state'),
+ (False, now, {}, {}, {}, [], {}, m_cls),
+
+ (True, now, {}, {}, {}, [], {}, 'state'),
+ (True, now, {}, None, {}, [], {}, 'state'),
+ (True, now, {}, {}, None, [], {}, 'state'),
+ (True, now, {}, {}, {}, None, {}, 'state'),
+ (True, now, {}, {}, {}, [], None, 'state'),
+ (True, now, {}, {}, {}, [], {}, None),
+ ]
+ )
+ def test_service_update_model_creation(self, service_storage, is_valid, created_at,
+ service_plan, service_update_nodes,
+ service_update_service, service_update_node_templates,
+ modified_entity_ids, state):
+ service_update = _test_model(
+ is_valid=is_valid,
+ storage=service_storage,
+ model_cls=ServiceUpdate,
+ model_kwargs=dict(
+ service=service_storage.service.list()[0],
+ created_at=created_at,
+ service_plan=service_plan,
+ service_update_nodes=service_update_nodes,
+ service_update_service=service_update_service,
+ service_update_node_templates=service_update_node_templates,
+ modified_entity_ids=modified_entity_ids,
+ state=state
+ ))
+ if is_valid:
+ assert service_update.service == \
+ service_storage.service.list()[0]
+
+
+class TestServiceUpdateStep(object):
+
+ @pytest.mark.parametrize(
+ 'is_valid, action, entity_id, entity_type',
+ [
+ (False, m_cls, 'id', ServiceUpdateStep.ENTITY_TYPES.NODE),
+ (False, ServiceUpdateStep.ACTION_TYPES.ADD, m_cls,
+ ServiceUpdateStep.ENTITY_TYPES.NODE),
+ (False, ServiceUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
+
+ (True, ServiceUpdateStep.ACTION_TYPES.ADD, 'id',
+ ServiceUpdateStep.ENTITY_TYPES.NODE)
+ ]
+ )
+ def test_service_update_step_model_creation(self, service_update_storage, is_valid, action,
+ entity_id, entity_type):
+ service_update_step = _test_model(
+ is_valid=is_valid,
+ storage=service_update_storage,
+ model_cls=ServiceUpdateStep,
+ model_kwargs=dict(
+ service_update=
+ service_update_storage.service_update.list()[0],
+ action=action,
+ entity_id=entity_id,
+ entity_type=entity_type
+ ))
+ if is_valid:
+ assert service_update_step.service_update == \
+ service_update_storage.service_update.list()[0]
+
+ def test_service_update_step_order(self):
+ add_node = ServiceUpdateStep(
+ id='add_step',
+ action='add',
+ entity_type='node',
+ entity_id='node_id')
+
+ modify_node = ServiceUpdateStep(
+ id='modify_step',
+ action='modify',
+ entity_type='node',
+ entity_id='node_id')
+
+ remove_node = ServiceUpdateStep(
+ id='remove_step',
+ action='remove',
+ entity_type='node',
+ entity_id='node_id')
+
+ for step in (add_node, modify_node, remove_node):
+ assert hash((step.id, step.entity_id)) == hash(step)
+
+ assert remove_node < modify_node < add_node
+ assert not remove_node > modify_node > add_node
+
+ add_rel = ServiceUpdateStep(
+ id='add_step',
+ action='add',
+ entity_type='relationship',
+ entity_id='relationship_id')
+
+ remove_rel = ServiceUpdateStep(
+ id='remove_step',
+ action='remove',
+ entity_type='relationship',
+ entity_id='relationship_id')
+
+ assert remove_rel < remove_node < add_node < add_rel
+ assert not add_node < None
+
+
+class TestServiceModification(object):
+ @pytest.mark.parametrize(
+ 'is_valid, context, created_at, ended_at, modified_node_templates, nodes, status',
+ [
+ (False, m_cls, now, now, {}, {}, ServiceModification.STARTED),
+ (False, {}, m_cls, now, {}, {}, ServiceModification.STARTED),
+ (False, {}, now, m_cls, {}, {}, ServiceModification.STARTED),
+ (False, {}, now, now, m_cls, {}, ServiceModification.STARTED),
+ (False, {}, now, now, {}, m_cls, ServiceModification.STARTED),
+ (False, {}, now, now, {}, {}, m_cls),
+
+ (True, {}, now, now, {}, {}, ServiceModification.STARTED),
+ (True, {}, now, None, {}, {}, ServiceModification.STARTED),
+ (True, {}, now, now, None, {}, ServiceModification.STARTED),
+ (True, {}, now, now, {}, None, ServiceModification.STARTED),
+ ]
+ )
+ def test_service_modification_model_creation(self, service_storage, is_valid, context,
+ created_at, ended_at, modified_node_templates,
+ nodes, status):
+ service_modification = _test_model(
+ is_valid=is_valid,
+ storage=service_storage,
+ model_cls=ServiceModification,
+ model_kwargs=dict(
+ service=service_storage.service.list()[0],
+ context=context,
+ created_at=created_at,
+ ended_at=ended_at,
+ modified_node_templates=modified_node_templates,
+ nodes=nodes,
+ status=status,
+ ))
+ if is_valid:
+ assert service_modification.service == \
+ service_storage.service.list()[0]
+
+
+class TestNodeTemplate(object):
+ @pytest.mark.parametrize(
+ 'is_valid, name, default_instances, max_instances, min_instances, plugin_specifications, '
+ 'properties',
+ [
+ (False, m_cls, 1, 1, 1, [], {}),
+ (False, 'name', m_cls, 1, 1, [], {}),
+ (False, 'name', 1, m_cls, 1, [], {}),
+ (False, 'name', 1, 1, m_cls, [], {}),
+ (False, 'name', 1, 1, 1, m_cls, {}),
+ (False, 'name', 1, 1, 1, None, {}),
+
+ (True, 'name', 1, 1, 1, [], {}),
+ ]
+ )
+ def test_node_template_model_creation(self, service_storage, is_valid, name, default_instances,
+ max_instances, min_instances, plugin_specifications,
+ properties):
+ node_template = _test_model(
+ is_valid=is_valid,
+ storage=service_storage,
+ model_cls=NodeTemplate,
+ model_kwargs=dict(
+ name=name,
+ type=service_storage.type.list()[0],
+ default_instances=default_instances,
+ max_instances=max_instances,
+ min_instances=min_instances,
+ plugin_specifications=plugin_specifications,
+ properties=properties,
+ service_template=service_storage.service_template.list()[0]
+ ))
+ if is_valid:
+ assert node_template.service_template == \
+ service_storage.service_template.list()[0]
+
+
+class TestNode(object):
+ @pytest.mark.parametrize(
+ 'is_valid, name, runtime_properties, scaling_groups, state, version',
+ [
+ (False, m_cls, {}, [], 'state', 1),
+ (False, 'name', m_cls, [], 'state', 1),
+ (False, 'name', {}, m_cls, 'state', 1),
+ (False, 'name', {}, [], m_cls, 1),
+ (False, m_cls, {}, [], 'state', m_cls),
+
+ (True, 'name', {}, [], 'state', 1),
+ (True, None, {}, [], 'state', 1),
+ (True, 'name', None, [], 'state', 1),
+ (True, 'name', {}, None, 'state', 1),
+ (True, 'name', {}, [], 'state', None),
+ ]
+ )
+ def test_node_model_creation(self, node_template_storage, is_valid, name, runtime_properties,
+ scaling_groups, state, version):
+ node = _test_model(
+ is_valid=is_valid,
+ storage=node_template_storage,
+ model_cls=Node,
+ model_kwargs=dict(
+ node_template=node_template_storage.node_template.list()[0],
+ type=node_template_storage.type.list()[0],
+ name=name,
+ runtime_properties=runtime_properties,
+ scaling_groups=scaling_groups,
+ state=state,
+ version=version,
+ service=node_template_storage.service.list()[0]
+ ))
+ if is_valid:
+ assert node.node_template == node_template_storage.node_template.list()[0]
+ assert node.service == \
+ node_template_storage.service.list()[0]
+
+
+class TestNodeInstanceIP(object):
+
+ ip = '1.1.1.1'
+
+ def test_ip_on_none_hosted_node(self, service_storage):
+ node_template = self._node_template(service_storage, ip='not considered')
+ node = self._node(service_storage,
+ node_template,
+ is_host=False,
+ ip='not considered')
+ assert node.ip is None
+
+ def test_property_ip_on_host_node(self, service_storage):
+ node_template = self._node_template(service_storage, ip=self.ip)
+ node = self._node(service_storage, node_template, is_host=True, ip=None)
+ assert node.ip == self.ip
+
+ def test_runtime_property_ip_on_host_node(self, service_storage):
+ node_template = self._node_template(service_storage, ip='not considered')
+ node = self._node(service_storage, node_template, is_host=True, ip=self.ip)
+ assert node.ip == self.ip
+
+ def test_no_ip_configured_on_host_node(self, service_storage):
+ node_template = self._node_template(service_storage, ip=None)
+ node = self._node(service_storage, node_template, is_host=True, ip=None)
+ assert node.ip is None
+
+ def test_runtime_property_on_hosted_node(self, service_storage):
+ host_node_template = self._node_template(service_storage, ip=None)
+ host_node = self._node(service_storage,
+ host_node_template,
+ is_host=True,
+ ip=self.ip)
+ node_template = self._node_template(service_storage, ip=None)
+ node = self._node(service_storage,
+ node_template,
+ is_host=False,
+ ip=None,
+ host_fk=host_node.id)
+ assert node.ip == self.ip
+
+ def _node_template(self, storage, ip):
+ kwargs = dict(
+ name='node_template',
+ type=storage.type.list()[0],
+ default_instances=1,
+ max_instances=1,
+ min_instances=1,
+ service_template=storage.service_template.list()[0]
+ )
+ if ip:
+ kwargs['properties'] = {'ip': Parameter(name='ip', type_name='string', value=ip)}
+ node = NodeTemplate(**kwargs)
+ storage.node_template.put(node)
+ return node
+
+ def _node(self, storage, node, is_host, ip, host_fk=None):
+ kwargs = dict(
+ name='node',
+ node_template=node,
+ type=storage.type.list()[0],
+ runtime_properties={},
+ state='',
+ service=storage.service.list()[0]
+ )
+ if ip:
+ kwargs['runtime_properties']['ip'] = ip
+ if is_host:
+ kwargs['host_fk'] = 1
+ elif host_fk:
+ kwargs['host_fk'] = host_fk
+ node = Node(**kwargs)
+ storage.node.put(node)
+ return node
+
+
+@pytest.mark.skip('Should be reworked into relationship')
+class TestRelationship(object):
+ def test_relationship_model_creation(self, nodes_storage):
+ nodes = nodes_storage.node
+ source_node = nodes.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+ target_node = nodes.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+
+ relationship = mock.models.create_relationship(
+ source=source_node,
+ target=nodes_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ )
+ nodes_storage.relationship.put(relationship)
+
+ relationship_instance = _test_model(
+ is_valid=True,
+ storage=nodes_storage,
+ model_cls=Relationship,
+ model_kwargs=dict(
+ relationship=relationship,
+ source_node=source_node,
+ target_node=target_node
+ ))
+ assert relationship_instance.relationship == relationship
+ assert relationship_instance.source_node == source_node
+ assert relationship_instance.target_node == target_node
+
+
+class TestPlugin(object):
+ @pytest.mark.parametrize(
+ 'is_valid, archive_name, distribution, distribution_release, '
+ 'distribution_version, package_name, package_source, '
+ 'package_version, supported_platform, supported_py_versions, uploaded_at, wheels',
+ [
+ (False, m_cls, 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', m_cls, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', m_cls, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', m_cls, 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', m_cls, 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', m_cls, 'pak_ver',
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', m_cls,
+ 'sup_plat', [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', m_cls, [], now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', m_cls, now, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', [], m_cls, []),
+ (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', [], now, m_cls),
+
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', None, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', None, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', None, 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', None, 'pak_ver',
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', None,
+ 'sup_plat', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', None, [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', None, now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+ 'pak_ver', 'sup_plat', [], now, []),
+ ]
+ )
+ def test_plugin_model_creation(self, empty_storage, is_valid, archive_name, distribution,
+ distribution_release, distribution_version, package_name,
+ package_source, package_version, supported_platform,
+ supported_py_versions, uploaded_at, wheels):
+ _test_model(is_valid=is_valid,
+ storage=empty_storage,
+ model_cls=Plugin,
+ model_kwargs=dict(
+ archive_name=archive_name,
+ distribution=distribution,
+ distribution_release=distribution_release,
+ distribution_version=distribution_version,
+ package_name=package_name,
+ package_source=package_source,
+ package_version=package_version,
+ supported_platform=supported_platform,
+ supported_py_versions=supported_py_versions,
+ uploaded_at=uploaded_at,
+ wheels=wheels,
+ ))
+
+
+class TestTask(object):
+
+ @pytest.mark.parametrize(
+ 'is_valid, status, due_at, started_at, ended_at, max_attempts, retry_count, '
+ 'retry_interval, ignore_failure, name, operation_mapping, inputs, plugin_id',
+ [
+ (False, m_cls, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, m_cls, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, m_cls, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, now, m_cls, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, now, now, m_cls, 1, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, now, now, 1, m_cls, 1, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, now, now, 1, 1, m_cls, True, 'name', 'map', {}, '1'),
+ (False, Task.STARTED, now, now, now, 1, 1, 1, True, m_cls, 'map', {}, '1'),
+ (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', m_cls, {}, '1'),
+ (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', m_cls, '1'),
+ (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, m_cls),
+ (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', None, '1'),
+
+ (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, None, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, None, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, now, None, 1, 1, 1, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, None, 1, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, 1, None, True, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, 1, 1, None, 'name', 'map', {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, 1, 1, True, None, 'map', {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', None, {}, '1'),
+ (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, None),
+ ]
+ )
+ def test_task_model_creation(self, execution_storage, is_valid, status, due_at, started_at,
+ ended_at, max_attempts, retry_count, retry_interval,
+ ignore_failure, name, operation_mapping, inputs, plugin_id):
+ task = _test_model(
+ is_valid=is_valid,
+ storage=execution_storage,
+ model_cls=Task,
+ model_kwargs=dict(
+ status=status,
+ execution=execution_storage.execution.list()[0],
+ due_at=due_at,
+ started_at=started_at,
+ ended_at=ended_at,
+ max_attempts=max_attempts,
+ retry_count=retry_count,
+ retry_interval=retry_interval,
+ ignore_failure=ignore_failure,
+ name=name,
+ implementation=operation_mapping,
+ inputs=inputs,
+ plugin_fk=plugin_id,
+ ))
+ if is_valid:
+ assert task.execution == execution_storage.execution.list()[0]
+ if task.plugin:
+ assert task.plugin == execution_storage.plugin.list()[0]
+
+ def test_task_max_attempts_validation(self):
+ def create_task(max_attempts):
+ Task(execution_fk='eid',
+ name='name',
+ implementation='',
+ inputs={},
+ max_attempts=max_attempts)
+ create_task(max_attempts=1)
+ create_task(max_attempts=2)
+ create_task(max_attempts=Task.INFINITE_RETRIES)
+ with pytest.raises(ValueError):
+ create_task(max_attempts=0)
+ with pytest.raises(ValueError):
+ create_task(max_attempts=-2)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index 8ac8d49..7817a90 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -84,7 +84,7 @@ def test_node_operation_task_execution(ctx, executor):
operation_context = global_test_holder[api.task.OperationTask.NAME_FORMAT.format(
type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name
)]
@@ -95,7 +95,7 @@ def test_node_operation_task_execution(ctx, executor):
assert operation_context.task.actor == node
assert operation_context.task.name == api.task.OperationTask.NAME_FORMAT.format(
type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name
)
@@ -140,7 +140,7 @@ def test_relationship_operation_task_execution(ctx, executor):
operation_context = global_test_holder[api.task.OperationTask.NAME_FORMAT.format(
type='relationship',
- id=relationship.id,
+ id=relationship.name,
interface=interface_name,
operation=operation_name
)]
@@ -205,7 +205,7 @@ def test_invalid_task_operation_id(ctx, executor):
op_node_id = global_test_holder[api.task.OperationTask.NAME_FORMAT.format(
type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name
)]
@@ -218,7 +218,8 @@ def test_plugin_workdir(ctx, executor, tmpdir):
operation_name = 'create'
plugin = mock.models.create_plugin()
- plugin.name = 'mock_plugin'
+ ctx.model.plugin.put(plugin)
+ plugin_specification = mock.models.create_plugin_specification()
node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
interface = mock.models.create_interface(
node.service,
@@ -226,10 +227,10 @@ def test_plugin_workdir(ctx, executor, tmpdir):
operation_name,
operation_kwargs=dict(
implementation='{0}.{1}'.format(__name__, _test_plugin_workdir.__name__),
- plugin=plugin)
+ plugin_specification=plugin_specification)
)
node.interfaces[interface.name] = interface
- node.plugins = [plugin]
+ node.plugin_specifications = [plugin_specification]
ctx.model.node.update(node)
filename = 'test_file'
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/context/test_serialize.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_serialize.py b/tests/orchestrator/context/test_serialize.py
index 9e5a0b4..2ec999c 100644
--- a/tests/orchestrator/context/test_serialize.py
+++ b/tests/orchestrator/context/test_serialize.py
@@ -44,14 +44,17 @@ def test_serialize_operation_context(context, executor, tmpdir):
def _mock_workflow(ctx, graph):
node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
plugin = mock.models.create_plugin()
+ ctx.model.plugin.put(plugin)
+ plugin_specification = mock.models.create_plugin_specification()
interface = mock.models.create_interface(
node.service,
'test',
'op',
- operation_kwargs=dict(implementation=_operation_mapping(), plugin=plugin)
+ operation_kwargs=dict(implementation=_operation_mapping(),
+ plugin_specification=plugin_specification)
)
node.interfaces[interface.name] = interface
- node.plugins = [plugin]
+ node.plugin_specifications = [plugin_specification]
task = api.task.OperationTask.for_node(node=node, interface_name='test', operation_name='op')
graph.add_tasks(task)
return graph
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/context/test_toolbelt.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_toolbelt.py b/tests/orchestrator/context/test_toolbelt.py
index f04fd4b..0e35a26 100644
--- a/tests/orchestrator/context/test_toolbelt.py
+++ b/tests/orchestrator/context/test_toolbelt.py
@@ -132,7 +132,7 @@ def test_relationship_tool_belt(workflow_context, executor):
assert isinstance(global_test_holder.get(api.task.OperationTask.NAME_FORMAT.format(
type='relationship',
- id=relationship.id,
+ id=relationship.name,
interface=interface_name,
operation=operation_name
)), RelationshipToolBelt)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/workflows/api/test_task.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/api/test_task.py b/tests/orchestrator/workflows/api/test_task.py
index 4f5671b..139d239 100644
--- a/tests/orchestrator/workflows/api/test_task.py
+++ b/tests/orchestrator/workflows/api/test_task.py
@@ -43,17 +43,20 @@ class TestOperationTask(object):
operation_name = 'create'
plugin = mock.models.create_plugin('package', '0.1')
- plugin.name = 'test_plugin'
+ ctx.model.node.update(plugin)
+
+ plugin_specification = mock.models.create_plugin_specification('package', '0.1')
interface = mock.models.create_interface(
ctx.service,
interface_name,
operation_name,
- operation_kwargs=dict(plugin=plugin, implementation='op_path'))
+ operation_kwargs=dict(plugin_specification=plugin_specification,
+ implementation='op_path'))
node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
node.interfaces = {interface.name: interface}
- node.plugins = [plugin]
+ node.plugin_specifications = [plugin_specification]
ctx.model.node.update(node)
inputs = {'test_input': True}
max_attempts = 10
@@ -72,7 +75,7 @@ class TestOperationTask(object):
assert api_task.name == api.task.OperationTask.NAME_FORMAT.format(
type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name
)
@@ -90,18 +93,21 @@ class TestOperationTask(object):
operation_name = 'preconfigure'
plugin = mock.models.create_plugin('package', '0.1')
- plugin.name = 'test_plugin'
+ ctx.model.node.update(plugin)
+
+ plugin_specification = mock.models.create_plugin_specification('package', '0.1')
interface = mock.models.create_interface(
ctx.service,
interface_name,
operation_name,
- operation_kwargs=dict(plugin=plugin, implementation='op_path')
+ operation_kwargs=dict(plugin_specification=plugin_specification,
+ implementation='op_path')
)
relationship = ctx.model.relationship.list()[0]
relationship.interfaces[interface.name] = interface
- relationship.source_node.plugins = [plugin]
+ relationship.source_node.plugin_specifications = [plugin_specification]
inputs = {'test_input': True}
max_attempts = 10
retry_interval = 10
@@ -117,7 +123,7 @@ class TestOperationTask(object):
assert api_task.name == api.task.OperationTask.NAME_FORMAT.format(
type='relationship',
- id=relationship.id,
+ id=relationship.name,
interface=interface_name,
operation=operation_name
)
@@ -134,18 +140,21 @@ class TestOperationTask(object):
operation_name = 'preconfigure'
plugin = mock.models.create_plugin('package', '0.1')
- plugin.name = 'test_plugin'
+ ctx.model.node.update(plugin)
+
+ plugin_specification = mock.models.create_plugin_specification('package', '0.1')
interface = mock.models.create_interface(
ctx.service,
interface_name,
operation_name,
- operation_kwargs=dict(plugin=plugin, implementation='op_path')
+ operation_kwargs=dict(plugin_specification=plugin_specification,
+ implementation='op_path')
)
relationship = ctx.model.relationship.list()[0]
relationship.interfaces[interface.name] = interface
- relationship.target_node.plugins = [plugin]
+ relationship.target_node.plugin_specifications = [plugin_specification]
inputs = {'test_input': True}
max_attempts = 10
retry_interval = 10
@@ -162,7 +171,7 @@ class TestOperationTask(object):
assert api_task.name == api.task.OperationTask.NAME_FORMAT.format(
type='relationship',
- id=relationship.id,
+ id=relationship.name,
interface=interface_name,
operation=operation_name
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/workflows/builtin/test_execute_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/builtin/test_execute_operation.py b/tests/orchestrator/workflows/builtin/test_execute_operation.py
index 0e5a506..e2a8ef0 100644
--- a/tests/orchestrator/workflows/builtin/test_execute_operation.py
+++ b/tests/orchestrator/workflows/builtin/test_execute_operation.py
@@ -57,7 +57,7 @@ def test_execute_operation(ctx):
assert len(execute_tasks) == 1
assert execute_tasks[0].name == task.OperationTask.NAME_FORMAT.format(
type='node',
- id=node.id,
+ id=node.name,
interface=interface_name,
operation=operation_name
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/orchestrator/workflows/core/test_task.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_task.py b/tests/orchestrator/workflows/core/test_task.py
index d03597d..49f020f 100644
--- a/tests/orchestrator/workflows/core/test_task.py
+++ b/tests/orchestrator/workflows/core/test_task.py
@@ -83,18 +83,23 @@ class TestOperationTask(object):
return api_task, core_task
def test_node_operation_task_creation(self, ctx):
- storage_plugin = mock.models.create_plugin(package_name='p1', package_version='0.1')
- storage_plugin_other = mock.models.create_plugin(package_name='p0', package_version='0.0')
- ctx.model.plugin.put(storage_plugin_other)
+ storage_plugin = mock.models.create_plugin(
+ package_name='p1', package_version='0.1')
+ storage_plugin_specification = mock.models.create_plugin_specification(
+ package_name='p1', package_version='0.1')
+ storage_plugin_specification_other = mock.models.create_plugin(
+ package_name='p0', package_version='0.0')
ctx.model.plugin.put(storage_plugin)
+ ctx.model.plugin_specification.put(storage_plugin_specification_other)
+ ctx.model.plugin_specification.put(storage_plugin_specification)
node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
node_template = node.node_template
- node_template.plugins = [storage_plugin]
+ node_template.plugin_specifications = [storage_plugin_specification]
interface = mock.models.create_interface(
node.service,
NODE_INTERFACE_NAME,
NODE_OPERATION_NAME,
- operation_kwargs=dict(plugin=storage_plugin)
+ operation_kwargs=dict(plugin_specification=storage_plugin_specification)
)
node.interfaces[interface.name] = interface
ctx.model.node_template.update(node_template)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/resources/service-templates/tosca-simple-1.0/node-cellar/workflows.py
----------------------------------------------------------------------
diff --git a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/workflows.py b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/workflows.py
index 4f73c50..94ee824 100644
--- a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/workflows.py
+++ b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/workflows.py
@@ -20,7 +20,6 @@ def maintenance(ctx, graph, enabled):
graph.add_tasks(OperationTask.for_node(node=node,
interface_name=INTERFACE_NAME,
operation_name=ENABLE_OPERATION_NAME if enabled
- else DISABLE_OPERATION_NAME,
- dry=True))
+ else DISABLE_OPERATION_NAME))
except TaskException:
pass
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index f33205f..e836e2a 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -12,35 +12,17 @@
# 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.
-import os
-import platform
+
from shutil import rmtree
from tempfile import mkdtemp
from sqlalchemy import (
create_engine,
orm,
- Column,
- Text,
- Integer,
pool
)
-
-from aria.modeling import (
- models,
- types as modeling_types,
- bases
-)
-from aria import modeling
-
-
-class MockModel(models.aria_declarative_base, bases.ModelMixin): #pylint: disable=abstract-method
- __tablename__ = 'mock_model'
- model_dict = Column(modeling_types.Dict)
- model_list = Column(modeling_types.List)
- value = Column(Integer)
- name = Column(Text)
+from aria.modeling import models
class TestFileSystem(object):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
index 3ef74e4..4188fcd 100644
--- a/tests/storage/test_instrumentation.py
+++ b/tests/storage/test_instrumentation.py
@@ -17,7 +17,7 @@ import pytest
from sqlalchemy import Column, Text, Integer, event
from aria.modeling import (
- bases,
+ mixins,
types as modeling_types,
models
)
@@ -28,7 +28,7 @@ from aria.storage import (
instrumentation
)
-from ..storage import release_sqlite_storage, init_inmemory_model_storage
+from . import release_sqlite_storage, init_inmemory_model_storage
STUB = instrumentation._STUB
Value = instrumentation._Value
@@ -337,7 +337,7 @@ def storage():
release_sqlite_storage(result)
-class _MockModel(bases.ModelMixin):
+class _MockModel(mixins.ModelMixin):
name = Column(Text)
dict1 = Column(modeling_types.Dict)
dict2 = Column(modeling_types.Dict)
@@ -356,7 +356,7 @@ class MockModel2(_MockModel, models.aria_declarative_base):
__tablename__ = 'mock_model_2'
-class StrictMockModel(bases.ModelMixin, models.aria_declarative_base):
+class StrictMockModel(mixins.ModelMixin, models.aria_declarative_base):
__tablename__ = 'strict_mock_model'
strict_dict = Column(modeling_types.StrictDict(basestring, basestring))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/storage/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
deleted file mode 100644
index bb778d4..0000000
--- a/tests/storage/test_model_storage.py
+++ /dev/null
@@ -1,102 +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.
-
-import pytest
-
-from aria.storage import (
- ModelStorage,
- exceptions,
- sql_mapi
-)
-from aria import (application_model_storage, modeling)
-from ..storage import (release_sqlite_storage, init_inmemory_model_storage)
-
-from . import MockModel
-
-
-@pytest.fixture
-def storage():
- base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
- initiator=init_inmemory_model_storage)
- base_storage.register(MockModel)
- yield base_storage
- release_sqlite_storage(base_storage)
-
-
-@pytest.fixture(scope='module', autouse=True)
-def module_cleanup():
- modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member
-
-
-def test_storage_base(storage):
- with pytest.raises(AttributeError):
- storage.non_existent_attribute()
-
-
-def test_model_storage(storage):
- mock_model = MockModel(value=0, name='model_name')
- storage.mock_model.put(mock_model)
-
- assert storage.mock_model.get_by_name('model_name') == mock_model
-
- assert [mm_from_storage for mm_from_storage in storage.mock_model.iter()] == [mock_model]
- assert [mm_from_storage for mm_from_storage in storage.mock_model] == [mock_model]
-
- storage.mock_model.delete(mock_model)
- with pytest.raises(exceptions.StorageError):
- storage.mock_model.get(mock_model.id)
-
-
-def test_application_storage_factory():
- storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
- initiator=init_inmemory_model_storage)
-
- assert storage.service_template
- assert storage.node_template
- assert storage.group_template
- assert storage.policy_template
- assert storage.substitution_template
- assert storage.substitution_template_mapping
- assert storage.requirement_template
- assert storage.relationship_template
- assert storage.capability_template
- assert storage.interface_template
- assert storage.operation_template
- assert storage.artifact_template
-
- assert storage.service
- assert storage.node
- assert storage.group
- assert storage.policy
- assert storage.substitution
- assert storage.substitution_mapping
- assert storage.relationship
- assert storage.capability
- assert storage.interface
- assert storage.operation
- assert storage.artifact
-
- assert storage.execution
- assert storage.service_update
- assert storage.service_update_step
- assert storage.service_modification
- assert storage.plugin
- assert storage.task
-
- assert storage.parameter
- assert storage.type
- assert storage.metadata
-
- release_sqlite_storage(storage)