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/02/13 18:09:41 UTC
[1/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model [Forced Update!]
Repository: incubator-ariatosca
Updated Branches:
refs/heads/ARIA-106-Create-sqla-logging-handler 6bff90672 -> 67b754254 (forced update)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 b39a81f..26564c5 100644
--- a/tests/orchestrator/workflows/core/test_task.py
+++ b/tests/orchestrator/workflows/core/test_task.py
@@ -28,30 +28,43 @@ from aria.orchestrator.workflows import (
from tests import mock, storage
+OP_NAME = 'tosca.interfaces.node.lifecycle.Standard.create'
+RELATIONSHIP_OP_NAME = 'tosca.interfaces.relationship.Configure.pre_configure'
+
@pytest.fixture
def ctx(tmpdir):
context = mock.context.simple(str(tmpdir))
+
+ relationship = context.model.relationship.list()[0]
+ relationship.interfaces = [
+ mock.models.get_interface(RELATIONSHIP_OP_NAME, edge='source'),
+ mock.models.get_interface(RELATIONSHIP_OP_NAME, edge='target')
+ ]
+ context.model.relationship.update(relationship)
+
+ dependent_node = context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ dependent_node.interfaces = [mock.models.get_interface(OP_NAME)]
+ context.model.node.update(dependent_node)
+
yield context
storage.release_sqlite_storage(context.model)
class TestOperationTask(object):
- def _create_node_operation_task(self, ctx, node_instance):
+ def _create_node_operation_task(self, ctx, node):
with workflow_context.current.push(ctx):
- api_task = api.task.OperationTask.node_instance(
- instance=node_instance,
+ api_task = api.task.OperationTask.node(
+ instance=node,
name='tosca.interfaces.node.lifecycle.Standard.create')
core_task = core.task.OperationTask(api_task=api_task)
return api_task, core_task
- def _create_relationship_operation_task(self, ctx, relationship_instance, operation_end):
+ def _create_relationship_operation_task(self, ctx, relationship, operation_name, edge):
with workflow_context.current.push(ctx):
- api_task = api.task.OperationTask.relationship_instance(
- instance=relationship_instance,
- name='tosca.interfaces.relationship.Configure.pre_configure_source',
- operation_end=operation_end)
+ api_task = api.task.OperationTask.relationship(
+ instance=relationship, name=operation_name, edge=edge)
core_task = core.task.OperationTask(api_task=api_task)
return api_task, core_task
@@ -60,45 +73,47 @@ class TestOperationTask(object):
storage_plugin_other = mock.models.get_plugin(package_name='p0', package_version='0.0')
ctx.model.plugin.put(storage_plugin_other)
ctx.model.plugin.put(storage_plugin)
- node_instance = ctx.model.node_instance.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node = node_instance.node
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node_template = node.node_template
plugin_name = 'plugin1'
- node.plugins = [{'name': plugin_name,
- 'package_name': 'p1',
- 'package_version': '0.1'}]
- node.operations['tosca.interfaces.node.lifecycle.Standard.create'] = {'plugin': plugin_name}
- api_task, core_task = self._create_node_operation_task(ctx, node_instance)
+ node_template.plugins = [{'name': 'plugin1',
+ 'package_name': 'p1',
+ 'package_version': '0.1'}]
+ node.interfaces = [mock.models.get_interface(
+ 'tosca.interfaces.node.lifecycle.Standard.create',
+ operation_kwargs=dict(plugin='plugin1')
+ )]
+ ctx.model.node_template.update(node_template)
+ ctx.model.node.update(node)
+ api_task, core_task = self._create_node_operation_task(ctx, node)
storage_task = ctx.model.task.get_by_name(core_task.name)
assert storage_task.plugin_name == plugin_name
assert storage_task.execution_name == ctx.execution.name
- assert storage_task.runs_on.id == core_task.context.node_instance.id
+ assert storage_task.runs_on == core_task.context.node
assert core_task.model_task == storage_task
assert core_task.name == api_task.name
- assert core_task.operation_mapping == api_task.operation_mapping
- assert core_task.actor == api_task.actor == node_instance
+ assert core_task.implementation == api_task.implementation
+ assert core_task.actor == api_task.actor == node
assert core_task.inputs == api_task.inputs == storage_task.inputs
assert core_task.plugin == storage_plugin
def test_source_relationship_operation_task_creation(self, ctx):
- relationship_instance = ctx.model.relationship_instance.list()[0]
+ relationship = ctx.model.relationship.list()[0]
+ ctx.model.relationship.update(relationship)
_, core_task = self._create_relationship_operation_task(
- ctx, relationship_instance,
- api.task.OperationTask.SOURCE_OPERATION)
- assert core_task.model_task.runs_on.id == relationship_instance.source_node_instance.id
+ ctx, relationship, RELATIONSHIP_OP_NAME, 'source')
+ assert core_task.model_task.runs_on == relationship.source_node
def test_target_relationship_operation_task_creation(self, ctx):
- relationship_instance = ctx.model.relationship_instance.list()[0]
+ relationship = ctx.model.relationship.list()[0]
_, core_task = self._create_relationship_operation_task(
- ctx, relationship_instance,
- api.task.OperationTask.TARGET_OPERATION)
- assert core_task.model_task.runs_on.id == relationship_instance.target_node_instance.id
+ ctx, relationship, RELATIONSHIP_OP_NAME, 'target')
+ assert core_task.model_task.runs_on == relationship.target_node
def test_operation_task_edit_locked_attribute(self, ctx):
- node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- _, core_task = self._create_node_operation_task(ctx, node_instance)
+ _, core_task = self._create_node_operation_task(ctx, node)
now = datetime.utcnow()
with pytest.raises(exceptions.TaskException):
core_task.status = core_task.STARTED
@@ -112,10 +127,9 @@ class TestOperationTask(object):
core_task.due_at = now
def test_operation_task_edit_attributes(self, ctx):
- node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- _, core_task = self._create_node_operation_task(ctx, node_instance)
+ _, core_task = self._create_node_operation_task(ctx, node)
future_time = datetime.utcnow() + timedelta(seconds=3)
with core_task._update():
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py b/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
index 57be075..167004e 100644
--- a/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
+++ b/tests/orchestrator/workflows/core/test_task_graph_into_exececution_graph.py
@@ -25,21 +25,23 @@ from tests import storage
def test_task_graph_into_execution_graph(tmpdir):
operation_name = 'tosca.interfaces.node.lifecycle.Standard.create'
task_context = mock.context.simple(str(tmpdir))
- node_instance = \
- task_context.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node = task_context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(operation_name)]
+ task_context.model.node.update(node)
+
def sub_workflow(name, **_):
return api.task_graph.TaskGraph(name)
with context.workflow.current.push(task_context):
test_task_graph = api.task.WorkflowTask(sub_workflow, name='test_task_graph')
- simple_before_task = api.task.OperationTask.node_instance(instance=node_instance,
- name=operation_name)
- simple_after_task = api.task.OperationTask.node_instance(instance=node_instance,
- name=operation_name)
+ simple_before_task = api.task.OperationTask.node(instance=node,
+ name=operation_name)
+ simple_after_task = api.task.OperationTask.node(instance=node,
+ name=operation_name)
inner_task_graph = api.task.WorkflowTask(sub_workflow, name='test_inner_task_graph')
- inner_task = api.task.OperationTask.node_instance(instance=node_instance,
- name=operation_name)
+ inner_task = api.task.OperationTask.node(instance=node,
+ name=operation_name)
inner_task_graph.add_tasks(inner_task)
test_task_graph.add_tasks(simple_before_task)
@@ -91,7 +93,7 @@ def test_task_graph_into_execution_graph(tmpdir):
def _assert_execution_is_api_task(execution_task, api_task):
assert execution_task.id == api_task.id
assert execution_task.name == api_task.name
- assert execution_task.operation_mapping == api_task.operation_mapping
+ assert execution_task.implementation == api_task.implementation
assert execution_task.actor == api_task.actor
assert execution_task.inputs == api_task.inputs
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_executor.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_executor.py b/tests/orchestrator/workflows/executor/test_executor.py
index d983fe9..580bf8b 100644
--- a/tests/orchestrator/workflows/executor/test_executor.py
+++ b/tests/orchestrator/workflows/executor/test_executor.py
@@ -28,7 +28,7 @@ except ImportError:
_celery = None
app = None
-from aria.storage import model
+from aria.storage.modeling import model
from aria.orchestrator import events
from aria.orchestrator.workflows.executor import (
thread,
@@ -43,7 +43,7 @@ def test_execute(executor):
expected_value = 'value'
successful_task = MockTask(mock_successful_task)
failing_task = MockTask(mock_failing_task)
- task_with_inputs = MockTask(mock_task_with_input, inputs={'input': expected_value})
+ task_with_inputs = MockTask(mock_task_with_input, inputs=dict(input='value'))
for task in [successful_task, failing_task, task_with_inputs]:
executor.execute(task)
@@ -105,8 +105,9 @@ class MockTask(object):
self.exception = None
self.id = str(uuid.uuid4())
name = func.__name__
- operation = 'tests.orchestrator.workflows.executor.test_executor.{name}'.format(name=name)
- self.operation_mapping = operation
+ implementation = 'tests.orchestrator.workflows.executor.test_executor.{name}'.format(
+ name=name)
+ self.implementation = implementation
self.logger = logging.getLogger()
self.name = name
self.inputs = inputs or {}
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor.py b/tests/orchestrator/workflows/executor/test_process_executor.py
index ff5dce6..e904eb3 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor.py
@@ -42,7 +42,7 @@ class TestProcessExecutor(object):
def test_plugin_execution(self, executor, mock_plugin):
task = MockTask(plugin=mock_plugin,
- operation='mock_plugin1.operation')
+ implementation='mock_plugin1.operation')
queue = Queue.Queue()
@@ -131,11 +131,11 @@ class MockTask(object):
INFINITE_RETRIES = aria_model.Task.INFINITE_RETRIES
- def __init__(self, plugin, operation):
+ def __init__(self, plugin, implementation):
self.id = str(uuid.uuid4())
- self.operation_mapping = operation
+ self.implementation = implementation
self.logger = logging.getLogger()
- self.name = operation
+ self.name = implementation
self.inputs = {}
self.context = MockContext()
self.retry_count = 0
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor_extension.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_extension.py b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
index 18957f1..ac95554 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_extension.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
@@ -30,15 +30,18 @@ def test_decorate_extension(context, executor):
inputs = {'input1': 1, 'input2': 2}
def get_node_instance(ctx):
- return ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ return ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
@workflow
def mock_workflow(ctx, graph):
node_instance = get_node_instance(ctx)
op = 'test.op'
- op_dict = {'operation': '{0}.{1}'.format(__name__, _mock_operation.__name__)}
- node_instance.node.operations['test.op'] = op_dict
- task = api.task.OperationTask.node_instance(instance=node_instance, name=op, inputs=inputs)
+ node_instance.interfaces = [mock.models.get_interface(
+ op,
+ operation_kwargs=dict(implementation='{0}.{1}'.format(__name__,
+ _mock_operation.__name__))
+ )]
+ task = api.task.OperationTask.node(instance=node_instance, name=op, inputs=inputs)
graph.add_tasks(task)
return graph
graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
@@ -55,7 +58,7 @@ class MockProcessExecutorExtension(object):
def decorate(self):
def decorator(function):
def wrapper(ctx, **operation_inputs):
- ctx.node_instance.runtime_properties['out'] = {'wrapper_inputs': operation_inputs}
+ ctx.node.runtime_properties['out'] = {'wrapper_inputs': operation_inputs}
function(ctx=ctx, **operation_inputs)
return wrapper
return decorator
@@ -63,7 +66,7 @@ class MockProcessExecutorExtension(object):
@operation
def _mock_operation(ctx, **operation_inputs):
- ctx.node_instance.runtime_properties['out']['function_inputs'] = operation_inputs
+ ctx.node.runtime_properties['out']['function_inputs'] = operation_inputs
@pytest.fixture
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
index e383859..d3b3300 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
@@ -45,13 +45,13 @@ def test_track_changes_of_failed_operation(context, executor):
def _assert_tracked_changes_are_applied(context):
- instance = context.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ instance = context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
assert instance.runtime_properties == _TEST_RUNTIME_PROPERTIES
def _update_runtime_properties(context):
- context.node_instance.runtime_properties.clear()
- context.node_instance.runtime_properties.update(_TEST_RUNTIME_PROPERTIES)
+ context.node.runtime_properties.clear()
+ context.node.runtime_properties.update(_TEST_RUNTIME_PROPERTIES)
def test_refresh_state_of_tracked_attributes(context, executor):
@@ -66,7 +66,7 @@ def test_apply_tracked_changes_during_an_operation(context, executor):
'changed_but_refreshed': {'some': 'newer', 'properties': 'right there'}
}
- expected_initial = context.model.node_instance.get_by_name(
+ expected_initial = context.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
out = _run_workflow(context=context, executor=executor, op_func=_mock_updating_operation,
@@ -87,17 +87,18 @@ def test_apply_tracked_changes_during_an_operation(context, executor):
def _run_workflow(context, executor, op_func, inputs=None):
@workflow
def mock_workflow(ctx, graph):
- node_instance = ctx.model.node_instance.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node_instance.node.operations['test.op'] = {'operation': _operation_mapping(op_func)}
- task = api.task.OperationTask.node_instance(instance=node_instance, name='test.op',
- inputs=inputs or {})
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(
+ 'test.op', operation_kwargs=dict(implementation=_operation_mapping(op_func)))]
+ task = api.task.OperationTask.node(instance=node,
+ name='test.op',
+ inputs=inputs or {})
graph.add_tasks(task)
return graph
graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
eng = engine.Engine(executor=executor, workflow_context=context, tasks_graph=graph)
eng.execute()
- return context.model.node_instance.get_by_name(
+ return context.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties.get('out')
@@ -114,25 +115,25 @@ def _mock_fail_operation(ctx):
@operation
def _mock_refreshing_operation(ctx):
- out = {'initial': copy.deepcopy(ctx.node_instance.runtime_properties)}
- ctx.node_instance.runtime_properties.update({'some': 'new', 'properties': 'right here'})
- out['after_change'] = copy.deepcopy(ctx.node_instance.runtime_properties)
- ctx.model.node_instance.refresh(ctx.node_instance)
- out['after_refresh'] = copy.deepcopy(ctx.node_instance.runtime_properties)
- ctx.node_instance.runtime_properties['out'] = out
+ out = {'initial': copy.deepcopy(ctx.node.runtime_properties)}
+ ctx.node.runtime_properties.update({'some': 'new', 'properties': 'right here'})
+ out['after_change'] = copy.deepcopy(ctx.node.runtime_properties)
+ ctx.model.node.refresh(ctx.node)
+ out['after_refresh'] = copy.deepcopy(ctx.node.runtime_properties)
+ ctx.node.runtime_properties['out'] = out
@operation
def _mock_updating_operation(ctx, committed, changed_but_refreshed):
- out = {'initial': copy.deepcopy(ctx.node_instance.runtime_properties)}
- ctx.node_instance.runtime_properties.update(committed)
- ctx.model.node_instance.update(ctx.node_instance)
- out['after_update'] = copy.deepcopy(ctx.node_instance.runtime_properties)
- ctx.node_instance.runtime_properties.update(changed_but_refreshed)
- out['after_change'] = copy.deepcopy(ctx.node_instance.runtime_properties)
- ctx.model.node_instance.refresh(ctx.node_instance)
- out['after_refresh'] = copy.deepcopy(ctx.node_instance.runtime_properties)
- ctx.node_instance.runtime_properties['out'] = out
+ out = {'initial': copy.deepcopy(ctx.node.runtime_properties)}
+ ctx.node.runtime_properties.update(committed)
+ ctx.model.node.update(ctx.node)
+ out['after_update'] = copy.deepcopy(ctx.node.runtime_properties)
+ ctx.node.runtime_properties.update(changed_but_refreshed)
+ out['after_change'] = copy.deepcopy(ctx.node.runtime_properties)
+ ctx.model.node.refresh(ctx.node)
+ out['after_refresh'] = copy.deepcopy(ctx.node.runtime_properties)
+ ctx.node.runtime_properties['out'] = out
def _operation_mapping(func):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/resources/scripts/test_ssh.sh
----------------------------------------------------------------------
diff --git a/tests/resources/scripts/test_ssh.sh b/tests/resources/scripts/test_ssh.sh
index 6f18278..90202c7 100644
--- a/tests/resources/scripts/test_ssh.sh
+++ b/tests/resources/scripts/test_ssh.sh
@@ -4,7 +4,7 @@ set -u
set -e
test_run_script_basic() {
- ctx node-instance runtime-properties test_value $test_value
+ ctx node runtime-properties test_value $test_value
}
test_run_script_as_sudo() {
@@ -12,7 +12,7 @@ test_run_script_as_sudo() {
}
test_run_script_default_base_dir() {
- ctx node-instance runtime-properties work_dir $PWD
+ ctx node runtime-properties work_dir $PWD
}
test_run_script_with_hide() {
@@ -20,44 +20,44 @@ test_run_script_with_hide() {
}
test_run_script_process_config() {
- ctx node-instance runtime-properties env_value $test_value_env
- ctx node-instance runtime-properties bash_version $BASH_VERSION
- ctx node-instance runtime-properties arg1_value $1
- ctx node-instance runtime-properties arg2_value $2
- ctx node-instance runtime-properties cwd $PWD
- ctx node-instance runtime-properties ctx_path $(which ctx)
+ ctx node runtime-properties env_value $test_value_env
+ ctx node runtime-properties bash_version $BASH_VERSION
+ ctx node runtime-properties arg1_value $1
+ ctx node runtime-properties arg2_value $2
+ ctx node runtime-properties cwd $PWD
+ ctx node runtime-properties ctx_path $(which ctx)
}
test_run_script_command_prefix() {
- ctx node-instance runtime-properties dollar_dash $-
+ ctx node runtime-properties dollar_dash $-
}
test_run_script_reuse_existing_ctx_1() {
- ctx node-instance runtime-properties test_value1 $test_value1
+ ctx node runtime-properties test_value1 $test_value1
}
test_run_script_reuse_existing_ctx_2() {
- ctx node-instance runtime-properties test_value2 $test_value2
+ ctx node runtime-properties test_value2 $test_value2
}
test_run_script_download_resource_plain() {
local destination=$(mktemp)
ctx download-resource ${destination} test_resource
- ctx node-instance runtime-properties test_value "$(cat ${destination})"
+ ctx node runtime-properties test_value "$(cat ${destination})"
}
test_run_script_download_resource_and_render() {
local destination=$(mktemp)
ctx download-resource-and-render ${destination} test_resource
- ctx node-instance runtime-properties test_value "$(cat ${destination})"
+ ctx node runtime-properties test_value "$(cat ${destination})"
}
test_run_script_inputs_as_env_variables_no_override() {
- ctx node-instance runtime-properties test_value "$custom_env_var"
+ ctx node runtime-properties test_value "$custom_env_var"
}
test_run_script_inputs_as_env_variables_process_env_override() {
- ctx node-instance runtime-properties test_value "$custom_env_var"
+ ctx node runtime-properties test_value "$custom_env_var"
}
test_run_script_error_in_script() {
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/resources/service_templates/node-cellar/workflows.py
----------------------------------------------------------------------
diff --git a/tests/resources/service_templates/node-cellar/workflows.py b/tests/resources/service_templates/node-cellar/workflows.py
index b3546bb..fff78bf 100644
--- a/tests/resources/service_templates/node-cellar/workflows.py
+++ b/tests/resources/service_templates/node-cellar/workflows.py
@@ -11,9 +11,6 @@ def maintenance(ctx, graph, enabled):
operation = 'Maintenance.enable' if enabled else 'Maintenance.disable'
- for node_instance in ctx.model.node_instance.iter():
- if operation in node_instance.node.operations:
- task = OperationTask.node_instance(
- instance=node_instance,
- name=operation)
- graph.add_tasks(task)
+ for node in ctx.model.node.iter():
+ for interface in node.interfaces.filter_by(name='Maintenance', type_name='Maintenance'):
+ graph.add_tasks(OperationTask.node(instance=node, name=operation))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index b798e01..4278831 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -14,8 +14,8 @@
# limitations under the License.
import os
import platform
-from tempfile import mkdtemp
from shutil import rmtree
+from tempfile import mkdtemp
from sqlalchemy import (
create_engine,
@@ -29,13 +29,14 @@ from sqlalchemy import (
from aria.storage import (
model,
- structure,
type as aria_type,
+ structure,
+ modeling
)
-class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method
- __tablename__ = 'mock_models'
+class MockModel(model.aria_declarative_base, structure.ModelMixin): #pylint: disable=abstract-method
+ __tablename__ = 'mock_model'
model_dict = Column(aria_type.Dict)
model_list = Column(aria_type.List)
value = Column(Integer)
@@ -64,7 +65,7 @@ def release_sqlite_storage(storage):
session.rollback()
session.close()
for engine in set(mapi._engine for mapi in mapis):
- model.DeclarativeBase.metadata.drop_all(engine)
+ model.aria_declarative_base.metadata.drop_all(engine)
def init_inmemory_model_storage():
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
index 771342c..08d5ae0 100644
--- a/tests/storage/test_instrumentation.py
+++ b/tests/storage/test_instrumentation.py
@@ -17,17 +17,16 @@ import pytest
from sqlalchemy import Column, Text, Integer, event
from aria.storage import (
- model,
structure,
- type as aria_type,
ModelStorage,
sql_mapi,
instrumentation,
- exceptions
+ exceptions,
+ type as aria_type,
+ model
)
from ..storage import release_sqlite_storage, init_inmemory_model_storage
-
STUB = instrumentation._STUB
Value = instrumentation._Value
instruments_holder = []
@@ -346,15 +345,15 @@ class _MockModel(structure.ModelMixin):
string2 = Column(Text)
-class MockModel1(model.DeclarativeBase, _MockModel):
- __tablename__ = 'mock_model1'
+class MockModel1(_MockModel, model.aria_declarative_base):
+ __tablename__ = 'mock_model_1'
-class MockModel2(model.DeclarativeBase, _MockModel):
- __tablename__ = 'mock_model2'
+class MockModel2(_MockModel, model.aria_declarative_base):
+ __tablename__ = 'mock_model_2'
-class StrictMockModel(model.DeclarativeBase):
+class StrictMockModel(structure.ModelMixin, model.aria_declarative_base):
__tablename__ = 'strict_mock_model'
strict_dict = Column(aria_type.StrictDict(basestring, basestring))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 34cc5df..f88080a 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -17,9 +17,9 @@ import pytest
from aria.storage import (
ModelStorage,
- model,
exceptions,
sql_mapi,
+ modeling,
)
from aria import application_model_storage
from ..storage import release_sqlite_storage, init_inmemory_model_storage
@@ -38,7 +38,7 @@ def storage():
@pytest.fixture(scope='module', autouse=True)
def module_cleanup():
- model.DeclarativeBase.metadata.remove(MockModel.__table__) #pylint: disable=no-member
+ modeling.model.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member
def test_storage_base(storage):
@@ -63,14 +63,41 @@ def test_model_storage(storage):
def test_application_storage_factory():
storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
initiator=init_inmemory_model_storage)
+
+ assert storage.parameter
+ assert storage.mapping_template
+ assert storage.substitution_template
+ assert storage.service_template
+ assert storage.node_template
+ assert storage.group_template
+ assert storage.interface_template
+ assert storage.operation_template
+ assert storage.artifact_template
+ assert storage.policy_template
+ assert storage.group_policy_template
+ assert storage.group_policy_trigger_template
+ assert storage.requirement_template
+ assert storage.capability_template
+
+ assert storage.mapping
+ assert storage.substitution
+ assert storage.service_instance
assert storage.node
- assert storage.node_instance
- assert storage.plugin
- assert storage.blueprint
- assert storage.deployment
- assert storage.deployment_update
- assert storage.deployment_update_step
- assert storage.deployment_modification
+ assert storage.group
+ assert storage.interface
+ assert storage.operation
+ assert storage.capability
+ assert storage.artifact
+ assert storage.policy
+ assert storage.group_policy
+ assert storage.group_policy_trigger
+ assert storage.relationship
+
assert storage.execution
+ assert storage.service_instance_update
+ assert storage.service_instance_update_step
+ assert storage.service_instance_modification
+ assert storage.plugin
+ assert storage.task
release_sqlite_storage(storage)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_models.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py
index 6450152..1e8d6b5 100644
--- a/tests/storage/test_models.py
+++ b/tests/storage/test_models.py
@@ -22,22 +22,21 @@ from aria.storage import (
exceptions,
sql_mapi,
)
-from aria.storage.model import (
- DeploymentUpdateStep,
- Blueprint,
+from aria.storage.modeling.model import (
+ ServiceTemplate,
+ ServiceInstance,
+ ServiceInstanceUpdate,
+ ServiceInstanceUpdateStep,
+ ServiceInstanceModification,
Execution,
Task,
Plugin,
- Deployment,
- Node,
- NodeInstance,
Relationship,
- RelationshipInstance,
- DeploymentUpdate,
- DeploymentModification,
+ NodeTemplate,
+ Node,
+ Parameter
)
-
from tests import mock
from ..storage import release_sqlite_storage, init_inmemory_model_storage
@@ -58,61 +57,56 @@ def _empty_storage():
initiator=init_inmemory_model_storage)
-def _blueprint_storage():
+def _service_template_storage():
storage = _empty_storage()
- blueprint = mock.models.get_blueprint()
- storage.blueprint.put(blueprint)
+ service_template = mock.models.get_blueprint()
+ storage.service_template.put(service_template)
return storage
-def _deployment_storage():
- storage = _blueprint_storage()
- deployment = mock.models.get_deployment(storage.blueprint.list()[0])
- storage.deployment.put(deployment)
+def _service_instance_storage():
+ storage = _service_template_storage()
+ service_instance = mock.models.get_deployment(
+ storage.service_template.get_by_name(mock.models.BLUEPRINT_NAME))
+ storage.service_instance.put(service_instance)
return storage
-def _deployment_update_storage():
- storage = _deployment_storage()
- deployment_update = DeploymentUpdate(
- deployment=storage.deployment.list()[0],
+def _service_instance_update_storage():
+ storage = _service_instance_storage()
+ service_instance_update = ServiceInstanceUpdate(
+ service_instance=storage.service_instance.list()[0],
created_at=now,
- deployment_plan={},
+ service_instance_plan={},
)
- storage.deployment_update.put(deployment_update)
+ storage.service_instance_update.put(service_instance_update)
return storage
-def _node_storage():
- storage = _deployment_storage()
- node = mock.models.get_dependency_node(storage.deployment.list()[0])
- storage.node.put(node)
+def _node_template_storage():
+ storage = _service_instance_storage()
+ node_template = mock.models.get_dependency_node(storage.service_instance.list()[0])
+ storage.node_template.put(node_template)
return storage
def _nodes_storage():
- storage = _deployment_storage()
- dependent_node = mock.models.get_dependent_node(storage.deployment.list()[0])
- dependency_node = mock.models.get_dependency_node(storage.deployment.list()[0])
- storage.node.put(dependent_node)
- storage.node.put(dependency_node)
- return storage
-
-
-def _node_instances_storage():
storage = _nodes_storage()
- dependent_node = storage.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- dependency_node = storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- dependency_node_instance = mock.models.get_dependency_node_instance(dependency_node)
- dependent_node_instance = mock.models.get_dependent_node_instance(dependent_node)
- storage.node_instance.put(dependency_node_instance)
- storage.node_instance.put(dependent_node_instance)
+ service_instance = storage.service_instance.get_by_name(mock.models.DEPLOYMENT_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.get_dependency_node_instance(dependency_node_template,
+ service_instance)
+ dependent_node = mock.models.get_dependent_node_instance(dependent_node_template,
+ service_instance)
+ storage.node.put(dependency_node)
+ storage.node.put(dependent_node)
return storage
def _execution_storage():
- storage = _deployment_storage()
- execution = mock.models.get_execution(storage.deployment.list()[0])
+ storage = _service_instance_storage()
+ execution = mock.models.get_execution(storage.service_instance.list()[0])
plugin = mock.models.get_plugin()
storage.execution.put(execution)
storage.plugin.put(plugin)
@@ -126,26 +120,26 @@ def empty_storage():
@pytest.fixture
-def blueprint_storage():
- with sql_storage(_blueprint_storage) as storage:
+def service_template_storage():
+ with sql_storage(_service_template_storage) as storage:
yield storage
@pytest.fixture
-def deployment_storage():
- with sql_storage(_deployment_storage) as storage:
+def service_instance_storage():
+ with sql_storage(_service_instance_storage) as storage:
yield storage
@pytest.fixture
-def deployment_update_storage():
- with sql_storage(_deployment_update_storage) as storage:
+def service_instance_update_storage():
+ with sql_storage(_service_instance_update_storage) as storage:
yield storage
@pytest.fixture
-def node_storage():
- with sql_storage(_node_storage) as storage:
+def node_template_storage():
+ with sql_storage(_node_template_storage) as storage:
yield storage
@@ -156,12 +150,6 @@ def nodes_storage():
@pytest.fixture
-def node_instances_storage():
- with sql_storage(_node_instances_storage) as storage:
- yield storage
-
-
-@pytest.fixture
def execution_storage():
with sql_storage(_execution_storage) as storage:
yield storage
@@ -171,17 +159,17 @@ m_cls = type('MockClass')
now = datetime.utcnow()
-def _test_model(is_valid, storage, model_name, model_cls, model_kwargs):
+def _test_model(is_valid, storage, model_cls, model_kwargs):
if is_valid:
model = model_cls(**model_kwargs)
- getattr(storage, model_name).put(model)
+ getattr(storage, model_cls.__modelname__).put(model)
return model
else:
- with pytest.raises(exceptions.StorageError):
- getattr(storage, model_name).put(model_cls(**model_kwargs))
+ with pytest.raises((exceptions.StorageError, TypeError),):
+ getattr(storage, model_cls.__modelname__).put(model_cls(**model_kwargs))
-class TestBlueprint(object):
+class TestServiceTemplate(object):
@pytest.mark.parametrize(
'is_valid, plan, description, created_at, updated_at, main_file_name',
@@ -198,74 +186,71 @@ class TestBlueprint(object):
updated_at, main_file_name):
_test_model(is_valid=is_valid,
storage=empty_storage,
- model_name='blueprint',
- model_cls=Blueprint,
- model_kwargs=dict(plan=plan,
- description=description,
- created_at=created_at,
- updated_at=updated_at,
- main_file_name=main_file_name))
+ model_cls=ServiceTemplate,
+ model_kwargs=dict(
+ plan=plan,
+ description=description,
+ created_at=created_at,
+ updated_at=updated_at,
+ main_file_name=main_file_name)
+ )
-class TestDeployment(object):
+class TestServiceInstance(object):
@pytest.mark.parametrize(
- 'is_valid, name, created_at, description, inputs, groups, permalink, policy_triggers, '
+ 'is_valid, name, created_at, description, inputs, permalink, policy_triggers, '
'policy_types, outputs, scaling_groups, updated_at, workflows',
[
- (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, {}, 'perlnk', {}, {}, {}, {}, now, {}),
- (False, 'name', now, 'desc', {}, 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, {}, now, {}),
- (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, m_cls, now, {}),
- (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, m_cls, {}),
- (False, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, 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', None, {}, 'perlnk', {}, {}, {}, {}, now, {}),
- (True, 'name', now, 'desc', {}, None, 'perlnk', {}, {}, {}, {}, now, {}),
- (True, 'name', now, 'desc', {}, {}, None, {}, {}, {}, {}, now, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', None, {}, {}, {}, now, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, None, {}, {}, now, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, None, {}, now, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, None, now, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, None, {}),
- (True, 'name', now, 'desc', {}, {}, 'perlnk', {}, {}, {}, {}, now, None),
+ (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, {}, now, {}),
+ (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], m_cls, now, {}),
+ (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, m_cls, {}),
+ (False, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, 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', None, {}, [], {}, now, {}),
+ (True, 'name', now, 'desc', [], 'perlnk', {}, None, [], {}, now, {}),
+ (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], None, now, {}),
+ (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, None, {}),
+ (True, 'name', now, 'desc', [], 'perlnk', {}, {}, [], {}, now, None),
]
)
- def test_deployment_model_creation(self, deployment_storage, is_valid, name, created_at,
- description, inputs, groups, permalink, policy_triggers,
+ def test_deployment_model_creation(self, service_instance_storage, is_valid, name, created_at,
+ description, inputs, permalink, policy_triggers,
policy_types, outputs, scaling_groups, updated_at,
workflows):
- deployment = _test_model(is_valid=is_valid,
- storage=deployment_storage,
- model_name='deployment',
- model_cls=Deployment,
- model_kwargs=dict(
- name=name,
- blueprint=deployment_storage.blueprint.list()[0],
- created_at=created_at,
- description=description,
- inputs=inputs,
- groups=groups,
- permalink=permalink,
- policy_triggers=policy_triggers,
- policy_types=policy_types,
- outputs=outputs,
- scaling_groups=scaling_groups,
- updated_at=updated_at,
- workflows=workflows
- ))
+ service_instance = _test_model(
+ is_valid=is_valid,
+ storage=service_instance_storage,
+ model_cls=ServiceInstance,
+ model_kwargs=dict(
+ name=name,
+ service_template=service_instance_storage.service_template.list()[0],
+ created_at=created_at,
+ description=description,
+ inputs=inputs,
+ permalink=permalink,
+ policy_triggers=policy_triggers,
+ policy_types=policy_types,
+ outputs=outputs,
+ scaling_groups=scaling_groups,
+ updated_at=updated_at,
+ workflows=workflows
+ ))
if is_valid:
- assert deployment.blueprint == deployment_storage.blueprint.list()[0]
+ assert service_instance.service_template == \
+ service_instance_storage.service_template.list()[0]
class TestExecution(object):
@@ -289,27 +274,27 @@ class TestExecution(object):
(True, now, now, now, 'error', False, None, Execution.STARTED, 'wf_name'),
]
)
- def test_execution_model_creation(self, deployment_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=deployment_storage,
- model_name='execution',
- model_cls=Execution,
- model_kwargs=dict(
- deployment=deployment_storage.deployment.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,
- ))
+ def test_execution_model_creation(self, service_instance_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_instance_storage,
+ model_cls=Execution,
+ model_kwargs=dict(
+ service_instance=service_instance_storage.service_instance.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.deployment == deployment_storage.deployment.list()[0]
- assert execution.blueprint == deployment_storage.blueprint.list()[0]
+ assert execution.service_instance == service_instance_storage.service_instance.list()[0]
+ assert execution.service_template == service_instance_storage.service_template.list()[0]
def test_execution_status_transition(self):
def create_execution(status):
@@ -376,10 +361,11 @@ class TestExecution(object):
execution.status = transitioned_status
-class TestDeploymentUpdate(object):
+class TestServiceInstanceUpdate(object):
@pytest.mark.parametrize(
- 'is_valid, created_at, deployment_plan, deployment_update_node_instances, '
- 'deployment_update_deployment, deployment_update_nodes, modified_entity_ids, state',
+ 'is_valid, created_at, deployment_plan, service_instance_update_node_instances, '
+ 'service_instance_update_service_instance, service_instance_update_nodes, '
+ 'modified_entity_ids, state',
[
(False, m_cls, {}, {}, {}, [], {}, 'state'),
(False, now, m_cls, {}, {}, [], {}, 'state'),
@@ -397,74 +383,74 @@ class TestDeploymentUpdate(object):
(True, now, {}, {}, {}, [], {}, None),
]
)
- def test_deployment_update_model_creation(self, deployment_storage, is_valid, created_at,
- deployment_plan, deployment_update_node_instances,
- deployment_update_deployment, deployment_update_nodes,
- modified_entity_ids, state):
- deployment_update = _test_model(
+ def test_service_instance_update_model_creation(
+ self, service_instance_storage, is_valid, created_at, deployment_plan,
+ service_instance_update_node_instances, service_instance_update_service_instance,
+ service_instance_update_nodes, modified_entity_ids, state):
+ service_instance_update = _test_model(
is_valid=is_valid,
- storage=deployment_storage,
- model_name='deployment_update',
- model_cls=DeploymentUpdate,
+ storage=service_instance_storage,
+ model_cls=ServiceInstanceUpdate,
model_kwargs=dict(
- deployment=deployment_storage.deployment.list()[0],
+ service_instance=service_instance_storage.service_instance.list()[0],
created_at=created_at,
- deployment_plan=deployment_plan,
- deployment_update_node_instances=deployment_update_node_instances,
- deployment_update_deployment=deployment_update_deployment,
- deployment_update_nodes=deployment_update_nodes,
+ service_instance_plan=deployment_plan,
+ service_instance_update_node_instances=service_instance_update_node_instances,
+ service_instance_update_service_instance=service_instance_update_service_instance,
+ service_instance_update_nodes=service_instance_update_nodes,
modified_entity_ids=modified_entity_ids,
state=state,
))
if is_valid:
- assert deployment_update.deployment == deployment_storage.deployment.list()[0]
+ assert service_instance_update.service_instance == \
+ service_instance_storage.service_instance.list()[0]
-class TestDeploymentUpdateStep(object):
+class TestServiceInstanceUpdateStep(object):
@pytest.mark.parametrize(
'is_valid, action, entity_id, entity_type',
[
- (False, m_cls, 'id', DeploymentUpdateStep.ENTITY_TYPES.NODE),
- (False, DeploymentUpdateStep.ACTION_TYPES.ADD, m_cls,
- DeploymentUpdateStep.ENTITY_TYPES.NODE),
- (False, DeploymentUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
+ (False, m_cls, 'id', ServiceInstanceUpdateStep.ENTITY_TYPES.NODE),
+ (False, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, m_cls,
+ ServiceInstanceUpdateStep.ENTITY_TYPES.NODE),
+ (False, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
- (True, DeploymentUpdateStep.ACTION_TYPES.ADD, 'id',
- DeploymentUpdateStep.ENTITY_TYPES.NODE)
+ (True, ServiceInstanceUpdateStep.ACTION_TYPES.ADD, 'id',
+ ServiceInstanceUpdateStep.ENTITY_TYPES.NODE)
]
)
- def test_deployment_update_step_model_creation(self, deployment_update_storage, is_valid,
+ def test_deployment_update_step_model_creation(self, service_instance_update_storage, is_valid,
action, entity_id, entity_type):
- deployment_update_step = _test_model(
+ service_instance_update_step = _test_model(
is_valid=is_valid,
- storage=deployment_update_storage,
- model_name='deployment_update_step',
- model_cls=DeploymentUpdateStep,
+ storage=service_instance_update_storage,
+ model_cls=ServiceInstanceUpdateStep,
model_kwargs=dict(
- deployment_update=deployment_update_storage.deployment_update.list()[0],
+ service_instance_update=
+ service_instance_update_storage.service_instance_update.list()[0],
action=action,
entity_id=entity_id,
entity_type=entity_type
))
if is_valid:
- assert deployment_update_step.deployment_update == \
- deployment_update_storage.deployment_update.list()[0]
+ assert service_instance_update_step.service_instance_update == \
+ service_instance_update_storage.service_instance_update.list()[0]
def test_deployment_update_step_order(self):
- add_node = DeploymentUpdateStep(
+ add_node = ServiceInstanceUpdateStep(
id='add_step',
action='add',
entity_type='node',
entity_id='node_id')
- modify_node = DeploymentUpdateStep(
+ modify_node = ServiceInstanceUpdateStep(
id='modify_step',
action='modify',
entity_type='node',
entity_id='node_id')
- remove_node = DeploymentUpdateStep(
+ remove_node = ServiceInstanceUpdateStep(
id='remove_step',
action='remove',
entity_type='node',
@@ -476,13 +462,13 @@ class TestDeploymentUpdateStep(object):
assert remove_node < modify_node < add_node
assert not remove_node > modify_node > add_node
- add_rel = DeploymentUpdateStep(
+ add_rel = ServiceInstanceUpdateStep(
id='add_step',
action='add',
entity_type='relationship',
entity_id='relationship_id')
- remove_rel = DeploymentUpdateStep(
+ remove_rel = ServiceInstanceUpdateStep(
id='remove_step',
action='remove',
entity_type='relationship',
@@ -494,140 +480,84 @@ class TestDeploymentUpdateStep(object):
class TestDeploymentModification(object):
@pytest.mark.parametrize(
- 'is_valid, context, created_at, ended_at, modified_nodes, node_instances, status',
+ 'is_valid, context, created_at, ended_at, modified_node_templates, nodes, status',
[
- (False, m_cls, now, now, {}, {}, DeploymentModification.STARTED),
- (False, {}, m_cls, now, {}, {}, DeploymentModification.STARTED),
- (False, {}, now, m_cls, {}, {}, DeploymentModification.STARTED),
- (False, {}, now, now, m_cls, {}, DeploymentModification.STARTED),
- (False, {}, now, now, {}, m_cls, DeploymentModification.STARTED),
+ (False, m_cls, now, now, {}, {}, ServiceInstanceModification.STARTED),
+ (False, {}, m_cls, now, {}, {}, ServiceInstanceModification.STARTED),
+ (False, {}, now, m_cls, {}, {}, ServiceInstanceModification.STARTED),
+ (False, {}, now, now, m_cls, {}, ServiceInstanceModification.STARTED),
+ (False, {}, now, now, {}, m_cls, ServiceInstanceModification.STARTED),
(False, {}, now, now, {}, {}, m_cls),
- (True, {}, now, now, {}, {}, DeploymentModification.STARTED),
- (True, {}, now, None, {}, {}, DeploymentModification.STARTED),
- (True, {}, now, now, None, {}, DeploymentModification.STARTED),
- (True, {}, now, now, {}, None, DeploymentModification.STARTED),
+ (True, {}, now, now, {}, {}, ServiceInstanceModification.STARTED),
+ (True, {}, now, None, {}, {}, ServiceInstanceModification.STARTED),
+ (True, {}, now, now, None, {}, ServiceInstanceModification.STARTED),
+ (True, {}, now, now, {}, None, ServiceInstanceModification.STARTED),
]
)
- def test_deployment_modification_model_creation(self, deployment_storage, is_valid, context,
- created_at, ended_at, modified_nodes,
- node_instances, status):
+ def test_deployment_modification_model_creation(
+ self, service_instance_storage, is_valid, context, created_at, ended_at,
+ modified_node_templates, nodes, status):
deployment_modification = _test_model(
is_valid=is_valid,
- storage=deployment_storage,
- model_name='deployment_modification',
- model_cls=DeploymentModification,
+ storage=service_instance_storage,
+ model_cls=ServiceInstanceModification,
model_kwargs=dict(
- deployment=deployment_storage.deployment.list()[0],
+ service_instance=service_instance_storage.service_instance.list()[0],
context=context,
created_at=created_at,
ended_at=ended_at,
- modified_nodes=modified_nodes,
- node_instances=node_instances,
+ modified_nodes=modified_node_templates,
+ node_instances=nodes,
status=status,
))
if is_valid:
- assert deployment_modification.deployment == deployment_storage.deployment.list()[0]
+ assert deployment_modification.service_instance == \
+ service_instance_storage.service_instance.list()[0]
-class TestNode(object):
+class TestNodeTemplate(object):
@pytest.mark.parametrize(
- 'is_valid, name, deploy_number_of_instances, max_number_of_instances, '
- 'min_number_of_instances, number_of_instances, planned_number_of_instances, plugins, '
- 'properties, operations, type, type_hierarchy',
+ 'is_valid, name, default_instances, max_instances, min_instances, plugins, properties, '
+ 'type_name, type_hierarchy',
[
- (False, m_cls, 1, 1, 1, 1, 1, [], {}, {}, 'type', []),
- (False, 'name', m_cls, 1, 1, 1, 1, [], {}, {}, 'type', []),
- (False, 'name', 1, m_cls, 1, 1, 1, [], {}, {}, 'type', []),
- (False, 'name', 1, 1, m_cls, 1, 1, [], {}, {}, 'type', []),
- (False, 'name', 1, 1, 1, m_cls, 1, [], {}, {}, 'type', []),
- (False, 'name', 1, 1, 1, 1, m_cls, [], {}, {}, 'type', []),
- (False, 'name', 1, 1, 1, 1, 1, m_cls, {}, {}, 'type', []),
- (False, 'name', 1, 1, 1, 1, 1, [], m_cls, {}, 'type', []),
- (False, 'name', 1, 1, 1, 1, 1, [], {}, m_cls, 'type', []),
- (False, 'name', 1, 1, 1, 1, 1, [], {}, {}, m_cls, []),
- (False, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', m_cls),
-
- (True, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', []),
- (True, 'name', 1, 1, 1, 1, 1, None, {}, {}, 'type', []),
- (True, 'name', 1, 1, 1, 1, 1, [], None, {}, 'type', []),
- (True, 'name', 1, 1, 1, 1, 1, [], {}, None, 'type', []),
- (True, 'name', 1, 1, 1, 1, 1, [], {}, {}, 'type', None),
+ (False, m_cls, 1, 1, 1, [], [], 'type', []),
+ (False, 'name', m_cls, 1, 1, [], [], 'type', []),
+ (False, 'name', 1, m_cls, 1, [], [], 'type', []),
+ (False, 'name', 1, 1, m_cls, [], [], 'type', []),
+ (False, 'name', 1, 1, 1, m_cls, [], 'type', []),
+ (False, 'name', 1, 1, 1, [], [], m_cls, []),
+ (False, 'name', 1, 1, 1, [], [], 'type', m_cls),
+ #
+ (True, 'name', 1, 1, 1, [], [], 'type', []),
+ (True, 'name', 1, 1, 1, None, [], 'type', []),
+ (True, 'name', 1, 1, 1, [], [], 'type', None),
]
)
- def test_node_model_creation(self, deployment_storage, is_valid, name,
- deploy_number_of_instances, max_number_of_instances,
- min_number_of_instances, number_of_instances,
- planned_number_of_instances, plugins,
- properties, operations, type, type_hierarchy):
- node = _test_model(
+ def test_node_model_creation(self, service_instance_storage, is_valid, name,
+ default_instances, max_instances, min_instances, plugins,
+ properties, type_name, type_hierarchy):
+ node_template = _test_model(
is_valid=is_valid,
- storage=deployment_storage,
- model_name='node',
- model_cls=Node,
+ storage=service_instance_storage,
+ model_cls=NodeTemplate,
model_kwargs=dict(
name=name,
- deploy_number_of_instances=deploy_number_of_instances,
- max_number_of_instances=max_number_of_instances,
- min_number_of_instances=min_number_of_instances,
- number_of_instances=number_of_instances,
- planned_number_of_instances=planned_number_of_instances,
+ default_instances=default_instances,
+ max_instances=max_instances,
+ min_instances=min_instances,
plugins=plugins,
properties=properties,
- operations=operations,
- type=type,
+ type_name=type_name,
type_hierarchy=type_hierarchy,
- deployment=deployment_storage.deployment.list()[0]
+ service_template=service_instance_storage.service_template.list()[0]
))
if is_valid:
- assert node.deployment == deployment_storage.deployment.list()[0]
+ assert node_template.service_template == \
+ service_instance_storage.service_template.list()[0]
-class TestRelationship(object):
- @pytest.mark.parametrize(
- 'is_valid, source_interfaces, source_operations, target_interfaces, target_operations, '
- 'type, type_hierarchy, properties',
- [
- (False, m_cls, {}, {}, {}, 'type', [], {}),
- (False, {}, m_cls, {}, {}, 'type', [], {}),
- (False, {}, {}, m_cls, {}, 'type', [], {}),
- (False, {}, {}, {}, m_cls, 'type', [], {}),
- (False, {}, {}, {}, {}, m_cls, [], {}),
- (False, {}, {}, {}, {}, 'type', m_cls, {}),
- (False, {}, {}, {}, {}, 'type', [], m_cls),
-
- (True, {}, {}, {}, {}, 'type', [], {}),
- (True, None, {}, {}, {}, 'type', [], {}),
- (True, {}, {}, None, {}, 'type', [], {}),
- (True, {}, {}, {}, {}, 'type', None, {}),
- (True, {}, {}, {}, {}, 'type', [], None),
- ]
- )
- def test_relationship_model_ceration(self, nodes_storage, is_valid, source_interfaces,
- source_operations, target_interfaces, target_operations,
- type, type_hierarchy, properties):
- relationship = _test_model(
- is_valid=is_valid,
- storage=nodes_storage,
- model_name='relationship',
- model_cls=Relationship,
- model_kwargs=dict(
- source_node=nodes_storage.node.list()[1],
- target_node=nodes_storage.node.list()[0],
- source_interfaces=source_interfaces,
- source_operations=source_operations,
- target_interfaces=target_interfaces,
- target_operations=target_operations,
- type=type,
- type_hierarchy=type_hierarchy,
- properties=properties,
- ))
- if is_valid:
- assert relationship.source_node == nodes_storage.node.list()[1]
- assert relationship.target_node == nodes_storage.node.list()[0]
-
-
-class TestNodeInstance(object):
+class TestNode(object):
@pytest.mark.parametrize(
'is_valid, name, runtime_properties, scaling_groups, state, version',
[
@@ -644,94 +574,90 @@ class TestNodeInstance(object):
(True, 'name', {}, [], 'state', None),
]
)
- def test_node_instance_model_creation(self, node_storage, is_valid, name, runtime_properties,
- scaling_groups, state, version):
+ def test_node_instance_model_creation(self, node_template_storage, is_valid, name,
+ runtime_properties, scaling_groups, state, version):
node_instance = _test_model(
is_valid=is_valid,
- storage=node_storage,
- model_name='node_instance',
- model_cls=NodeInstance,
+ storage=node_template_storage,
+ model_cls=Node,
model_kwargs=dict(
- node=node_storage.node.list()[0],
+ node_template=node_template_storage.node_template.list()[0],
name=name,
runtime_properties=runtime_properties,
scaling_groups=scaling_groups,
state=state,
version=version,
+ service_instance=node_template_storage.service_instance.list()[0]
))
if is_valid:
- assert node_instance.node == node_storage.node.list()[0]
- assert node_instance.deployment == node_storage.deployment.list()[0]
+ assert node_instance.node_template == node_template_storage.node_template.list()[0]
+ assert node_instance.service_instance == \
+ node_template_storage.service_instance.list()[0]
class TestNodeInstanceIP(object):
ip = '1.1.1.1'
- def test_ip_on_none_hosted_node_instance(self, deployment_storage):
- node = self._node(deployment_storage, ip='not considered')
- node_instance = self._node_instance(deployment_storage, node,
- is_host=False,
- ip='not considered')
- assert node_instance.ip is None
-
- def test_property_ip_on_host_node_instance(self, deployment_storage):
- node = self._node(deployment_storage, ip=self.ip)
- node_instance = self._node_instance(deployment_storage, node,
- is_host=True,
- ip=None)
- assert node_instance.ip == self.ip
-
- def test_runtime_property_ip_on_host_node_instance(self, deployment_storage):
- node = self._node(deployment_storage, ip='not considered')
- node_instance = self._node_instance(deployment_storage, node,
- is_host=True,
- ip=self.ip)
- assert node_instance.ip == self.ip
-
- def test_no_ip_configured_on_host_node_instance(self, deployment_storage):
- node = self._node(deployment_storage, ip=None)
- node_instance = self._node_instance(deployment_storage, node,
- is_host=True,
- ip=None)
- assert node_instance.ip is None
-
- def test_runtime_property_on_hosted_node_instance(self, deployment_storage):
- host_node = self._node(deployment_storage, ip=None)
- host_node_instance = self._node_instance(deployment_storage, host_node,
- is_host=True,
- ip=self.ip)
- node = self._node(deployment_storage, ip=None)
- node_instance = self._node_instance(deployment_storage, node,
- is_host=False,
- ip=None,
- host_fk=host_node_instance.id)
- assert node_instance.ip == self.ip
-
- def _node(self, storage, ip):
+ def test_ip_on_none_hosted_node_instance(self, service_instance_storage):
+ node_template = self._node_template(service_instance_storage, ip='not considered')
+ node = self._node(service_instance_storage,
+ node_template,
+ is_host=False,
+ ip='not considered')
+ assert node.ip is None
+
+ def test_property_ip_on_host_node_instance(self, service_instance_storage):
+ node_template = self._node_template(service_instance_storage, ip=self.ip)
+ node = self._node(service_instance_storage, node_template, is_host=True, ip=None)
+ assert node.ip == self.ip
+
+ def test_runtime_property_ip_on_host_node_instance(self, service_instance_storage):
+ node_template = self._node_template(service_instance_storage, ip='not considered')
+ node = self._node(service_instance_storage, node_template, is_host=True, ip=self.ip)
+ assert node.ip == self.ip
+
+ def test_no_ip_configured_on_host_node_instance(self, service_instance_storage):
+ node_template = self._node_template(service_instance_storage, ip=None)
+ node = self._node(service_instance_storage, node_template, is_host=True, ip=None)
+ assert node.ip is None
+
+ def test_runtime_property_on_hosted_node_instance(self, service_instance_storage):
+ host_node_template = self._node_template(service_instance_storage, ip=None)
+ host_node = self._node(service_instance_storage,
+ host_node_template,
+ is_host=True,
+ ip=self.ip)
+ node_template = self._node_template(service_instance_storage, ip=None)
+ node = self._node(service_instance_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',
- deploy_number_of_instances=1,
- max_number_of_instances=1,
- min_number_of_instances=1,
- number_of_instances=1,
- planned_number_of_instances=1,
- properties={},
- type='',
- deployment=storage.deployment.list()[0]
+ name='node_template',
+ default_instances=1,
+ max_instances=1,
+ min_instances=1,
+ type_name='',
+ service_template=storage.service_template.list()[0]
)
if ip:
- kwargs['properties']['ip'] = ip
- node = Node(**kwargs)
- storage.node.put(node)
+ kwargs['properties'] = [Parameter(name='ip', type='str', str_value=str(ip))]
+ node = NodeTemplate(**kwargs)
+ storage.node_template.put(node)
return node
- def _node_instance(self, storage, node, is_host, ip, host_fk=None):
+ def _node(self, storage, node, is_host, ip, host_fk=None):
kwargs = dict(
- name='node_instance',
- node=node,
+ name='node',
+ node_template=node,
runtime_properties={},
- state=''
+ state='',
+ service_instance=storage.service_instance.list()[0]
)
if ip:
kwargs['runtime_properties']['ip'] = ip
@@ -739,27 +665,26 @@ class TestNodeInstanceIP(object):
kwargs['host_fk'] = 1
elif host_fk:
kwargs['host_fk'] = host_fk
- node_instance = NodeInstance(**kwargs)
- storage.node_instance.put(node_instance)
+ node_instance = Node(**kwargs)
+ storage.node.put(node_instance)
return node_instance
+@pytest.mark.skip('Should be reworked into relationship')
class TestRelationshipInstance(object):
- def test_relatiship_instance_model_creation(self, node_instances_storage):
+ def test_relatiship_instance_model_creation(self, nodes_storage):
relationship = mock.models.get_relationship(
- source=node_instances_storage.node.get_by_name(mock.models.DEPENDENT_NODE_NAME),
- target=node_instances_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ target=nodes_storage.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
)
- node_instances_storage.relationship.put(relationship)
- node_instances = node_instances_storage.node_instance
+ nodes_storage.relationship.put(relationship)
+ node_instances = nodes_storage.node
source_node_instance = node_instances.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
target_node_instance = node_instances.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
relationship_instance = _test_model(
is_valid=True,
- storage=node_instances_storage,
- model_name='relationship_instance',
- model_cls=RelationshipInstance,
+ storage=nodes_storage,
+ model_cls=Relationship,
model_kwargs=dict(
relationship=relationship,
source_node_instance=source_node_instance,
@@ -827,7 +752,6 @@ class TestPlugin(object):
supported_platform, supported_py_versions, uploaded_at, wheels):
_test_model(is_valid=is_valid,
storage=empty_storage,
- model_name='plugin',
model_cls=Plugin,
model_kwargs=dict(
archive_name=archive_name,
@@ -881,7 +805,6 @@ class TestTask(object):
task = _test_model(
is_valid=is_valid,
storage=execution_storage,
- model_name='task',
model_cls=Task,
model_kwargs=dict(
status=status,
@@ -894,7 +817,7 @@ class TestTask(object):
retry_interval=retry_interval,
ignore_failure=ignore_failure,
name=name,
- operation_mapping=operation_mapping,
+ implementation=operation_mapping,
inputs=inputs,
plugin_fk=plugin_id,
))
@@ -907,7 +830,7 @@ class TestTask(object):
def create_task(max_attempts):
Task(execution_fk='eid',
name='name',
- operation_mapping='',
+ implementation='',
inputs={},
max_attempts=max_attempts)
create_task(max_attempts=1)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/storage/test_structures.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_structures.py b/tests/storage/test_structures.py
index 4127905..30f0064 100644
--- a/tests/storage/test_structures.py
+++ b/tests/storage/test_structures.py
@@ -20,23 +20,23 @@ import sqlalchemy
from aria.storage import (
ModelStorage,
sql_mapi,
- model,
+ exceptions,
type,
- exceptions
+ modeling,
)
from ..storage import release_sqlite_storage, structure, init_inmemory_model_storage
from . import MockModel
from ..mock import (
models,
- operations,
context as mock_context
)
@pytest.fixture
def storage():
- base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, initiator=init_inmemory_model_storage)
+ base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI,
+ initiator=init_inmemory_model_storage)
base_storage.register(MockModel)
yield base_storage
release_sqlite_storage(base_storage)
@@ -44,7 +44,7 @@ def storage():
@pytest.fixture(scope='module', autouse=True)
def module_cleanup():
- model.DeclarativeBase.metadata.remove(MockModel.__table__) #pylint: disable=no-member
+ modeling.model.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member
@pytest.fixture
@@ -89,144 +89,87 @@ def test_inner_list_update(storage):
def test_model_to_dict(context):
- deployment = context.deployment
- deployment_dict = deployment.to_dict()
+ service_instance = context.service_instance
+ service_instance = service_instance.to_dict()
expected_keys = [
- 'created_at',
'description',
- 'inputs',
- 'groups',
+ '_metadata',
+ 'created_at',
'permalink',
'policy_triggers',
'policy_types',
- 'outputs',
'scaling_groups',
'updated_at',
'workflows',
- 'blueprint_name',
]
for expected_key in expected_keys:
- assert expected_key in deployment_dict
-
- assert 'blueprint_fk' not in deployment_dict
+ assert expected_key in service_instance
def test_relationship_model_ordering(context):
- deployment = context.model.deployment.get_by_name(models.DEPLOYMENT_NAME)
- source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
- source_node_instance = context.model.node_instance.get_by_name(
- models.DEPENDENT_NODE_INSTANCE_NAME)
- target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
- target_node_instance = context.model.node_instance.get_by_name(
- models.DEPENDENCY_NODE_INSTANCE_NAME)
- new_node = model.Node(
+ service_instance = context.model.service_instance.get_by_name(models.DEPLOYMENT_NAME)
+ source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_INSTANCE_NAME)
+ target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_INSTANCE_NAME)
+ new_node_template = modeling.model.NodeTemplate(
name='new_node',
- type='test_node_type',
- type_hierarchy=[],
- number_of_instances=1,
- planned_number_of_instances=1,
- deploy_number_of_instances=1,
- properties={},
- operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
- min_number_of_instances=1,
- max_number_of_instances=1,
- deployment=deployment
- )
- source_to_new_relationship = model.Relationship(
- source_node=source_node,
- target_node=new_node,
- source_interfaces={},
- source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- target_interfaces={},
- target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- type='rel_type',
+ type_name='test_node_type',
type_hierarchy=[],
- properties={},
+ default_instances=1,
+ min_instances=1,
+ max_instances=1,
+ service_template=service_instance.service_template
)
- new_node_instance = model.NodeInstance(
+ new_node = modeling.model.Node(
name='new_node_instance',
runtime_properties={},
+ service_instance=service_instance,
version=None,
- node=new_node,
+ node_template=new_node_template,
state='',
scaling_groups=[]
)
- source_to_new_relationship_instance = model.RelationshipInstance(
- relationship=source_to_new_relationship,
- source_node_instance=source_node_instance,
- target_node_instance=new_node_instance,
+
+ source_to_new_relationship = modeling.model.Relationship(
+ target_node=new_node,
+ source_node=source_node,
)
- new_to_target_relationship = model.Relationship(
+ new_to_target_relationship = modeling.model.Relationship(
source_node=new_node,
target_node=target_node,
- source_interfaces={},
- source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- target_interfaces={},
- target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- type='rel_type',
- type_hierarchy=[],
- properties={},
- )
- new_to_target_relationship_instance = model.RelationshipInstance(
- relationship=new_to_target_relationship,
- source_node_instance=new_node_instance,
- target_node_instance=target_node_instance,
)
+ context.model.node_template.put(new_node_template)
context.model.node.put(new_node)
- context.model.node_instance.put(new_node_instance)
context.model.relationship.put(source_to_new_relationship)
context.model.relationship.put(new_to_target_relationship)
- context.model.relationship_instance.put(source_to_new_relationship_instance)
- context.model.relationship_instance.put(new_to_target_relationship_instance)
- def flip_and_assert(node_instance, direction):
+ def flip_and_assert(node, direction):
"""
Reversed the order of relationships and assert effects took place.
- :param node_instance: the node instance to operatate on
+ :param node: the node instance to operatate on
:param direction: the type of relationships to flip (inbound/outbount)
:return:
"""
assert direction in ('inbound', 'outbound')
- relationships = getattr(node_instance.node, direction + '_relationships')
- relationship_instances = getattr(node_instance, direction + '_relationship_instances')
+ relationships = getattr(node, direction + '_relationships').all()
assert len(relationships) == 2
- assert len(relationship_instances) == 2
-
- first_rel, second_rel = relationships
- first_rel_instance, second_rel_instance = relationship_instances
- assert getattr(first_rel, relationships.ordering_attr) == 0
- assert getattr(second_rel, relationships.ordering_attr) == 1
- assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 0
- assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 1
-
- reversed_relationships = list(reversed(relationships))
- reversed_relationship_instances = list(reversed(relationship_instances))
-
- assert relationships != reversed_relationships
- assert relationship_instances != reversed_relationship_instances
-
- relationships[:] = reversed_relationships
- relationship_instances[:] = reversed_relationship_instances
- context.model.node_instance.update(node_instance)
- assert relationships == reversed_relationships
- assert relationship_instances == reversed_relationship_instances
+ reversed_relationship_instances = list(reversed(relationships))
+ assert relationships != reversed_relationship_instances
- assert getattr(first_rel, relationships.ordering_attr) == 1
- assert getattr(second_rel, relationships.ordering_attr) == 0
- assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 1
- assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 0
+ relationships[:] = reversed_relationship_instances
+ context.model.node.update(node)
+ assert relationships == reversed_relationship_instances
- flip_and_assert(source_node_instance, 'outbound')
- flip_and_assert(target_node_instance, 'inbound')
+ flip_and_assert(source_node, 'outbound')
+ flip_and_assert(target_node, 'inbound')
-class StrictClass(model.DeclarativeBase, structure.ModelMixin):
+class StrictClass(modeling.model.aria_declarative_base, structure.ModelMixin):
__tablename__ = 'strict_class'
strict_dict = sqlalchemy.Column(type.StrictDict(basestring, basestring))
[5/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/base_model.py
----------------------------------------------------------------------
diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py
deleted file mode 100644
index f7d0e5b..0000000
--- a/aria/storage/base_model.py
+++ /dev/null
@@ -1,757 +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.models module
-Path: aria.storage.models
-
-models module holds aria's models.
-
-classes:
- * Field - represents a single field.
- * IterField - represents an iterable field.
- * Model - abstract model implementation.
- * Snapshot - snapshots implementation model.
- * Deployment - deployment implementation model.
- * DeploymentUpdateStep - deployment update step implementation model.
- * DeploymentUpdate - deployment update implementation model.
- * DeploymentModification - deployment modification implementation model.
- * Execution - execution implementation model.
- * Node - node implementation model.
- * Relationship - relationship implementation model.
- * NodeInstance - node instance implementation model.
- * RelationshipInstance - relationship instance implementation model.
- * Plugin - plugin implementation model.
-"""
-from collections import namedtuple
-from datetime import datetime
-
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import declared_attr
-from sqlalchemy import (
- Column,
- Integer,
- Text,
- DateTime,
- Boolean,
- Enum,
- String,
- Float,
- orm,
-)
-from sqlalchemy.ext.orderinglist import ordering_list
-
-from ..orchestrator.exceptions import TaskAbortException, TaskRetryException
-from .structure import ModelMixin
-from .type import (
- List,
- Dict
-)
-
-__all__ = (
- 'BlueprintBase',
- 'DeploymentBase',
- 'DeploymentUpdateStepBase',
- 'DeploymentUpdateBase',
- 'DeploymentModificationBase',
- 'ExecutionBase',
- 'NodeBase',
- 'RelationshipBase',
- 'NodeInstanceBase',
- 'RelationshipInstanceBase',
- 'PluginBase',
- 'TaskBase'
-)
-
-#pylint: disable=no-self-argument, abstract-method
-
-
-class BlueprintBase(ModelMixin):
- """
- Blueprint model representation.
- """
- __tablename__ = 'blueprints'
-
- created_at = Column(DateTime, nullable=False, index=True)
- main_file_name = Column(Text, nullable=False)
- plan = Column(Dict, nullable=False)
- updated_at = Column(DateTime)
- description = Column(Text)
-
-
-class DeploymentBase(ModelMixin):
- """
- Deployment model representation.
- """
- __tablename__ = 'deployments'
-
- _private_fields = ['blueprint_fk']
-
- created_at = Column(DateTime, nullable=False, index=True)
- description = Column(Text)
- inputs = Column(Dict)
- groups = Column(Dict)
- permalink = Column(Text)
- policy_triggers = Column(Dict)
- policy_types = Column(Dict)
- outputs = Column(Dict)
- scaling_groups = Column(Dict)
- updated_at = Column(DateTime)
- workflows = Column(Dict)
-
- @declared_attr
- def blueprint_fk(cls):
- return cls.foreign_key(BlueprintBase, nullable=False)
-
- @declared_attr
- def blueprint(cls):
- return cls.one_to_many_relationship('blueprint_fk')
-
- @declared_attr
- def blueprint_name(cls):
- return association_proxy('blueprint', cls.name_column_name())
-
-
-class ExecutionBase(ModelMixin):
- """
- Execution model representation.
- """
- # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
- __tablename__ = 'executions'
- _private_fields = ['deployment_fk']
-
- TERMINATED = 'terminated'
- FAILED = 'failed'
- CANCELLED = 'cancelled'
- PENDING = 'pending'
- STARTED = 'started'
- CANCELLING = 'cancelling'
- FORCE_CANCELLING = 'force_cancelling'
-
- STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING]
- END_STATES = [TERMINATED, FAILED, CANCELLED]
- ACTIVE_STATES = [state for state in STATES if state not in END_STATES]
-
- VALID_TRANSITIONS = {
- PENDING: [STARTED, CANCELLED],
- STARTED: END_STATES + [CANCELLING],
- CANCELLING: END_STATES + [FORCE_CANCELLING]
- }
-
- @orm.validates('status')
- def validate_status(self, key, value):
- """Validation function that verifies execution status transitions are OK"""
- try:
- current_status = getattr(self, key)
- except AttributeError:
- return
- valid_transitions = self.VALID_TRANSITIONS.get(current_status, [])
- if all([current_status is not None,
- current_status != value,
- value not in valid_transitions]):
- raise ValueError('Cannot change execution status from {current} to {new}'.format(
- current=current_status,
- new=value))
- return value
-
- created_at = Column(DateTime, index=True)
- started_at = Column(DateTime, nullable=True, index=True)
- ended_at = Column(DateTime, nullable=True, index=True)
- error = Column(Text, nullable=True)
- is_system_workflow = Column(Boolean, nullable=False, default=False)
- parameters = Column(Dict)
- status = Column(Enum(*STATES, name='execution_status'), default=PENDING)
- workflow_name = Column(Text)
-
- @declared_attr
- def blueprint(cls):
- return association_proxy('deployment', 'blueprint')
-
- @declared_attr
- def deployment_fk(cls):
- return cls.foreign_key(DeploymentBase, nullable=True)
-
- @declared_attr
- def deployment(cls):
- return cls.one_to_many_relationship('deployment_fk')
-
- @declared_attr
- def deployment_name(cls):
- return association_proxy('deployment', cls.name_column_name())
-
- @declared_attr
- def blueprint_name(cls):
- return association_proxy('deployment', 'blueprint_name')
-
- def __str__(self):
- return '<{0} id=`{1}` (status={2})>'.format(
- self.__class__.__name__,
- getattr(self, self.name_column_name()),
- self.status
- )
-
-
-class DeploymentUpdateBase(ModelMixin):
- """
- Deployment update model representation.
- """
- # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
- steps = None
-
- __tablename__ = 'deployment_updates'
-
- _private_fields = ['execution_fk', 'deployment_fk']
-
- created_at = Column(DateTime, nullable=False, index=True)
- deployment_plan = Column(Dict, nullable=False)
- deployment_update_node_instances = Column(Dict)
- deployment_update_deployment = Column(Dict)
- deployment_update_nodes = Column(List)
- modified_entity_ids = Column(Dict)
- state = Column(Text)
-
- @declared_attr
- def execution_fk(cls):
- return cls.foreign_key(ExecutionBase, nullable=True)
-
- @declared_attr
- def execution(cls):
- return cls.one_to_many_relationship('execution_fk')
-
- @declared_attr
- def execution_name(cls):
- return association_proxy('execution', cls.name_column_name())
-
- @declared_attr
- def deployment_fk(cls):
- return cls.foreign_key(DeploymentBase)
-
- @declared_attr
- def deployment(cls):
- return cls.one_to_many_relationship('deployment_fk')
-
- @declared_attr
- def deployment_name(cls):
- return association_proxy('deployment', cls.name_column_name())
-
- def to_dict(self, suppress_error=False, **kwargs):
- dep_update_dict = super(DeploymentUpdateBase, 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 DeploymentUpdateStepBase(ModelMixin):
- """
- Deployment update step model representation.
- """
- # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
- __tablename__ = 'deployment_update_steps'
- _private_fields = ['deployment_update_fk']
-
- _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, '
- 'POLICY_TYPE, POLICY_TRIGGER, PLUGIN')
- ENTITY_TYPES = _entity_types(
- NODE='node',
- RELATIONSHIP='relationship',
- PROPERTY='property',
- OPERATION='operation',
- WORKFLOW='workflow',
- OUTPUT='output',
- DESCRIPTION='description',
- GROUP='group',
- POLICY_TYPE='policy_type',
- POLICY_TRIGGER='policy_trigger',
- 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 deployment_update_fk(cls):
- return cls.foreign_key(DeploymentUpdateBase)
-
- @declared_attr
- def deployment_update(cls):
- return cls.one_to_many_relationship('deployment_update_fk', backreference='steps')
-
- @declared_attr
- def deployment_update_name(cls):
- return association_proxy('deployment_update', cls.name_column_name())
-
- 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 DeploymentModificationBase(ModelMixin):
- """
- Deployment modification model representation.
- """
- __tablename__ = 'deployment_modifications'
- _private_fields = ['deployment_fk']
-
- 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_nodes = Column(Dict)
- node_instances = Column(Dict)
- status = Column(Enum(*STATES, name='deployment_modification_status'))
-
- @declared_attr
- def deployment_fk(cls):
- return cls.foreign_key(DeploymentBase)
-
- @declared_attr
- def deployment(cls):
- return cls.one_to_many_relationship('deployment_fk', backreference='modifications')
-
- @declared_attr
- def deployment_name(cls):
- return association_proxy('deployment', cls.name_column_name())
-
-
-class NodeBase(ModelMixin):
- """
- Node model representation.
- """
- __tablename__ = 'nodes'
-
- # See base class for an explanation on these properties
- is_id_unique = False
-
- _private_fields = ['blueprint_fk', 'host_fk']
-
- @declared_attr
- def host_fk(cls):
- return cls.foreign_key(NodeBase, nullable=True)
-
- @declared_attr
- def host(cls):
- return cls.relationship_to_self('host_fk')
-
- @declared_attr
- def host_name(cls):
- return association_proxy('host', cls.name_column_name())
-
- @declared_attr
- def deployment_fk(cls):
- return cls.foreign_key(DeploymentBase)
-
- @declared_attr
- def deployment(cls):
- return cls.one_to_many_relationship('deployment_fk')
-
- @declared_attr
- def deployment_name(cls):
- return association_proxy('deployment', cls.name_column_name())
-
- @declared_attr
- def blueprint_name(cls):
- return association_proxy('deployment', 'blueprint_{0}'.format(cls.name_column_name()))
-
- deploy_number_of_instances = Column(Integer, nullable=False)
- max_number_of_instances = Column(Integer, nullable=False)
- min_number_of_instances = Column(Integer, nullable=False)
- number_of_instances = Column(Integer, nullable=False)
- planned_number_of_instances = Column(Integer, nullable=False)
- plugins = Column(List)
- properties = Column(Dict)
- operations = Column(Dict)
- type = Column(Text, nullable=False, index=True)
- type_hierarchy = Column(List)
-
-
-class RelationshipBase(ModelMixin):
- """
- Relationship model representation.
- """
- __tablename__ = 'relationships'
-
- _private_fields = ['source_node_fk', 'target_node_fk', 'source_position', 'target_position']
-
- source_position = Column(Integer)
- target_position = Column(Integer)
-
- @declared_attr
- def deployment_id(self):
- return association_proxy('source_node', 'deployment_id')
-
- @declared_attr
- def source_node_fk(cls):
- return cls.foreign_key(NodeBase)
-
- @declared_attr
- def source_node(cls):
- return cls.one_to_many_relationship(
- 'source_node_fk',
- backreference='outbound_relationships',
- backref_kwargs=dict(
- order_by=cls.source_position,
- collection_class=ordering_list('source_position', count_from=0)
- )
- )
-
- @declared_attr
- def source_name(cls):
- return association_proxy('source_node', cls.name_column_name())
-
- @declared_attr
- def target_node_fk(cls):
- return cls.foreign_key(NodeBase, nullable=True)
-
- @declared_attr
- def target_node(cls):
- return cls.one_to_many_relationship(
- 'target_node_fk',
- backreference='inbound_relationships',
- backref_kwargs=dict(
- order_by=cls.target_position,
- collection_class=ordering_list('target_position', count_from=0)
- )
- )
-
- @declared_attr
- def target_name(cls):
- return association_proxy('target_node', cls.name_column_name())
-
- source_interfaces = Column(Dict)
- source_operations = Column(Dict, nullable=False)
- target_interfaces = Column(Dict)
- target_operations = Column(Dict, nullable=False)
- type = Column(String, nullable=False)
- type_hierarchy = Column(List)
- properties = Column(Dict)
-
-
-class NodeInstanceBase(ModelMixin):
- """
- Node instance model representation.
- """
- __tablename__ = 'node_instances'
- _private_fields = ['node_fk', 'host_fk']
-
- runtime_properties = Column(Dict)
- scaling_groups = Column(List)
- state = Column(Text, nullable=False)
- version = Column(Integer, default=1)
-
- @declared_attr
- def host_fk(cls):
- return cls.foreign_key(NodeInstanceBase, nullable=True)
-
- @declared_attr
- def host(cls):
- return cls.relationship_to_self('host_fk')
-
- @declared_attr
- def host_name(cls):
- return association_proxy('host', cls.name_column_name())
-
- @declared_attr
- def deployment(cls):
- return association_proxy('node', 'deployment')
-
- @declared_attr
- def deployment_name(cls):
- return association_proxy('node', 'deployment_name')
-
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key(NodeBase, nullable=True)
-
- @declared_attr
- def node(cls):
- return cls.one_to_many_relationship('node_fk')
-
- @declared_attr
- def node_name(cls):
- return association_proxy('node', cls.name_column_name())
-
- @property
- def ip(self):
- if not self.host_fk:
- return None
- host_node_instance = self.host
- if 'ip' in host_node_instance.runtime_properties: # pylint: disable=no-member
- return host_node_instance.runtime_properties['ip'] # pylint: disable=no-member
- host_node = host_node_instance.node # pylint: disable=no-member
- if 'ip' in host_node.properties:
- return host_node.properties['ip']
- return None
-
-
-class RelationshipInstanceBase(ModelMixin):
- """
- Relationship instance model representation.
- """
- __tablename__ = 'relationship_instances'
- _private_fields = ['relationship_storage_fk',
- 'source_node_instance_fk',
- 'target_node_instance_fk',
- 'source_position',
- 'target_position']
-
- source_position = Column(Integer)
- target_position = Column(Integer)
-
- @declared_attr
- def source_node_instance_fk(cls):
- return cls.foreign_key(NodeInstanceBase, nullable=True)
-
- @declared_attr
- def source_node_instance(cls):
- return cls.one_to_many_relationship(
- 'source_node_instance_fk',
- backreference='outbound_relationship_instances',
- backref_kwargs=dict(
- order_by=cls.source_position,
- collection_class=ordering_list('source_position', count_from=0)
- )
- )
-
- @declared_attr
- def source_node_instance_name(cls):
- return association_proxy('source_node_instance', 'node_{0}'.format(cls.name_column_name()))
-
- @declared_attr
- def source_node_name(cls):
- return association_proxy('source_node_instance', cls.name_column_name())
-
- @declared_attr
- def target_node_instance_fk(cls):
- return cls.foreign_key(NodeInstanceBase, nullable=True)
-
- @declared_attr
- def target_node_instance(cls):
- return cls.one_to_many_relationship(
- 'target_node_instance_fk',
- backreference='inbound_relationship_instances',
- backref_kwargs=dict(
- order_by=cls.target_position,
- collection_class=ordering_list('target_position', count_from=0)
- )
- )
-
- @declared_attr
- def target_node_instance_name(cls):
- return association_proxy('target_node_instance', cls.name_column_name())
-
- @declared_attr
- def target_node_name(cls):
- return association_proxy('target_node_instance', 'node_{0}'.format(cls.name_column_name()))
-
- @declared_attr
- def relationship_fk(cls):
- return cls.foreign_key(RelationshipBase)
-
- @declared_attr
- def relationship(cls):
- return cls.one_to_many_relationship('relationship_fk')
-
- @declared_attr
- def relationship_name(cls):
- return association_proxy('relationship', cls.name_column_name())
-
-
-
-class PluginBase(ModelMixin):
- """
- Plugin model representation.
- """
- __tablename__ = 'plugins'
-
- 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)
- uploaded_at = Column(DateTime, nullable=False, index=True)
- wheels = Column(List, nullable=False)
-
-
-class TaskBase(ModelMixin):
- """
- A Model which represents an task
- """
- __tablename__ = 'tasks'
- _private_fields = ['node_instance_fk', 'relationship_instance_fk', 'execution_fk']
-
- @declared_attr
- def node_instance_fk(cls):
- return cls.foreign_key(NodeInstanceBase, nullable=True)
-
- @declared_attr
- def node_instance_name(cls):
- return association_proxy('node_instance', cls.name_column_name())
-
- @declared_attr
- def node_instance(cls):
- return cls.one_to_many_relationship('node_instance_fk')
-
- @declared_attr
- def relationship_instance_fk(cls):
- return cls.foreign_key(RelationshipInstanceBase, nullable=True)
-
- @declared_attr
- def relationship_instance_name(cls):
- return association_proxy('relationship_instance', cls.name_column_name())
-
- @declared_attr
- def relationship_instance(cls):
- return cls.one_to_many_relationship('relationship_instance_fk')
-
- @declared_attr
- def plugin_fk(cls):
- return cls.foreign_key(PluginBase, nullable=True)
-
- @declared_attr
- def plugin(cls):
- return cls.one_to_many_relationship('plugin_fk')
-
- @declared_attr
- def execution_fk(cls):
- return cls.foreign_key(ExecutionBase, nullable=True)
-
- @declared_attr
- def execution(cls):
- return cls.one_to_many_relationship('execution_fk')
-
- @declared_attr
- def execution_name(cls):
- return association_proxy('execution', cls.name_column_name())
-
- PENDING = 'pending'
- RETRYING = 'retrying'
- SENT = 'sent'
- STARTED = 'started'
- SUCCESS = 'success'
- FAILED = 'failed'
- STATES = (
- PENDING,
- RETRYING,
- SENT,
- STARTED,
- SUCCESS,
- FAILED,
- )
-
- WAIT_STATES = [PENDING, RETRYING]
- END_STATES = [SUCCESS, FAILED]
-
- RUNS_ON_SOURCE = 'source'
- RUNS_ON_TARGET = 'target'
- RUNS_ON_NODE_INSTANCE = 'node_instance'
- RUNS_ON = (RUNS_ON_NODE_INSTANCE, RUNS_ON_SOURCE, RUNS_ON_TARGET)
-
- @orm.validates('max_attempts')
- def validate_max_attempts(self, _, value): # pylint: disable=no-self-use
- """Validates that max attempts is either -1 or a positive number"""
- if value < 1 and value != TaskBase.INFINITE_RETRIES:
- raise ValueError('Max attempts can be either -1 (infinite) or any positive number. '
- 'Got {value}'.format(value=value))
- return value
-
- INFINITE_RETRIES = -1
-
- status = Column(Enum(*STATES, name='status'), default=PENDING)
-
- due_at = Column(DateTime, default=datetime.utcnow)
- started_at = Column(DateTime, default=None)
- ended_at = Column(DateTime, default=None)
- max_attempts = Column(Integer, default=1)
- retry_count = Column(Integer, default=0)
- retry_interval = Column(Float, default=0)
- ignore_failure = Column(Boolean, default=False)
-
- # Operation specific fields
- operation_mapping = Column(String)
- inputs = Column(Dict)
- plugin_name = Column(String)
- _runs_on = Column(Enum(*RUNS_ON, name='runs_on'), name='runs_on')
-
- @property
- def actor(self):
- """
- Return the actor of the task
- :return:
- """
- return self.node_instance or self.relationship_instance
-
- @property
- def runs_on(self):
- if self._runs_on == self.RUNS_ON_NODE_INSTANCE:
- return self.node_instance
- elif self._runs_on == self.RUNS_ON_SOURCE:
- return self.relationship_instance.source_node_instance # pylint: disable=no-member
- elif self._runs_on == self.RUNS_ON_TARGET:
- return self.relationship_instance.target_node_instance # pylint: disable=no-member
- return None
-
- @classmethod
- def as_node_instance(cls, instance, runs_on, **kwargs):
- return cls(node_instance=instance, _runs_on=runs_on, **kwargs)
-
- @classmethod
- def as_relationship_instance(cls, instance, runs_on, **kwargs):
- return cls(relationship_instance=instance, _runs_on=runs_on, **kwargs)
-
- @staticmethod
- def abort(message=None):
- raise TaskAbortException(message)
-
- @staticmethod
- def retry(message=None, retry_interval=None):
- raise TaskRetryException(message, retry_interval=retry_interval)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index 3878dca..0e189e6 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -39,10 +39,7 @@ API:
"""
from aria.logger import LoggerMixin
-from . import (
- api as storage_api,
- sql_mapi
-)
+from . import sql_mapi
__all__ = (
'Storage',
@@ -146,7 +143,7 @@ class ModelStorage(Storage):
:param model_cls: the model to register.
:return:
"""
- model_name = storage_api.generate_lower_name(model_cls)
+ model_name = model_cls.__modelname__
if model_name in self.registered:
self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
self=self))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/instrumentation.py
----------------------------------------------------------------------
diff --git a/aria/storage/instrumentation.py b/aria/storage/instrumentation.py
index 537dbb5..57fe9bd 100644
--- a/aria/storage/instrumentation.py
+++ b/aria/storage/instrumentation.py
@@ -17,12 +17,11 @@ import copy
import sqlalchemy.event
-from . import api
-from . import model as _model
+from .modeling import model as _model
_STUB = object()
_INSTRUMENTED = {
- _model.NodeInstance.runtime_properties: dict
+ _model.Node.runtime_properties: dict
}
@@ -75,7 +74,7 @@ class _Instrumentation(object):
def _register_set_attribute_listener(self, instrumented_attribute, attribute_type):
def listener(target, value, *_):
- mapi_name = self._mapi_name(target.__class__)
+ mapi_name = target.__modelname__
tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
tracked_attributes = tracked_instances.setdefault(target.id, {})
if value is None:
@@ -90,7 +89,7 @@ class _Instrumentation(object):
def _register_instance_listeners(self, instrumented_class, instrumented_attributes):
def listener(target, *_):
- mapi_name = self._mapi_name(instrumented_class)
+ mapi_name = instrumented_class.__modelname__
tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
tracked_attributes = tracked_instances.setdefault(target.id, {})
for attribute_name, attribute_type in instrumented_attributes.items():
@@ -110,7 +109,7 @@ class _Instrumentation(object):
def clear(self, target=None):
if target:
- mapi_name = self._mapi_name(target.__class__)
+ mapi_name = target.__modelname__
tracked_instances = self.tracked_changes.setdefault(mapi_name, {})
tracked_instances.pop(target.id, None)
else:
@@ -128,10 +127,6 @@ class _Instrumentation(object):
def __exit__(self, exc_type, exc_val, exc_tb):
self.restore()
- @staticmethod
- def _mapi_name(instrumented_class):
- return api.generate_lower_name(instrumented_class)
-
class _Value(object):
# You may wonder why is this a full blown class and not a named tuple. The reason is that
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/model.py b/aria/storage/model.py
deleted file mode 100644
index afca3e4..0000000
--- a/aria/storage/model.py
+++ /dev/null
@@ -1,110 +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.models module
-Path: aria.storage.models
-
-models module holds aria's models.
-
-classes:
- * Field - represents a single field.
- * IterField - represents an iterable field.
- * Model - abstract model implementation.
- * Snapshot - snapshots implementation model.
- * Deployment - deployment implementation model.
- * DeploymentUpdateStep - deployment update step implementation model.
- * DeploymentUpdate - deployment update implementation model.
- * DeploymentModification - deployment modification implementation model.
- * Execution - execution implementation model.
- * Node - node implementation model.
- * Relationship - relationship implementation model.
- * NodeInstance - node instance implementation model.
- * RelationshipInstance - relationship instance implementation model.
- * ProviderContext - provider context implementation model.
- * Plugin - plugin implementation model.
-"""
-from sqlalchemy.ext.declarative import declarative_base
-
-from . import structure
-from . import base_model as base
-
-__all__ = (
- 'Blueprint',
- 'Deployment',
- 'DeploymentUpdateStep',
- 'DeploymentUpdate',
- 'DeploymentModification',
- 'Execution',
- 'Node',
- 'Relationship',
- 'NodeInstance',
- 'RelationshipInstance',
- 'Plugin',
-)
-
-
-#pylint: disable=abstract-method
-# The required abstract method implementation are implemented in the ModelIDMixin, which is used as
-# a base to the DeclerativeBase.
-DeclarativeBase = declarative_base(cls=structure.ModelIDMixin)
-
-
-class Blueprint(DeclarativeBase, base.BlueprintBase):
- pass
-
-
-class Deployment(DeclarativeBase, base.DeploymentBase):
- pass
-
-
-class Execution(DeclarativeBase, base.ExecutionBase):
- pass
-
-
-class DeploymentUpdate(DeclarativeBase, base.DeploymentUpdateBase):
- pass
-
-
-class DeploymentUpdateStep(DeclarativeBase, base.DeploymentUpdateStepBase):
- pass
-
-
-class DeploymentModification(DeclarativeBase, base.DeploymentModificationBase):
- pass
-
-
-class Node(DeclarativeBase, base.NodeBase):
- pass
-
-
-class Relationship(DeclarativeBase, base.RelationshipBase):
- pass
-
-
-class NodeInstance(DeclarativeBase, base.NodeInstanceBase):
- pass
-
-
-class RelationshipInstance(DeclarativeBase, base.RelationshipInstanceBase):
- pass
-
-
-class Plugin(DeclarativeBase, base.PluginBase):
- pass
-
-
-class Task(DeclarativeBase, base.TaskBase):
- pass
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/__init__.py b/aria/storage/modeling/__init__.py
new file mode 100644
index 0000000..697ed09
--- /dev/null
+++ b/aria/storage/modeling/__init__.py
@@ -0,0 +1,35 @@
+# 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 collections import namedtuple
+
+from . import (
+ model,
+ instance_elements as _instance_base,
+ orchestrator_elements as _orchestrator_base,
+ template_elements as _template_base,
+)
+
+_ModelBaseCls = namedtuple('ModelBase', 'instance_elements,'
+ 'orchestrator_elements,'
+ 'template_elements')
+model_base = _ModelBaseCls(instance_elements=_instance_base,
+ orchestrator_elements=_orchestrator_base,
+ template_elements=_template_base)
+
+__all__ = (
+ 'model',
+ 'model_base',
+)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/elements.py b/aria/storage/modeling/elements.py
new file mode 100644
index 0000000..8c720b9
--- /dev/null
+++ b/aria/storage/modeling/elements.py
@@ -0,0 +1,106 @@
+# 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
+)
+
+from ...parser.modeling import utils
+from ...utils.collections import OrderedDict
+from ...utils.console import puts
+from .. import exceptions
+
+from . import structure
+from . import type
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+class ParameterBase(structure.ModelMixin):
+ """
+ Represents a typed value.
+
+ This class is used by both service model and service instance elements.
+ """
+ __tablename__ = 'parameter'
+ name = Column(Text, nullable=False)
+ type = Column(Text, nullable=False)
+
+ # Check: value type
+ str_value = Column(Text)
+ description = Column(Text)
+
+ @property
+ def as_raw(self):
+ return OrderedDict((
+ ('name', self.name),
+ ('type_name', self.type),
+ ('value', self.value),
+ ('description', self.description)))
+
+ @property
+ def value(self):
+ if self.type is None:
+ return
+ try:
+ if self.type.lower() in ['str', 'unicode']:
+ return self.str_value.decode('utf-8')
+ elif self.type.lower() == 'int':
+ return int(self.str_value)
+ elif self.type.lower() == 'bool':
+ return bool(self.str_value)
+ elif self.type.lower() == 'float':
+ return float(self.str_value)
+ else:
+ raise exceptions.StorageError('No supported type_name was provided')
+ except ValueError:
+ raise exceptions.StorageError('Trying to cast {0} to {1} failed'.format(self.str_value,
+ self.type))
+
+ def instantiate(self, context, container):
+ return ParameterBase(self.type, self.str_value, self.description)
+
+ def coerce_values(self, context, container, report_issues):
+ if self.str_value is not None:
+ self.str_value = utils.coerce_value(context, container, self.str_value, report_issues)
+
+
+class MetadataBase(structure.ModelMixin):
+ """
+ Custom values associated with the deployment template and its plans.
+
+ This class is used by both service model and service instance elements.
+
+ Properties:
+
+ * :code:`values`: Dict of custom values
+ """
+ values = Column(type.StrictDict(key_cls=basestring))
+
+ @property
+ def as_raw(self):
+ return self.values
+
+ def instantiate(self, context, container):
+ metadata = MetadataBase()
+ metadata.values.update(self.values)
+ return metadata
+
+ def dump(self, context):
+ puts('Metadata:')
+ with context.style.indent:
+ for name, value in self.values.iteritems():
+ puts('%s: %s' % (name, context.style.meta(value)))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/instance_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/instance_elements.py b/aria/storage/modeling/instance_elements.py
new file mode 100644
index 0000000..0666c8a
--- /dev/null
+++ b/aria/storage/modeling/instance_elements.py
@@ -0,0 +1,1286 @@
+# 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,
+ Boolean,
+)
+from sqlalchemy import DateTime
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.ext.orderinglist import ordering_list
+
+from aria.parser import validation
+from aria.utils import collections, formatting, console
+
+from . import (
+ utils,
+ structure,
+ type as aria_types
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+# region Element instances
+
+
+class ServiceInstanceBase(structure.ModelMixin):
+ __tablename__ = 'service_instance'
+
+ __private_fields__ = ['substituion_fk',
+ 'service_template_fk']
+
+ description = Column(Text)
+ _metadata = Column(Text)
+
+ # region orchestrator required columns
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ permalink = Column(Text)
+ policy_triggers = Column(aria_types.Dict)
+ policy_types = Column(aria_types.Dict)
+ scaling_groups = Column(aria_types.Dict)
+ updated_at = Column(DateTime)
+ workflows = Column(aria_types.Dict)
+
+ @declared_attr
+ def service_template_name(cls):
+ return association_proxy('service_template', 'name')
+
+ # endregion
+
+ # region foreign keys
+ @declared_attr
+ def substitution_fk(cls):
+ return cls.foreign_key('substitution', nullable=True)
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls.foreign_key('service_template')
+
+ # endregion
+
+ # region one-to-one relationships
+ @declared_attr
+ def substitution(cls):
+ return cls.one_to_one_relationship('substitution')
+ # endregion
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_template(cls):
+ return cls.many_to_one_relationship('service_template')
+
+ # endregion
+
+ # region many-to-many relationships
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ @declared_attr
+ def outputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='outputs')
+
+ # endregion
+
+ # association proxies
+
+ def satisfy_requirements(self, context):
+ satisfied = True
+ for node in self.nodes.all():
+ if not node.satisfy_requirements(context):
+ satisfied = False
+ return satisfied
+
+ def validate_capabilities(self, context):
+ satisfied = True
+ for node in self.nodes.all():
+ if not node.validate_capabilities(context):
+ satisfied = False
+ return satisfied
+
+ def find_nodes(self, node_template_name):
+ nodes = []
+ for node in self.nodes.all():
+ if 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.id for node in self.find_nodes(node_template_name)))
+
+ def find_groups(self, group_template_name):
+ groups = []
+ for group in self.groups.all():
+ 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.id for group in self.find_groups(group_template_name)))
+
+ def is_node_a_target(self, context, target_node):
+ for node in self.nodes.all():
+ 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.id:
+ 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
+
+
+class OperationBase(structure.ModelMixin):
+ """
+ An operation in a :class:`Interface`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+ * :code:`dependencies`: List of strings (interpreted by the orchestrator)
+ * :code:`executor`: Executor string (interpreted by the orchestrator)
+ * :code:`max_retries`: Maximum number of retries allowed in case of failure
+ * :code:`retry_interval`: Interval between retries
+ * :code:`inputs`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'operation'
+
+ __private_fields__ = ['service_template_fk',
+ 'interface_instance_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance', nullable=True)
+
+ @declared_attr
+ def interface_instance_fk(cls):
+ return cls.foreign_key('interface', nullable=True)
+
+ # endregion
+ description = Column(Text)
+ implementation = Column(Text)
+ dependencies = Column(aria_types.StrictList(item_cls=basestring))
+
+ executor = Column(Text)
+ max_retries = Column(Integer, default=None)
+ retry_interval = Column(Integer, default=None)
+ plugin = Column(Text)
+ operation = Column(Boolean)
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ @declared_attr
+ def interface(cls):
+ return cls.many_to_one_relationship('interface')
+ # region many-to-many relationships
+
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ # 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):
+ 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: %s' % context.style.literal(self.implementation))
+ if self.dependencies:
+ console.puts(
+ 'Dependencies: %s'
+ % ', '.join((str(context.style.literal(v)) for v in self.dependencies)))
+ if self.executor is not None:
+ console.puts('Executor: %s' % context.style.literal(self.executor))
+ if self.max_retries is not None:
+ console.puts('Max retries: %s' % context.style.literal(self.max_retries))
+ if self.retry_interval is not None:
+ console.puts('Retry interval: %s' % context.style.literal(self.retry_interval))
+ utils.dump_parameters(context, self.inputs, 'Inputs')
+
+
+class InterfaceBase(structure.ModelMixin):
+ """
+ A typed set of :class:`Operation`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`inputs`: Dict of :class:`Parameter`
+ * :code:`operations`: Dict of :class:`Operation`
+ """
+ __tablename__ = 'interface'
+
+ __private_fields__ = ['group_fk',
+ 'node_fk',
+ 'relationship_fk']
+
+
+ # region foreign_keys
+ @declared_attr
+ def group_fk(cls):
+ return cls.foreign_key('group', nullable=True)
+
+ @declared_attr
+ def node_fk(cls):
+ return cls.foreign_key('node', nullable=True)
+
+ @declared_attr
+ def relationship_fk(cls):
+ return cls.foreign_key('relationship', nullable=True)
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ edge = Column(Text)
+
+ # region many-to-one relationships
+
+ @declared_attr
+ def node(cls):
+ return cls.many_to_one_relationship('node')
+
+ @declared_attr
+ def relationship(cls):
+ return cls.many_to_one_relationship('relationship')
+
+ @declared_attr
+ def group(cls):
+ return cls.many_to_one_relationship('group')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ # 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):
+ if self.type_name:
+ if context.modeling.interface_types.get_descendant(self.type_name) is None:
+ context.validation.report('interface "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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: %s' % context.style.type(self.type_name))
+ utils.dump_parameters(context, self.inputs, 'Inputs')
+ utils.dump_dict_values(context, self.operations, 'Operations')
+
+
+class CapabilityBase(structure.ModelMixin):
+ """
+ A capability of a :class:`Node`.
+
+ An instance of a :class:`CapabilityTemplate`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`min_occurrences`: Minimum number of requirement matches required
+ * :code:`max_occurrences`: Maximum number of requirement matches allowed
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'capability'
+
+ __private_fields__ = ['node_fk']
+
+ # region foreign_keys
+ @declared_attr
+ def node_fk(cls):
+ return cls.foreign_key('node')
+
+ # endregion
+ type_name = Column(Text)
+
+ min_occurrences = Column(Integer, default=None) # optional
+ max_occurrences = Column(Integer, default=None) # optional
+ occurrences = Column(Integer, default=0)
+
+ # region many-to-one relationships
+ @declared_attr
+ def node(cls):
+ return cls.many_to_one_relationship('node')
+
+ # endregion
+
+
+ # region many-to-many relationships
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # 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):
+ if context.modeling.capability_types.get_descendant(self.type_name) is None:
+ context.validation.report('capability "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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: %s' % context.style.type(self.type_name))
+ console.puts('Occurrences: %s (%s%s)'
+ % (self.occurrences,
+ self.min_occurrences or 0,
+ (' to %d' % self.max_occurrences)
+ if self.max_occurrences is not None
+ else ' or more'))
+ utils.dump_parameters(context, self.properties)
+
+
+class ArtifactBase(structure.ModelMixin):
+ """
+ A file associated with a :class:`Node`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`source_path`: Source path (CSAR or repository)
+ * :code:`target_path`: Path at destination machine
+ * :code:`repository_url`: Repository URL
+ * :code:`repository_credential`: Dict of string
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'artifact'
+
+ __private_fields__ = ['node_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def node_fk(cls):
+ return cls.foreign_key('node')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ source_path = Column(Text)
+ target_path = Column(Text)
+ repository_url = Column(Text)
+ repository_credential = Column(aria_types.StrictDict(basestring, basestring))
+
+ # region many-to-one relationships
+ @declared_attr
+ def node(cls):
+ return cls.many_to_one_relationship('node')
+
+ # endregion
+
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # 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):
+ if context.modeling.artifact_types.get_descendant(self.type_name) is None:
+ context.validation.report('artifact "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+ 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: %s' % context.style.type(self.type_name))
+ console.puts('Source path: %s' % context.style.literal(self.source_path))
+ if self.target_path is not None:
+ console.puts('Target path: %s' % context.style.literal(self.target_path))
+ if self.repository_url is not None:
+ console.puts('Repository URL: %s' % context.style.literal(self.repository_url))
+ if self.repository_credential:
+ console.puts('Repository credential: %s'
+ % context.style.literal(self.repository_credential))
+ utils.dump_parameters(context, self.properties)
+
+
+class PolicyBase(structure.ModelMixin):
+ """
+ An instance of a :class:`PolicyTemplate`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`target_node_ids`: Must be represented in the :class:`ServiceInstance`
+ * :code:`target_group_ids`: Must be represented in the :class:`ServiceInstance`
+ """
+ __tablename__ = 'policy'
+
+ __private_fields__ = ['service_instance_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ # endregion
+ type_name = Column(Text)
+ target_node_ids = Column(aria_types.StrictList(basestring))
+ target_group_ids = Column(aria_types.StrictList(basestring))
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_instnce(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('type_name', self.type_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('target_node_ids', self.target_node_ids),
+ ('target_group_ids', self.target_group_ids)))
+
+ def validate(self, context):
+ if context.modeling.policy_types.get_descendant(self.type_name) is None:
+ context.validation.report('policy "%s" has an unknown type: %s'
+ % (self.name, utils.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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: %s' % context.style.node(self.name))
+ with context.style.indent:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ utils.dump_parameters(context, self.properties)
+ if self.target_node_ids:
+ console.puts('Target nodes:')
+ with context.style.indent:
+ for node_id in self.target_node_ids:
+ console.puts(context.style.node(node_id))
+ if self.target_group_ids:
+ console.puts('Target groups:')
+ with context.style.indent:
+ for group_id in self.target_group_ids:
+ console.puts(context.style.node(group_id))
+
+
+class GroupPolicyBase(structure.ModelMixin):
+ """
+ Policies applied to groups.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`triggers`: Dict of :class:`GroupPolicyTrigger`
+ """
+ __tablename__ = 'group_policy'
+
+ __private_fields__ = ['group_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def group_fk(cls):
+ return cls.foreign_key('group')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+
+ # region many-to-one relationships
+ @declared_attr
+ def group(cls):
+ return cls.many_to_one_relationship('group')
+
+ # end region
+
+ # region many-to-many relationships
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('triggers', formatting.as_raw_list(self.triggers))))
+
+ def validate(self, context):
+ if context.modeling.policy_types.get_descendant(self.type_name) is None:
+ context.validation.report(
+ 'group policy "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.triggers)
+
+ 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.triggers, 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('Group policy type: %s' % context.style.type(self.type_name))
+ utils.dump_parameters(context, self.properties)
+ utils.dump_dict_values(context, self.triggers, 'Triggers')
+
+
+class GroupPolicyTriggerBase(structure.ModelMixin):
+ """
+ Triggers for :class:`GroupPolicy`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'group_policy_trigger'
+
+ __private_fields__ = ['group_policy_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def group_policy_fk(cls):
+ return cls.foreign_key('group_policy')
+
+ # endregion
+
+ description = Column(Text)
+ implementation = Column(Text)
+
+ # region many-to-one relationships
+
+ @declared_attr
+ def group_policy(cls):
+ return cls.many_to_one_relationship('group_policy')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('implementation', self.implementation),
+ ('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('Implementation: %s' % context.style.literal(self.implementation))
+ utils.dump_parameters(context, self.properties)
+
+
+class MappingBase(structure.ModelMixin):
+ """
+ An instance of a :class:`MappingTemplate`.
+
+ Properties:
+
+ * :code:`mapped_name`: Exposed capability or requirement name
+ * :code:`node_id`: Must be represented in the :class:`ServiceInstance`
+ * :code:`name`: Name of capability or requirement at the node
+ """
+ __tablename__ = 'mapping'
+
+ mapped_name = Column(Text)
+ node_id = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('mapped_name', self.mapped_name),
+ ('node_id', self.node_id),
+ ('name', self.name)))
+
+ def dump(self, context):
+ console.puts('%s -> %s.%s'
+ % (context.style.node(self.mapped_name),
+ context.style.node(self.node_id),
+ context.style.node(self.name)))
+
+
+class SubstitutionBase(structure.ModelMixin):
+ """
+ An instance of a :class:`SubstitutionTemplate`.
+
+ Properties:
+
+ * :code:`node_type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`capabilities`: Dict of :class:`Mapping`
+ * :code:`requirements`: Dict of :class:`Mapping`
+ """
+ __tablename__ = 'substitution'
+
+ node_type_name = Column(Text)
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def capabilities(cls):
+ return cls.many_to_many_relationship('mapping', table_prefix='capabilities')
+
+ @declared_attr
+ def requirements(cls):
+ return cls.many_to_many_relationship('mapping',
+ table_prefix='requirements',
+ relationship_kwargs=dict(lazy='dynamic'))
+
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('node_type_name', self.node_type_name),
+ ('capabilities', formatting.as_raw_list(self.capabilities)),
+ ('requirements', formatting.as_raw_list(self.requirements))))
+
+ def validate(self, context):
+ if context.modeling.node_types.get_descendant(self.node_type_name) is None:
+ context.validation.report('substitution "%s" has an unknown type: %s'
+ % (self.name, # pylint: disable=no-member
+ # TODO fix self.name reference
+ formatting.safe_repr(self.node_type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.capabilities)
+ utils.validate_dict_values(context, self.requirements)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, container, self.capabilities, report_issues)
+ utils.coerce_dict_values(context, container, self.requirements, report_issues)
+
+ def dump(self, context):
+ console.puts('Substitution:')
+ with context.style.indent:
+ console.puts('Node type: %s' % context.style.type(self.node_type_name))
+ utils.dump_dict_values(context, self.capabilities, 'Capability mappings')
+ utils.dump_dict_values(context, self.requirements, 'Requirement mappings')
+
+
+# endregion
+
+# region Node instances
+
+class NodeBase(structure.ModelMixin):
+ """
+ An instance of a :class:`NodeTemplate`.
+
+ Nodes may have zero or more :class:`Relationship` instances to other nodes.
+
+ Properties:
+
+ * :code:`id`: Unique ID (prefixed with the template name)
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`interfaces`: Dict of :class:`Interface`
+ * :code:`artifacts`: Dict of :class:`Artifact`
+ * :code:`capabilities`: Dict of :class:`CapabilityTemplate`
+ * :code:`relationships`: List of :class:`Relationship`
+ """
+ __tablename__ = 'node'
+
+ __private_fields__ = ['service_instance_fk',
+ 'host_fk',
+ 'node_template_fk']
+
+ # region foreign_keys
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ @declared_attr
+ def host_fk(cls):
+ return cls.foreign_key('node', nullable=True)
+
+ @declared_attr
+ def node_template_fk(cls):
+ return cls.foreign_key('node_template')
+
+ # endregion
+
+ type_name = Column(Text)
+ template_name = Column(Text)
+
+ # region orchestrator required columns
+ runtime_properties = Column(aria_types.Dict)
+ scaling_groups = Column(aria_types.List)
+ state = Column(Text, nullable=False)
+ version = Column(Integer, default=1)
+
+ @declared_attr
+ def plugins(cls):
+ return association_proxy('node_template', 'plugins')
+
+ @declared_attr
+ def host(cls):
+ return cls.relationship_to_self('host_fk')
+
+ @declared_attr
+ def service_instance_name(cls):
+ return association_proxy('service_instance', 'name')
+
+ @property
+ def ip(self):
+ 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 = [prop for prop in host_node.properties if prop.name == 'ip']
+ if host_ip_property:
+ return host_ip_property[0].value
+ return None
+
+ @declared_attr
+ def node_template(cls):
+ return cls.many_to_one_relationship('node_template')
+
+ @declared_attr
+ def service_template(cls):
+ return association_proxy('service_instance', 'service_template')
+ # endregion
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ def satisfy_requirements(self, context):
+ node_template = context.modeling.model.node_templates.get(self.template_name)
+ satisfied = True
+ for i in range(len(node_template.requirement_templates)):
+ requirement_template = node_template.requirement_templates[i]
+
+ # 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,
+ requirement_template_index=i)
+ else:
+ context.validation.report('requirement "%s" of node "%s" has no target node '
+ 'template' % (requirement_template.name,
+ self.id),
+ level=validation.Issue.BETWEEN_INSTANCES)
+ satisfied = False
+ return satisfied
+
+ def _satisfy_capability(self, context, target_node_capability, target_node_template,
+ requirement_template, requirement_template_index):
+ # 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:
+ relationship = RelationshipBase(
+ name=requirement_template.name,
+ source_requirement_index=requirement_template_index,
+ target_node_id=target_node.id,
+ target_capability_name=target_capability.name
+ )
+ self.relationships.append(relationship)
+ else:
+ context.validation.report('requirement "%s" of node "%s" targets node '
+ 'template "%s" but its instantiated nodes do not '
+ 'have enough capacity'
+ % (requirement_template.name,
+ self.id,
+ target_node_template.name),
+ level=validation.Issue.BETWEEN_INSTANCES)
+ return False
+ else:
+ context.validation.report('requirement "%s" of node "%s" targets node template '
+ '"%s" but it has no instantiated nodes'
+ % (requirement_template.name,
+ self.id,
+ 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 "%s" of node "%s" requires at least %d '
+ 'relationships but has %d'
+ % (capability.name,
+ self.id,
+ capability.min_occurrences,
+ capability.occurrences),
+ level=validation.Issue.BETWEEN_INSTANCES)
+ satisfied = False
+ return satisfied
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('id', self.id),
+ ('type_name', self.type_name),
+ ('template_name', self.template_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.relationships))))
+
+ def validate(self, context):
+ if len(self.id) > context.modeling.id_max_length:
+ context.validation.report('"%s" has an ID longer than the limit of %d characters: %d'
+ % (self.id,
+ context.modeling.id_max_length,
+ len(self.id)),
+ 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.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.relationships, report_issues)
+
+ def dump(self, context):
+ console.puts('Node: %s' % context.style.node(self.id))
+ with context.style.indent:
+ console.puts('Template: %s' % context.style.node(self.template_name))
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ utils.dump_parameters(context, self.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.relationships, 'Relationships')
+
+
+class GroupBase(structure.ModelMixin):
+ """
+ An instance of a :class:`GroupTemplate`.
+
+ Properties:
+
+ * :code:`id`: Unique ID (prefixed with the template name)
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`interfaces`: Dict of :class:`Interface`
+ * :code:`policies`: Dict of :class:`GroupPolicy`
+ * :code:`member_node_ids`: Must be represented in the :class:`ServiceInstance`
+ * :code:`member_group_ids`: Must be represented in the :class:`ServiceInstance`
+ """
+ __tablename__ = 'group'
+
+ __private_fields__ = ['service_instance_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ # endregion
+
+ type_name = Column(Text)
+ template_name = Column(Text)
+ member_node_ids = Column(aria_types.StrictList(basestring))
+ member_group_ids = Column(aria_types.StrictList(basestring))
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ # region many-to-many relationships
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('id', self.id),
+ ('type_name', self.type_name),
+ ('template_name', self.template_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('interfaces', formatting.as_raw_list(self.interfaces)),
+ ('policies', formatting.as_raw_list(self.policies)),
+ ('member_node_ids', self.member_node_ids),
+ ('member_group_ids', self.member_group_ids)))
+
+ def validate(self, context):
+ if context.modeling.group_types.get_descendant(self.type_name) is None:
+ context.validation.report('group "%s" has an unknown type: %s'
+ % (self.name, # pylint: disable=no-member
+ # TODO fix self.name reference
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.interfaces)
+ utils.validate_dict_values(context, self.policies)
+
+ 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)
+ utils.coerce_dict_values(context, container, self.policies, report_issues)
+
+ def dump(self, context):
+ console.puts('Group: %s' % context.style.node(self.id))
+ with context.style.indent:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ console.puts('Template: %s' % context.style.type(self.template_name))
+ utils.dump_parameters(context, self.properties)
+ utils.dump_interfaces(context, self.interfaces)
+ utils.dump_dict_values(context, self.policies, 'Policies')
+ if self.member_node_ids:
+ console.puts('Member nodes:')
+ with context.style.indent:
+ for node_id in self.member_node_ids:
+ console.puts(context.style.node(node_id))
+
+# endregion
+
+# region Relationship instances
+
+
+class RelationshipBase(structure.ModelMixin):
+ """
+ Connects :class:`Node` to another node.
+
+ An instance of a :class:`RelationshipTemplate`.
+
+ Properties:
+
+ * :code:`name`: Name (usually the name of the requirement at the source node template)
+ * :code:`source_requirement_index`: Must be represented in the source node template
+ * :code:`target_node_id`: Must be represented in the :class:`ServiceInstance`
+ * :code:`target_capability_name`: Matches the capability at the target node
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`template_name`: Must be represented in the :class:`ServiceModel`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`source_interfaces`: Dict of :class:`Interface`
+ * :code:`target_interfaces`: Dict of :class:`Interface`
+ """
+ __tablename__ = 'relationship'
+
+ __private_fields__ = ['source_node_fk',
+ 'target_node_fk']
+
+ source_requirement_index = Column(Integer)
+ target_node_id = Column(Text)
+ target_capability_name = Column(Text)
+ type_name = Column(Text)
+ template_name = Column(Text)
+
+ # # region orchestrator required columns
+ source_position = Column(Integer)
+ target_position = Column(Integer)
+
+ @declared_attr
+ def source_node_fk(cls):
+ return cls.foreign_key('node', nullable=True)
+
+ @declared_attr
+ def source_node(cls):
+ return cls.many_to_one_relationship(
+ 'node',
+ 'source_node_fk',
+ backreference='outbound_relationships',
+ backref_kwargs=dict(
+ order_by=cls.source_position,
+ collection_class=ordering_list('source_position', count_from=0),
+ )
+ )
+
+ @declared_attr
+ def source_node_name(cls):
+ return association_proxy('source_node', cls.name_column_name())
+
+ @declared_attr
+ def target_node_fk(cls):
+ return cls.foreign_key('node', nullable=True)
+
+ @declared_attr
+ def target_node(cls):
+ return cls.many_to_one_relationship(
+ 'node',
+ 'target_node_fk',
+ backreference='inbound_relationships',
+ backref_kwargs=dict(
+ order_by=cls.target_position,
+ collection_class=ordering_list('target_position', count_from=0),
+ )
+ )
+
+ @declared_attr
+ def target_node_name(cls):
+ return association_proxy('target_node', cls.name_column_name())
+ # endregion
+
+ # region many-to-many relationship
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('source_requirement_index', self.source_requirement_index),
+ ('target_node_id', self.target_node_id),
+ ('target_capability_name', self.target_capability_name),
+ ('type_name', self.type_name),
+ ('template_name', self.template_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('source_interfaces', formatting.as_raw_list(self.source_interfaces)),
+ ('target_interfaces', formatting.as_raw_list(self.target_interfaces))))
+
+ def validate(self, context):
+ if self.type_name:
+ if context.modeling.relationship_types.get_descendant(self.type_name) is None:
+ context.validation.report('relationship "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.source_interfaces)
+ utils.validate_dict_values(context, self.target_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.source_interfaces, report_issues)
+ utils.coerce_dict_values(context, container, self.target_interfaces, report_issues)
+
+ def dump(self, context):
+ if self.name:
+ if self.source_requirement_index is not None:
+ console.puts('%s (%d) ->' % (
+ context.style.node(self.name),
+ self.source_requirement_index))
+ else:
+ console.puts('%s ->' % context.style.node(self.name))
+ else:
+ console.puts('->')
+ with context.style.indent:
+ console.puts('Node: %s' % context.style.node(self.target_node_id))
+ if self.target_capability_name is not None:
+ console.puts('Capability: %s' % context.style.node(self.target_capability_name))
+ if self.type_name is not None:
+ console.puts('Relationship type: %s' % context.style.type(self.type_name))
+ if self.template_name is not None:
+ console.puts('Relationship template: %s' % context.style.node(self.template_name))
+ utils.dump_parameters(context, self.properties)
+ utils.dump_interfaces(context, self.source_interfaces, 'Source interfaces')
+ utils.dump_interfaces(context, self.target_interfaces, 'Target interfaces')
+
+# endregion
[4/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/model.py b/aria/storage/modeling/model.py
new file mode 100644
index 0000000..62b90b3
--- /dev/null
+++ b/aria/storage/modeling/model.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.
+
+from sqlalchemy.ext.declarative import declarative_base
+
+from . import (
+ template_elements,
+ instance_elements,
+ orchestrator_elements,
+ elements,
+ structure,
+)
+
+__all__ = (
+ 'aria_declarative_base',
+
+ 'Parameter',
+
+ 'MappingTemplate',
+ 'InterfaceTemplate',
+ 'OperationTemplate',
+ 'ServiceTemplate',
+ 'NodeTemplate',
+ 'GroupTemplate',
+ 'ArtifactTemplate',
+ 'PolicyTemplate',
+ 'GroupPolicyTemplate',
+ 'GroupPolicyTriggerTemplate',
+ 'RequirementTemplate',
+ 'CapabilityTemplate',
+
+ 'Mapping',
+ 'Substitution',
+ 'ServiceInstance',
+ 'Node',
+ 'Relationship',
+ 'Artifact',
+ 'Group',
+ 'Interface',
+ 'Operation',
+ 'Capability',
+ 'Policy',
+ 'GroupPolicy',
+ 'GroupPolicyTrigger',
+
+ 'Execution',
+ 'ServiceInstanceUpdate',
+ 'ServiceInstanceUpdateStep',
+ 'ServiceInstanceModification',
+ 'Plugin',
+ 'Task'
+)
+
+aria_declarative_base = declarative_base(cls=structure.ModelIDMixin)
+
+# pylint: disable=abstract-method
+
+# region elements
+
+
+class Parameter(aria_declarative_base, elements.ParameterBase):
+ pass
+
+# endregion
+
+# region template models
+
+
+class MappingTemplate(aria_declarative_base, template_elements.MappingTemplateBase):
+ pass
+
+
+class SubstitutionTemplate(aria_declarative_base, template_elements.SubstitutionTemplateBase):
+ pass
+
+
+class InterfaceTemplate(aria_declarative_base, template_elements.InterfaceTemplateBase):
+ pass
+
+
+class OperationTemplate(aria_declarative_base, template_elements.OperationTemplateBase):
+ pass
+
+
+class ServiceTemplate(aria_declarative_base, template_elements.ServiceTemplateBase):
+ pass
+
+
+class NodeTemplate(aria_declarative_base, template_elements.NodeTemplateBase):
+ pass
+
+
+class GroupTemplate(aria_declarative_base, template_elements.GroupTemplateBase):
+ pass
+
+
+class ArtifactTemplate(aria_declarative_base, template_elements.ArtifactTemplateBase):
+ pass
+
+
+class PolicyTemplate(aria_declarative_base, template_elements.PolicyTemplateBase):
+ pass
+
+
+class GroupPolicyTemplate(aria_declarative_base, template_elements.GroupPolicyTemplateBase):
+ pass
+
+
+class GroupPolicyTriggerTemplate(aria_declarative_base,
+ template_elements.GroupPolicyTriggerTemplateBase):
+ pass
+
+
+class RequirementTemplate(aria_declarative_base, template_elements.RequirementTemplateBase):
+ pass
+
+
+class CapabilityTemplate(aria_declarative_base, template_elements.CapabilityTemplateBase):
+ pass
+
+
+# endregion
+
+# region instance models
+
+class Mapping(aria_declarative_base, instance_elements.MappingBase):
+ pass
+
+
+class Substitution(aria_declarative_base, instance_elements.SubstitutionBase):
+ pass
+
+
+class ServiceInstance(aria_declarative_base, instance_elements.ServiceInstanceBase):
+ pass
+
+
+class Node(aria_declarative_base, instance_elements.NodeBase):
+ pass
+
+
+class Relationship(aria_declarative_base, instance_elements.RelationshipBase):
+ pass
+
+
+class Artifact(aria_declarative_base, instance_elements.ArtifactBase):
+ pass
+
+
+class Group(aria_declarative_base, instance_elements.GroupBase):
+ pass
+
+
+class Interface(aria_declarative_base, instance_elements.InterfaceBase):
+ pass
+
+
+class Operation(aria_declarative_base, instance_elements.OperationBase):
+ pass
+
+
+class Capability(aria_declarative_base, instance_elements.CapabilityBase):
+ pass
+
+
+class Policy(aria_declarative_base, instance_elements.PolicyBase):
+ pass
+
+
+class GroupPolicy(aria_declarative_base, instance_elements.GroupPolicyBase):
+ pass
+
+
+class GroupPolicyTrigger(aria_declarative_base, instance_elements.GroupPolicyTriggerBase):
+ pass
+
+
+# endregion
+
+# region orchestrator models
+
+class Execution(aria_declarative_base, orchestrator_elements.Execution):
+ pass
+
+
+class ServiceInstanceUpdate(aria_declarative_base,
+ orchestrator_elements.ServiceInstanceUpdateBase):
+ pass
+
+
+class ServiceInstanceUpdateStep(aria_declarative_base,
+ orchestrator_elements.ServiceInstanceUpdateStepBase):
+ pass
+
+
+class ServiceInstanceModification(aria_declarative_base,
+ orchestrator_elements.ServiceInstanceModificationBase):
+ pass
+
+
+class Plugin(aria_declarative_base, orchestrator_elements.PluginBase):
+ pass
+
+
+class Task(aria_declarative_base, orchestrator_elements.TaskBase):
+ pass
+# endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/orchestrator_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/orchestrator_elements.py b/aria/storage/modeling/orchestrator_elements.py
new file mode 100644
index 0000000..5f7a3f2
--- /dev/null
+++ b/aria/storage/modeling/orchestrator_elements.py
@@ -0,0 +1,468 @@
+# 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.models module
+Path: aria.storage.models
+
+models module holds aria's models.
+
+classes:
+ * Field - represents a single field.
+ * IterField - represents an iterable field.
+ * Model - abstract model implementation.
+ * Snapshot - snapshots implementation model.
+ * Deployment - deployment implementation model.
+ * DeploymentUpdateStep - deployment update step implementation model.
+ * DeploymentUpdate - deployment update implementation model.
+ * DeploymentModification - deployment modification implementation model.
+ * Execution - execution implementation model.
+ * Node - node implementation model.
+ * Relationship - relationship implementation model.
+ * NodeInstance - node instance implementation model.
+ * RelationshipInstance - relationship instance implementation model.
+ * Plugin - plugin implementation model.
+"""
+from collections import namedtuple
+from datetime import datetime
+
+from sqlalchemy import (
+ Column,
+ Integer,
+ Text,
+ DateTime,
+ Boolean,
+ Enum,
+ String,
+ Float,
+ orm,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from aria.orchestrator.exceptions import TaskAbortException, TaskRetryException
+
+from .type import List, Dict
+from .structure import ModelMixin
+
+__all__ = (
+ 'ServiceInstanceUpdateStepBase',
+ 'ServiceInstanceUpdateBase',
+ 'ServiceInstanceModificationBase',
+ 'Execution',
+ 'PluginBase',
+ 'TaskBase'
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+class Execution(ModelMixin):
+ """
+ Execution model representation.
+ """
+ # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+ __tablename__ = 'execution'
+
+ __private_fields__ = ['service_instance_fk']
+
+ TERMINATED = 'terminated'
+ FAILED = 'failed'
+ CANCELLED = 'cancelled'
+ PENDING = 'pending'
+ STARTED = 'started'
+ CANCELLING = 'cancelling'
+ FORCE_CANCELLING = 'force_cancelling'
+
+ STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING]
+ END_STATES = [TERMINATED, FAILED, CANCELLED]
+ ACTIVE_STATES = [state for state in STATES if state not in END_STATES]
+
+ VALID_TRANSITIONS = {
+ PENDING: [STARTED, CANCELLED],
+ STARTED: END_STATES + [CANCELLING],
+ CANCELLING: END_STATES + [FORCE_CANCELLING]
+ }
+
+ @orm.validates('status')
+ def validate_status(self, key, value):
+ """Validation function that verifies execution status transitions are OK"""
+ try:
+ current_status = getattr(self, key)
+ except AttributeError:
+ return
+ valid_transitions = self.VALID_TRANSITIONS.get(current_status, [])
+ if all([current_status is not None,
+ current_status != value,
+ value not in valid_transitions]):
+ raise ValueError('Cannot change execution status from {current} to {new}'.format(
+ current=current_status,
+ new=value))
+ return value
+
+ created_at = Column(DateTime, index=True)
+ started_at = Column(DateTime, nullable=True, index=True)
+ ended_at = Column(DateTime, nullable=True, index=True)
+ error = Column(Text, nullable=True)
+ is_system_workflow = Column(Boolean, nullable=False, default=False)
+ parameters = Column(Dict)
+ status = Column(Enum(*STATES, name='execution_status'), default=PENDING)
+ workflow_name = Column(Text)
+
+ @declared_attr
+ def service_template(cls):
+ return association_proxy('service_instance', 'service_template')
+
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ @declared_attr
+ def service_instance_name(cls):
+ return association_proxy('service_instance', cls.name_column_name())
+
+ @declared_attr
+ def service_template_name(cls):
+ return association_proxy('service_instance', 'service_template_name')
+
+ def __str__(self):
+ return '<{0} id=`{1}` (status={2})>'.format(
+ self.__class__.__name__,
+ getattr(self, self.name_column_name()),
+ self.status
+ )
+
+
+class ServiceInstanceUpdateBase(ModelMixin):
+ """
+ Deployment update model representation.
+ """
+ # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+ steps = None
+
+ __tablename__ = 'service_instance_update'
+ __private_fields__ = ['service_instance_fk',
+ 'execution_fk']
+
+ _private_fields = ['execution_fk', 'deployment_fk']
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ service_instance_plan = Column(Dict, nullable=False)
+ service_instance_update_node_instances = Column(Dict)
+ service_instance_update_service_instance = Column(Dict)
+ service_instance_update_nodes = Column(List)
+ modified_entity_ids = Column(Dict)
+ state = Column(Text)
+
+ @declared_attr
+ def execution_fk(cls):
+ return cls.foreign_key('execution', nullable=True)
+
+ @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_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance')
+
+ @declared_attr
+ def service_instance_name(cls):
+ return association_proxy('service_instance', cls.name_column_name())
+
+ def to_dict(self, suppress_error=False, **kwargs):
+ dep_update_dict = super(ServiceInstanceUpdateBase, 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 ServiceInstanceUpdateStepBase(ModelMixin):
+ """
+ Deployment update step model representation.
+ """
+ # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column.
+ __tablename__ = 'service_instance_update_step'
+ __private_fields__ = ['service_instance_update_fk']
+
+ _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, '
+ 'POLICY_TYPE, POLICY_TRIGGER, PLUGIN')
+ ENTITY_TYPES = _entity_types(
+ NODE='node',
+ RELATIONSHIP='relationship',
+ PROPERTY='property',
+ OPERATION='operation',
+ WORKFLOW='workflow',
+ OUTPUT='output',
+ DESCRIPTION='description',
+ GROUP='group',
+ POLICY_TYPE='policy_type',
+ POLICY_TRIGGER='policy_trigger',
+ 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_instance_update_fk(cls):
+ return cls.foreign_key('service_instance_update')
+
+ @declared_attr
+ def service_instance_update(cls):
+ return cls.many_to_one_relationship('service_instance_update',
+ backreference='steps')
+
+ @declared_attr
+ def deployment_update_name(cls):
+ return association_proxy('deployment_update', cls.name_column_name())
+
+ 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 ServiceInstanceModificationBase(ModelMixin):
+ """
+ Deployment modification model representation.
+ """
+ __tablename__ = 'service_instance_modification'
+ __private_fields__ = ['service_instance_fk']
+
+ 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_nodes = Column(Dict)
+ node_instances = Column(Dict)
+ status = Column(Enum(*STATES, name='deployment_modification_status'))
+
+ @declared_attr
+ def service_instance_fk(cls):
+ return cls.foreign_key('service_instance')
+
+ @declared_attr
+ def service_instance(cls):
+ return cls.many_to_one_relationship('service_instance',
+ backreference='modifications')
+
+ @declared_attr
+ def service_instance_name(cls):
+ return association_proxy('service_instance', cls.name_column_name())
+
+
+class PluginBase(ModelMixin):
+ """
+ Plugin model representation.
+ """
+ __tablename__ = 'plugin'
+
+ 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)
+ uploaded_at = Column(DateTime, nullable=False, index=True)
+ wheels = Column(List, nullable=False)
+
+
+class TaskBase(ModelMixin):
+ """
+ A Model which represents an task
+ """
+ __tablename__ = 'task'
+ __private_fields__ = ['node_fk',
+ 'relationship_fk',
+ 'execution_fk',
+ 'plugin_fk']
+
+ @declared_attr
+ def node_fk(cls):
+ return cls.foreign_key('node', nullable=True)
+
+ @declared_attr
+ def node_name(cls):
+ return association_proxy('node', cls.name_column_name())
+
+ @declared_attr
+ def node(cls):
+ return cls.many_to_one_relationship('node')
+
+ @declared_attr
+ def relationship_fk(cls):
+ return cls.foreign_key('relationship', nullable=True)
+
+ @declared_attr
+ def relationship_name(cls):
+ return association_proxy('relationships', cls.name_column_name())
+
+ @declared_attr
+ def relationship(cls):
+ return cls.many_to_one_relationship('relationship')
+
+ @declared_attr
+ def plugin_fk(cls):
+ return cls.foreign_key('plugin', nullable=True)
+
+ @declared_attr
+ def plugin(cls):
+ return cls.many_to_one_relationship('plugin')
+
+ @declared_attr
+ def execution_fk(cls):
+ return cls.foreign_key('execution', nullable=True)
+
+ @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())
+
+ PENDING = 'pending'
+ RETRYING = 'retrying'
+ SENT = 'sent'
+ STARTED = 'started'
+ SUCCESS = 'success'
+ FAILED = 'failed'
+ STATES = (
+ PENDING,
+ RETRYING,
+ SENT,
+ STARTED,
+ SUCCESS,
+ FAILED,
+ )
+
+ WAIT_STATES = [PENDING, RETRYING]
+ END_STATES = [SUCCESS, FAILED]
+
+ RUNS_ON_SOURCE = 'source'
+ RUNS_ON_TARGET = 'target'
+ RUNS_ON_NODE_INSTANCE = 'node_instance'
+ RUNS_ON = (RUNS_ON_NODE_INSTANCE, RUNS_ON_SOURCE, RUNS_ON_TARGET)
+
+ @orm.validates('max_attempts')
+ def validate_max_attempts(self, _, value): # pylint: disable=no-self-use
+ """Validates that max attempts is either -1 or a positive number"""
+ if value < 1 and value != TaskBase.INFINITE_RETRIES:
+ raise ValueError('Max attempts can be either -1 (infinite) or any positive number. '
+ 'Got {value}'.format(value=value))
+ return value
+
+ INFINITE_RETRIES = -1
+
+ status = Column(Enum(*STATES, name='status'), default=PENDING)
+
+ due_at = Column(DateTime, default=datetime.utcnow)
+ started_at = Column(DateTime, default=None)
+ ended_at = Column(DateTime, default=None)
+ max_attempts = Column(Integer, default=1)
+ retry_count = Column(Integer, default=0)
+ retry_interval = Column(Float, default=0)
+ ignore_failure = Column(Boolean, default=False)
+
+ # Operation specific fields
+ implementation = Column(String)
+ inputs = Column(Dict)
+ # 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
+ def runs_on(self):
+ if self._runs_on == self.RUNS_ON_NODE_INSTANCE:
+ return self.node
+ elif self._runs_on == self.RUNS_ON_SOURCE:
+ return self.relationship.source_node # pylint: disable=no-member
+ elif self._runs_on == self.RUNS_ON_TARGET:
+ return self.relationship.target_node # pylint: disable=no-member
+ return None
+
+ @property
+ def actor(self):
+ """
+ Return the actor of the task
+ :return:
+ """
+ return self.node or self.relationship
+
+ @classmethod
+ def as_node_instance(cls, instance, runs_on, **kwargs):
+ return cls(node=instance, _runs_on=runs_on, **kwargs)
+
+ @classmethod
+ def as_relationship_instance(cls, instance, runs_on, **kwargs):
+ return cls(relationship=instance, _runs_on=runs_on, **kwargs)
+
+ @staticmethod
+ def abort(message=None):
+ raise TaskAbortException(message)
+
+ @staticmethod
+ def retry(message=None, retry_interval=None):
+ raise TaskRetryException(message, retry_interval=retry_interval)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/structure.py b/aria/storage/modeling/structure.py
new file mode 100644
index 0000000..eacdb44
--- /dev/null
+++ b/aria/storage/modeling/structure.py
@@ -0,0 +1,320 @@
+# 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:
+ * Field - represents a single field.
+ * IterField - represents an iterable field.
+ * PointerField - represents a single pointer field.
+ * IterPointerField - represents an iterable pointers field.
+ * Model - abstract model implementation.
+"""
+
+from sqlalchemy.orm import relationship, backref
+from sqlalchemy.ext import associationproxy
+from sqlalchemy import (
+ Column,
+ ForeignKey,
+ Integer,
+ Text,
+ Table,
+)
+
+from . import utils
+
+
+class Function(object):
+ """
+ An intrinsic function.
+
+ Serves as a placeholder for a value that should eventually be derived
+ by calling the function.
+ """
+
+ @property
+ def as_raw(self):
+ raise NotImplementedError
+
+ def _evaluate(self, context, container):
+ raise NotImplementedError
+
+ def __deepcopy__(self, memo):
+ # Circumvent cloning in order to maintain our state
+ return self
+
+
+class ElementBase(object):
+ """
+ Base class for :class:`ServiceInstance` elements.
+
+ All elements 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 ModelElementBase(ElementBase):
+ """
+ Base class for :class:`ServiceModel` elements.
+
+ All model elements can be instantiated into :class:`ServiceInstance` elements.
+ """
+
+ def instantiate(self, context, container):
+ raise NotImplementedError
+
+
+class ModelMixin(ModelElementBase):
+
+ @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 _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
+
+ @classmethod
+ def foreign_key(cls, table_name, nullable=False):
+ """Return a ForeignKey object with the relevant
+
+ :param table: Unique id column in the parent table
+ :param nullable: Should the column be allowed to remain empty
+ """
+ return Column(Integer,
+ ForeignKey('{tablename}.id'.format(tablename=table_name), ondelete='CASCADE'),
+ nullable=nullable)
+
+ @classmethod
+ def one_to_one_relationship(cls, table_name, backreference=None):
+ return relationship(lambda: cls._get_cls_by_tablename(table_name),
+ backref=backref(backreference or cls.__tablename__, uselist=False))
+
+ @classmethod
+ def many_to_one_relationship(cls,
+ parent_table_name,
+ foreign_key_column=None,
+ backreference=None,
+ backref_kwargs=None,
+ **kwargs):
+ """Return a one-to-many SQL relationship object
+ Meant to be used from inside the *child* object
+
+ :param parent_class: Class of the parent table
+ :param cls: Class of the child table
+ :param foreign_key_column: 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)
+ """
+ relationship_kwargs = kwargs
+ if foreign_key_column:
+ relationship_kwargs.setdefault('foreign_keys', getattr(cls, foreign_key_column))
+
+ backref_kwargs = backref_kwargs or {}
+ 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 relationship(lambda: cls._get_cls_by_tablename(parent_table_name),
+ backref=backref(backreference or utils.pluralize(cls.__tablename__),
+ **backref_kwargs or {}),
+ **relationship_kwargs)
+
+ @classmethod
+ def relationship_to_self(cls, local_column):
+
+ remote_side_str = '{cls.__name__}.{remote_column}'.format(
+ cls=cls,
+ remote_column=cls.id_column_name()
+ )
+ primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format(
+ remote_side_str=remote_side_str,
+ cls=cls,
+ local_column=local_column)
+ return relationship(cls._get_cls_by_tablename(cls.__tablename__).__name__,
+ primaryjoin=primaryjoin_str,
+ remote_side=remote_side_str,
+ post_update=True)
+
+ @classmethod
+ def many_to_many_relationship(cls, other_table_name, table_prefix, 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_name: The class of the table we're connecting to
+ :param table_prefix: Custom prefix for the helper table name and the
+ backreference name
+ """
+ current_table_name = cls.__tablename__
+ current_column_name = '{0}_id'.format(current_table_name)
+ current_foreign_key = '{0}.id'.format(current_table_name)
+
+ other_column_name = '{0}_id'.format(other_table_name)
+ other_foreign_key = '{0}.id'.format(other_table_name)
+
+ helper_table_name = '{0}_{1}'.format(current_table_name, other_table_name)
+
+ backref_name = current_table_name
+ if table_prefix:
+ helper_table_name = '{0}_{1}'.format(table_prefix, helper_table_name)
+ backref_name = '{0}_{1}'.format(table_prefix, backref_name)
+
+ secondary_table = cls.get_secondary_table(
+ cls.metadata,
+ helper_table_name,
+ current_column_name,
+ other_column_name,
+ current_foreign_key,
+ other_foreign_key
+ )
+
+ return relationship(
+ lambda: cls._get_cls_by_tablename(other_table_name),
+ secondary=secondary_table,
+ backref=backref(backref_name),
+ **(relationship_kwargs or {})
+ )
+
+ @staticmethod
+ def get_secondary_table(metadata,
+ helper_table_name,
+ first_column_name,
+ second_column_name,
+ first_foreign_key,
+ second_foreign_key):
+ """Create a helper table for a many-to-many relationship
+
+ :param helper_table_name: 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_name,
+ metadata,
+ Column(
+ first_column_name,
+ Integer,
+ ForeignKey(first_foreign_key)
+ ),
+ Column(
+ second_column_name,
+ Integer,
+ ForeignKey(second_foreign_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 _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__', []))
+
+ def __repr__(self):
+ return '<{__class__.__name__} id=`{id}`>'.format(
+ __class__=self.__class__,
+ id=getattr(self, self.name_column_name()))
+
+
+class ModelIDMixin(object):
+ id = Column(Integer, primary_key=True, autoincrement=True)
+ name = Column(Text, nullable=True, index=True)
+
+ @classmethod
+ def id_column_name(cls):
+ return 'id'
+
+ @classmethod
+ def name_column_name(cls):
+ return 'name'
[7/7] incubator-ariatosca git commit: wip
Posted by mx...@apache.org.
wip
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/67b75425
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/67b75425
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/67b75425
Branch: refs/heads/ARIA-106-Create-sqla-logging-handler
Commit: 67b754254760db0f1276bc10a293c80c1f5f8a54
Parents: b619335
Author: mxmrlv <mx...@gmail.com>
Authored: Mon Feb 13 12:28:27 2017 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Mon Feb 13 20:09:21 2017 +0200
----------------------------------------------------------------------
aria/__init__.py | 3 +-
aria/logger.py | 42 ++++++++++-
aria/orchestrator/context/common.py | 22 +++++-
aria/orchestrator/workflows/core/engine.py | 5 ++
aria/orchestrator/workflows/executor/process.py | 1 -
aria/storage/__init__.py | 6 --
aria/storage/core.py | 16 +++--
aria/storage/modeling/model.py | 4 ++
aria/storage/modeling/orchestrator_elements.py | 12 ++++
tests/mock/topology.py | 2 +-
tests/orchestrator/context/test_operation.py | 74 ++++++++++++++++++--
tests/storage/__init__.py | 23 +++---
tests/storage/test_instrumentation.py | 6 +-
13 files changed, 176 insertions(+), 40 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/__init__.py
----------------------------------------------------------------------
diff --git a/aria/__init__.py b/aria/__init__.py
index 18eaa56..43529f0 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -97,7 +97,8 @@ def application_model_storage(api, api_kwargs=None, initiator=None, initiator_kw
storage.modeling.model.ServiceInstanceUpdateStep,
storage.modeling.model.ServiceInstanceModification,
storage.modeling.model.Plugin,
- storage.modeling.model.Task
+ storage.modeling.model.Task,
+ storage.modeling.model.Log
]
return storage.ModelStorage(api_cls=api,
api_kwargs=api_kwargs,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/logger.py
----------------------------------------------------------------------
diff --git a/aria/logger.py b/aria/logger.py
index 0002cb5..f19d286 100644
--- a/aria/logger.py
+++ b/aria/logger.py
@@ -17,8 +17,10 @@
Logging related mixins and functions
"""
+
import logging
-from logging.handlers import RotatingFileHandler
+from logging import handlers as logging_handlers
+from datetime import datetime
_base_logger = logging.getLogger('aria')
@@ -124,7 +126,7 @@ def create_file_log_handler(
"""
Create a logging.handlers.RotatingFileHandler
"""
- rotating_file = RotatingFileHandler(
+ rotating_file = logging_handlers.RotatingFileHandler(
filename=file_path,
maxBytes=max_bytes,
backupCount=backup_count,
@@ -135,5 +137,41 @@ def create_file_log_handler(
return rotating_file
+class SQLAlchemyHandler(logging.Handler):
+ def __init__(self, session, engine, log_cls, **kwargs):
+ self._session = session
+ self._engine = engine
+ self._cls = log_cls
+ super(SQLAlchemyHandler, self).__init__(**kwargs)
+
+ def emit(self, record):
+ log = self._cls(
+ logger=record.name,
+ level=record.levelname,
+ msg=record.msg,
+ created_at=datetime.utcnow()
+ )
+ self._session.add(log)
+ self._session.commit()
+
+
+class _SQLAlchemyHandlerFactory(object):
+ from aria.storage.model import Log
+
+ def __init__(self):
+ self._handler = None
+
+ def __call__(self, session, engine, model_cls=Log, level=logging.DEBUG):
+ if self._handler is None or not self._is_eq(session, engine, model_cls):
+ self._handler = SQLAlchemyHandler(session, engine, model_cls, level=level)
+ return self._handler
+
+ def _is_eq(self, session, engine, model_cls):
+ return all([self._handler._session == session,
+ self._handler._engine == engine,
+ self._handler._cls == model_cls])
+
+create_sqla_log_handler = _SQLAlchemyHandlerFactory()
+
_default_file_formatter = logging.Formatter(
'%(asctime)s [%(name)s:%(levelname)s] %(message)s <%(pathname)s:%(lineno)d>')
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 37482cf..d5ef42c 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -17,13 +17,15 @@ A common context for both workflow and operation
"""
from uuid import uuid4
+import logging
+
import jinja2
from aria import logger
from aria.storage import exceptions
-class BaseContext(logger.LoggerMixin):
+class BaseContext(object):
"""
Base context object for workflow and operation
"""
@@ -34,6 +36,7 @@ class BaseContext(logger.LoggerMixin):
service_instance_id,
model_storage,
resource_storage,
+ ctx_logger=None,
workdir=None,
**kwargs):
super(BaseContext, self).__init__(**kwargs)
@@ -42,8 +45,21 @@ class BaseContext(logger.LoggerMixin):
self._model = model_storage
self._resource = resource_storage
self._service_instance_id = service_instance_id
+ self._logger = self._init_logger(ctx_logger)
self._workdir = workdir
+ def _init_logger(self, ctx_logger=None):
+ ctx_logger = ctx_logger or logging.getLogger('aria_ctx')
+
+ # A handler should be registered only once.
+ sqla_handler = logger.create_sqla_log_handler(**self._model.all_api_kwargs)
+ if sqla_handler not in ctx_logger.handlers:
+ ctx_logger.addHandler(sqla_handler)
+
+ ctx_logger.setLevel(logging.DEBUG)
+
+ return ctx_logger
+
def __repr__(self):
return (
'{name}(name={self.name}, '
@@ -51,6 +67,10 @@ class BaseContext(logger.LoggerMixin):
.format(name=self.__class__.__name__, self=self))
@property
+ def logger(self):
+ return self._logger
+
+ @property
def model(self):
"""
Access to the model storage
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/workflows/core/engine.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index 55b4159..7148dd1 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -71,6 +71,11 @@ class Engine(logger.LoggerMixin):
except BaseException as e:
events.on_failure_workflow_signal.send(self._workflow_context, exception=e)
raise
+ finally:
+ # Each context creates its own handlers an assign them to the logger.
+ # This enables easy serialization. In order the handlers would not overlap, we
+ # need to clear them each execution.
+ self._workflow_context.logger.handlers = []
def cancel_execution(self):
"""
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index 560ac43..acc0828 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -324,7 +324,6 @@ def _main():
# This is required for the instrumentation work properly.
# See docstring of `remove_mutable_association_listener` for further details
storage_type.remove_mutable_association_listener()
-
with instrumentation.track_changes() as instrument:
try:
ctx = context_dict['context_cls'].deserialize_from_dict(**context_dict['context'])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/__init__.py b/aria/storage/__init__.py
index eaadc7e..b76bdf2 100644
--- a/aria/storage/__init__.py
+++ b/aria/storage/__init__.py
@@ -42,12 +42,6 @@ from .core import (
ModelStorage,
ResourceStorage,
)
-from .modeling import (
- structure,
- model,
- model_base,
- type
-)
from . import (
exceptions,
api,
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index 0e189e6..7d70070 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -84,6 +84,12 @@ class Storage(LoggerMixin):
self.logger.debug('{name} object is ready: {0!r}'.format(
self, name=self.__class__.__name__))
+ @property
+ def all_api_kwargs(self):
+ kwargs = self._api_kwargs.copy()
+ kwargs.update(self._additional_api_kwargs)
+ return kwargs
+
def __repr__(self):
return '{name}(api={self.api})'.format(name=self.__class__.__name__, self=self)
@@ -121,9 +127,7 @@ class ResourceStorage(Storage):
:param name:
:return:
"""
- kwargs = self._api_kwargs.copy()
- kwargs.update(self._additional_api_kwargs)
- self.registered[name] = self.api(name=name, **kwargs)
+ self.registered[name] = self.api(name=name, **self.all_api_kwargs)
self.registered[name].create()
self.logger.debug('setup {name} in storage {self!r}'.format(name=name, self=self))
@@ -148,9 +152,9 @@ class ModelStorage(Storage):
self.logger.debug('{name} in already storage {self!r}'.format(name=model_name,
self=self))
return
- kwargs = self._api_kwargs.copy()
- kwargs.update(self._additional_api_kwargs)
- self.registered[model_name] = self.api(name=model_name, model_cls=model_cls, **kwargs)
+ self.registered[model_name] = self.api(name=model_name,
+ model_cls=model_cls,
+ **self.all_api_kwargs)
self.registered[model_name].create()
self.logger.debug('setup {name} in storage {self!r}'.format(name=model_name, self=self))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/modeling/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/model.py b/aria/storage/modeling/model.py
index 62b90b3..cf7d933 100644
--- a/aria/storage/modeling/model.py
+++ b/aria/storage/modeling/model.py
@@ -216,4 +216,8 @@ class Plugin(aria_declarative_base, orchestrator_elements.PluginBase):
class Task(aria_declarative_base, orchestrator_elements.TaskBase):
pass
+
+
+class Log(aria_declarative_base, orchestrator_elements.LogBase):
+ pass
# endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/aria/storage/modeling/orchestrator_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/orchestrator_elements.py b/aria/storage/modeling/orchestrator_elements.py
index 5f7a3f2..8efc147 100644
--- a/aria/storage/modeling/orchestrator_elements.py
+++ b/aria/storage/modeling/orchestrator_elements.py
@@ -466,3 +466,15 @@ class TaskBase(ModelMixin):
@staticmethod
def retry(message=None, retry_interval=None):
raise TaskRetryException(message, retry_interval=retry_interval)
+
+
+class LogBase(ModelMixin):
+ __tablename__ = 'log'
+
+ logger = Column(String)
+ level = Column(String)
+ msg = Column(String)
+ created_at = Column(DateTime, index=True)
+
+ def __repr__(self):
+ return "<Log: {0} - {1}>".format(self.created_at, self.msg[:50])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/mock/topology.py
----------------------------------------------------------------------
diff --git a/tests/mock/topology.py b/tests/mock/topology.py
index b04fb46..d3e8b7b 100644
--- a/tests/mock/topology.py
+++ b/tests/mock/topology.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from aria.storage import model
+from aria.storage.modeling import model
from . import models
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index 3f39979..08d360c 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -38,8 +38,7 @@ global_test_holder = {}
@pytest.fixture
def ctx(tmpdir):
context = mock.context.simple(
- str(tmpdir.join('workdir')),
- inmemory=True,
+ str(tmpdir),
context_kwargs=dict(workdir=str(tmpdir.join('workdir')))
)
yield context
@@ -61,7 +60,7 @@ def test_node_operation_task_execution(ctx, executor):
node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
interface = mock.models.get_interface(
operation_name,
- operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__))
+ operation_kwargs=dict(implementation=op_path(basic_operation, module_path=__name__))
)
node.interfaces = [interface]
ctx.model.node.update(node)
@@ -102,7 +101,7 @@ def test_relationship_operation_task_execution(ctx, executor):
interface = mock.models.get_interface(
operation_name=operation_name,
- operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__)),
+ operation_kwargs=dict(implementation=op_path(basic_operation, module_path=__name__)),
edge='source'
)
@@ -210,8 +209,73 @@ def test_plugin_workdir(ctx, executor, tmpdir):
assert expected_file.read() == content
+def test_operation_logging(ctx, executor):
+ operation_name = 'aria.interfaces.lifecycle.create'
+
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ interface = mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(implementation=op_path(logged_operation, module_path=__name__))
+ )
+ node.interfaces = [interface]
+ ctx.model.node.update(node)
+
+ wf_start = 'wf_start'
+ wf_end = 'wf_end'
+
+ inputs = {
+ 'op_start': 'op_start',
+ 'op_end': 'op_end',
+ }
+
+ @workflow
+ def basic_workflow(graph, ctx, **_):
+ ctx.logger.info(wf_start)
+ graph.add_tasks(
+ api.task.OperationTask.node(
+ name=operation_name,
+ instance=node,
+ inputs=inputs
+ )
+ )
+ ctx.logger.debug(wf_end)
+
+ execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
+
+ op_start_log = ctx.model.log.list(filters=dict(msg=inputs['op_start']))
+ assert len(op_start_log) == 1
+ op_start_log = op_start_log[0]
+ assert op_start_log.level.lower() == 'info'
+
+ op_end_log = ctx.model.log.list(filters=dict(msg=inputs['op_end']))
+ assert len(op_end_log) == 1
+ op_end_log = op_end_log[0]
+ assert op_end_log.level.lower() == 'debug'
+
+ wf_start_log = ctx.model.log.list(filters=dict(msg=wf_start))
+ assert len(wf_start_log) == 1
+ wf_start_log = wf_start_log[0]
+ assert wf_start_log.level.lower() == 'info'
+
+ wf_end_log = ctx.model.log.list(filters=dict(msg=wf_end))
+ assert len(wf_end_log) == 1
+ wf_end_log = wf_end_log[0]
+ assert wf_end_log.level.lower() == 'debug'
+
+ assert (wf_start_log.created_at <
+ wf_end_log.created_at <
+ op_start_log.created_at <
+ op_end_log.created_at)
+
+
+@operation
+def logged_operation(ctx, **_):
+ ctx.logger.info(ctx.task.inputs['op_start'])
+ ctx.logger.debug(ctx.task.inputs['op_end'])
+
+
@operation
-def my_operation(ctx, **_):
+def basic_operation(ctx, **_):
global_test_holder[ctx.name] = ctx
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index 4278831..5323d01 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -12,8 +12,7 @@
# 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
@@ -23,19 +22,19 @@ from sqlalchemy import (
Column,
Text,
Integer,
- pool
+ pool,
+ MetaData
)
-from aria.storage import (
+from aria.storage.modeling import (
model,
- type as aria_type,
structure,
- modeling
+ type as aria_type
)
-class MockModel(model.aria_declarative_base, structure.ModelMixin): #pylint: disable=abstract-method
+class MockModel(structure.ModelMixin, model.aria_declarative_base): #pylint: disable=abstract-method
__tablename__ = 'mock_model'
model_dict = Column(aria_type.Dict)
model_list = Column(aria_type.List)
@@ -58,14 +57,8 @@ def release_sqlite_storage(storage):
:param storage:
:return:
"""
- mapis = storage.registered.values()
-
- if mapis:
- for session in set(mapi._session for mapi in mapis):
- session.rollback()
- session.close()
- for engine in set(mapi._engine for mapi in mapis):
- model.aria_declarative_base.metadata.drop_all(engine)
+ storage.all_api_kwargs['session'].close()
+ MetaData(bind=storage.all_api_kwargs['engine']).drop_all()
def init_inmemory_model_storage():
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/67b75425/tests/storage/test_instrumentation.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_instrumentation.py b/tests/storage/test_instrumentation.py
index 08d5ae0..7f0eb02 100644
--- a/tests/storage/test_instrumentation.py
+++ b/tests/storage/test_instrumentation.py
@@ -17,13 +17,15 @@ import pytest
from sqlalchemy import Column, Text, Integer, event
from aria.storage import (
- structure,
ModelStorage,
sql_mapi,
instrumentation,
exceptions,
+)
+from aria.storage.modeling import (
+ model,
type as aria_type,
- model
+ structure,
)
from ..storage import release_sqlite_storage, init_inmemory_model_storage
[3/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/template_elements.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/template_elements.py b/aria/storage/modeling/template_elements.py
new file mode 100644
index 0000000..4212b15
--- /dev/null
+++ b/aria/storage/modeling/template_elements.py
@@ -0,0 +1,1387 @@
+# 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 copy import deepcopy
+from types import FunctionType
+
+from sqlalchemy import (
+ Column,
+ Text,
+ Integer,
+ DateTime,
+ Boolean,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from aria.parser import validation
+from aria.utils import collections, formatting, console
+
+from . import (
+ utils,
+ instance_elements,
+ structure,
+ type as aria_type
+)
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+
+# region Element templates
+
+
+class ServiceTemplateBase(structure.ModelMixin):
+
+ __tablename__ = 'service_template'
+
+ __private_fields__ = ['substitution_template_fk']
+
+ description = Column(Text)
+ metadata = Column(Text)
+
+ # region orchestrator required columns
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ main_file_name = Column(Text)
+ plan = Column(aria_type.Dict, nullable=False)
+ updated_at = Column(DateTime)
+
+ # endregion
+
+ # region foreign keys
+ @declared_attr
+ def substitution_template_fk(cls):
+ return cls.foreign_key('substitution_template', nullable=True)
+
+ # endregion
+
+ # region one-to-one relationships
+ @declared_attr
+ def substitution_template(cls):
+ return cls.one_to_one_relationship('substitution_template')
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ @declared_attr
+ def outputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='outputs')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('description', self.description),
+ ('metadata', formatting.as_raw(self.metadata)),
+ ('node_templates', formatting.as_raw_list(self.node_templates)),
+ ('group_templates', formatting.as_raw_list(self.group_templates)),
+ ('policy_templates', formatting.as_raw_list(self.policy_templates)),
+ ('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))))
+
+ def instantiate(self, context, container):
+ service_instance = instance_elements.ServiceInstanceBase()
+ context.modeling.instance = service_instance
+
+ service_instance.description = deepcopy_with_locators(self.description)
+
+ if self.metadata is not None:
+ service_instance.metadata = self.metadata.instantiate(context, container)
+
+ for node_template in self.node_templates.itervalues():
+ for _ in range(node_template.default_instances):
+ node = node_template.instantiate(context, container)
+ service_instance.nodes[node.id] = node
+
+ utils.instantiate_dict(context, self, service_instance.groups, self.group_templates)
+ utils.instantiate_dict(context, self, service_instance.policies, self.policy_templates)
+ utils.instantiate_dict(context, self, service_instance.operations, self.operation_templates)
+
+ if self.substitution_template is not None:
+ service_instance.substitution = self.substitution_template.instantiate(context,
+ container)
+
+ utils.instantiate_dict(context, self, service_instance.inputs, self.inputs)
+ utils.instantiate_dict(context, self, service_instance.outputs, self.outputs)
+
+ for name, the_input in context.modeling.inputs.iteritems():
+ if name not in service_instance.inputs:
+ context.validation.report('input "%s" is not supported' % name)
+ else:
+ service_instance.inputs[name].value = the_input
+
+ return service_instance
+
+ def validate(self, context):
+ if self.metadata is not None:
+ self.metadata.validate(context)
+ utils.validate_dict_values(context, self.node_templates)
+ utils.validate_dict_values(context, self.group_templates)
+ utils.validate_dict_values(context, self.policy_templates)
+ if self.substitution_template is not None:
+ 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)
+
+ def coerce_values(self, context, container, report_issues):
+ if self.metadata is not None:
+ self.metadata.coerce_values(context, container, report_issues)
+ utils.coerce_dict_values(context, container, self.node_templates, report_issues)
+ utils.coerce_dict_values(context, container, self.group_templates, report_issues)
+ utils.coerce_dict_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.operation_templates, report_issues)
+
+ def dump(self, context):
+ if self.description is not None:
+ console.puts(context.style.meta(self.description))
+ if self.metadata is not None:
+ self.metadata.dump(context)
+ for node_template in self.node_templates.itervalues():
+ node_template.dump(context)
+ for group_template in self.group_templates.itervalues():
+ group_template.dump(context)
+ for policy_template in self.policy_templates.itervalues():
+ policy_template.dump(context)
+ if self.substitution_template is not None:
+ self.substitution_template.dump(context)
+ dump_parameters(context, self.inputs, 'Inputs')
+ dump_parameters(context, self.outputs, 'Outputs')
+ utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+
+
+class InterfaceTemplateBase(structure.ModelMixin):
+ __tablename__ = 'interface_template'
+
+ __private_fields__ = ['node_template_fk',
+ 'group_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def node_template_fk(cls):
+ return cls.foreign_key('node_template', nullable=True)
+
+ @declared_attr
+ def group_template_fk(cls):
+ return cls.foreign_key('group_template', nullable=True)
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+
+ # region many-to-one relationship
+ @declared_attr
+ def node_template(cls):
+ return cls.many_to_one_relationship('node_template')
+
+ @declared_attr
+ def group_template(cls):
+ return cls.many_to_one_relationship('group_template')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+ # 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.properties)), # pylint: disable=no-member
+ # TODO fix self.properties reference
+ ('operation_templates', formatting.as_raw_list(self.operation_templates))))
+
+ def instantiate(self, context, container):
+ interface = instance_elements.InterfaceBase(self.name, self.type_name)
+ interface.description = deepcopy_with_locators(self.description)
+ utils.instantiate_dict(context, container, interface.inputs, self.inputs)
+ utils.instantiate_dict(context, container, interface.operations, self.operation_templates)
+ return interface
+
+ def validate(self, context):
+ if self.type_name:
+ if context.modeling.interface_types.get_descendant(self.type_name) is None:
+ context.validation.report('interface "%s" has an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ console.puts('Interface type: %s' % context.style.type(self.type_name))
+ dump_parameters(context, self.inputs, 'Inputs')
+ utils.dump_dict_values(context, self.operation_templates, 'Operation templates')
+
+
+class OperationTemplateBase(structure.ModelMixin):
+ __tablename__ = 'operation_template'
+
+ __private_fields__ = ['service_template_fk',
+ 'interface_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls.foreign_key('service_template', nullable=True)
+
+ @declared_attr
+ def interface_template_fk(cls):
+ return cls.foreign_key('interface_template', nullable=True)
+
+ # endregion
+
+ description = Column(Text)
+ implementation = Column(Text)
+ dependencies = Column(aria_type.StrictList(item_cls=basestring))
+ executor = Column(Text)
+ max_retries = Column(Integer)
+ retry_interval = Column(Integer)
+
+ # region orchestrator required columns
+ plugin = Column(Text)
+ operation = Column(Boolean)
+
+ # region many-to-one relationships
+ @declared_attr
+ def service_template(cls):
+ return cls.many_to_one_relationship('service_template')
+
+ @declared_attr
+ def interface_template(cls):
+ return cls.many_to_one_relationship('interface_template')
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def inputs(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='inputs')
+
+ # 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 instantiate(self, context, container):
+ operation = instance_elements.OperationBase(self.name)
+ operation.description = deepcopy_with_locators(self.description)
+ operation.implementation = self.implementation
+ operation.dependencies = self.dependencies
+ operation.executor = self.executor
+ operation.max_retries = self.max_retries
+ operation.retry_interval = self.retry_interval
+ utils.instantiate_dict(context, container, operation.inputs, self.inputs)
+ return operation
+
+ 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:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ if self.implementation is not None:
+ console.puts('Implementation: %s' % context.style.literal(self.implementation))
+ if self.dependencies:
+ console.puts('Dependencies: %s' % ', '.join(
+ (str(context.style.literal(v)) for v in self.dependencies)))
+ if self.executor is not None:
+ console.puts('Executor: %s' % context.style.literal(self.executor))
+ if self.max_retries is not None:
+ console.puts('Max retries: %s' % context.style.literal(self.max_retries))
+ if self.retry_interval is not None:
+ console.puts('Retry interval: %s' % context.style.literal(self.retry_interval))
+ dump_parameters(context, self.inputs, 'Inputs')
+
+
+class ArtifactTemplateBase(structure.ModelMixin):
+ """
+ A file associated with a :class:`NodeTemplate`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`source_path`: Source path (CSAR or repository)
+ * :code:`target_path`: Path at destination machine
+ * :code:`repository_url`: Repository URL
+ * :code:`repository_credential`: Dict of string
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'artifact_template'
+
+ __private_fields__ = ['node_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def node_template_fk(cls):
+ return cls.foreign_key('node_template')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ source_path = Column(Text)
+ target_path = Column(Text)
+ repository_url = Column(Text)
+ repository_credential = Column(aria_type.StrictDict(basestring, basestring))
+
+ # region many-to-one relationship
+ @declared_attr
+ def node_template(cls):
+ return cls.many_to_one_relationship('node_template')
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # 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.iteritems()))))
+
+ def instantiate(self, context, container):
+ artifact = instance_elements.ArtifactBase(self.name, self.type_name, self.source_path)
+ artifact.description = deepcopy_with_locators(self.description)
+ artifact.target_path = self.target_path
+ artifact.repository_url = self.repository_url
+ artifact.repository_credential = self.repository_credential
+ utils.instantiate_dict(context, container, artifact.properties, self.properties)
+ return artifact
+
+ def validate(self, context):
+ if context.modeling.artifact_types.get_descendant(self.type_name) is None:
+ context.validation.report('artifact "%s" has an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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: %s' % context.style.type(self.type_name))
+ console.puts('Source path: %s' % context.style.literal(self.source_path))
+ if self.target_path is not None:
+ console.puts('Target path: %s' % context.style.literal(self.target_path))
+ if self.repository_url is not None:
+ console.puts('Repository URL: %s' % context.style.literal(self.repository_url))
+ if self.repository_credential:
+ console.puts('Repository credential: %s'
+ % context.style.literal(self.repository_credential))
+ dump_parameters(context, self.properties)
+
+
+class PolicyTemplateBase(structure.ModelMixin):
+ """
+ Policies can be applied to zero or more :class:`NodeTemplate` or :class:`GroupTemplate`
+ instances.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`target_node_template_names`: Must be represented in the :class:`ServiceModel`
+ * :code:`target_group_template_names`: Must be represented in the :class:`ServiceModel`
+ """
+ __tablename__ = 'policy_template'
+
+ __private_fields__ = ['service_templaet_fk',
+ 'group_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls.foreign_key('service_template')
+
+ @declared_attr
+ def group_template_fk(cls):
+ return cls.foreign_key('group_template')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ target_node_template_names = Column(aria_type.StrictList(basestring))
+ target_group_template_names = Column(aria_type.StrictList(basestring))
+
+ # region many-to-one relationship
+ @declared_attr
+ def service_template(cls):
+ return cls.many_to_one_relationship('service_template')
+
+ @declared_attr
+ def group_template(cls):
+ return cls.many_to_one_relationship('group_template')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('target_node_template_names', self.target_node_template_names),
+ ('target_group_template_names', self.target_group_template_names)))
+
+ def instantiate(self, context, *args, **kwargs):
+ policy = instance_elements.PolicyBase(self.name, self.type_name)
+ utils.instantiate_dict(context, self, policy.properties, self.properties)
+ for node_template_name in self.target_node_template_names:
+ policy.target_node_ids.extend(
+ context.modeling.instance.get_node_ids(node_template_name))
+ for group_template_name in self.target_group_template_names:
+ policy.target_group_ids.extend(
+ context.modeling.instance.get_group_ids(group_template_name))
+ return policy
+
+ def validate(self, context):
+ if context.modeling.policy_types.get_descendant(self.type_name) is None:
+ context.validation.report('policy template "%s" has an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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: %s' % context.style.node(self.name))
+ if self.description:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ dump_parameters(context, self.properties)
+ if self.target_node_template_names:
+ console.puts('Target node templates: %s' % ', '.join(
+ (str(context.style.node(v)) for v in self.target_node_template_names)))
+ if self.target_group_template_names:
+ console.puts('Target group templates: %s' % ', '.join(
+ (str(context.style.node(v)) for v in self.target_group_template_names)))
+
+
+class GroupPolicyTemplateBase(structure.ModelMixin):
+ """
+ Policies applied to groups.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`triggers`: Dict of :class:`GroupPolicyTrigger`
+ """
+
+ __tablename__ = 'group_policy_template'
+
+ __private_fields__ = ['group_template_fk']
+
+ # region foreign keys
+ @declared_attr
+ def group_template_fk(cls):
+ return cls.foreign_key('group_template')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('triggers', formatting.as_raw_list(self.triggers))))
+
+ def instantiate(self, context, container):
+ group_policy = instance_elements.GroupPolicyBase(self.name, self.type_name)
+ group_policy.description = deepcopy_with_locators(self.description)
+ utils.instantiate_dict(context, container, group_policy.properties, self.properties)
+ utils.instantiate_dict(context, container, group_policy.triggers, self.triggers)
+ return group_policy
+
+ def validate(self, context):
+ if context.modeling.policy_types.get_descendant(self.type_name) is None:
+ context.validation.report('group policy "%s" has an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.triggers)
+
+ 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.triggers, 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('Group policy type: %s' % context.style.type(self.type_name))
+ dump_parameters(context, self.properties)
+ utils.dump_dict_values(context, self.triggers, 'Triggers')
+
+
+class GroupPolicyTriggerTemplateBase(structure.ModelMixin):
+ """
+ Triggers for :class:`GroupPolicyTemplate`.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`implementation`: Implementation string (interpreted by the orchestrator)
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'group_policy_trigger_template'
+
+ __private_fields__ = ['group_policy_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def group_policy_template_fk(cls):
+ return cls.foreign_key('group_policy_template')
+
+ # endregion
+
+ description = Column(Text)
+ implementation = Column(Text)
+
+ # region many-to-one relationship
+ @declared_attr
+ def group_policy_template(cls):
+ return cls.many_to_one_relationship('group_policy_template')
+
+ # endregion
+
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('implementation', self.implementation),
+ ('properties', formatting.as_raw_dict(self.properties))))
+
+ def instantiate(self, context, container):
+ group_policy_trigger = instance_elements.GroupPolicyTriggerBase(self.name,
+ self.implementation)
+ group_policy_trigger.description = deepcopy_with_locators(self.description)
+ utils.instantiate_dict(context, container, group_policy_trigger.properties,
+ self.properties)
+ return group_policy_trigger
+
+ 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('Implementation: %s' % context.style.literal(self.implementation))
+ dump_parameters(context, self.properties)
+
+
+class MappingTemplateBase(structure.ModelMixin):
+ """
+ Used by :class:`SubstitutionTemplate` to map a capability or a requirement to a node.
+
+ Properties:
+
+ * :code:`mapped_name`: Exposed capability or requirement name
+ * :code:`node_template_name`: Must be represented in the :class:`ServiceModel`
+ * :code:`name`: Name of capability or requirement at the node template
+ """
+ __tablename__ = 'mapping_template'
+
+ mapped_name = Column(Text)
+ node_template_name = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('mapped_name', self.mapped_name),
+ ('node_template_name', self.node_template_name),
+ ('name', self.name)))
+
+ def instantiate(self, context, *args, **kwargs):
+ nodes = context.modeling.instance.find_nodes(self.node_template_name)
+ if len(nodes) == 0:
+ context.validation.report(
+ 'mapping "%s" refer to node template "%s" but there are no '
+ 'node instances' % (self.mapped_name,
+ self.node_template_name),
+ level=validation.Issue.BETWEEN_INSTANCES)
+ return None
+ return instance_elements.MappingBase(self.mapped_name, nodes[0].id, self.name)
+
+ def validate(self, context):
+ if self.node_template_name not in context.modeling.model.node_templates:
+ context.validation.report('mapping "%s" refers to an unknown node template: %s'
+ % (
+ self.mapped_name,
+ formatting.safe_repr(self.node_template_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ def dump(self, context):
+ console.puts('%s -> %s.%s' % (context.style.node(self.mapped_name),
+ context.style.node(self.node_template_name),
+ context.style.node(self.name)))
+
+
+class SubstitutionTemplateBase(structure.ModelMixin):
+ """
+ Used to substitute a single node for the entire deployment.
+
+ Properties:
+
+ * :code:`node_type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`capability_templates`: Dict of :class:`MappingTemplate`
+ * :code:`requirement_templates`: Dict of :class:`MappingTemplate`
+ """
+ __tablename__ = 'substitution_template'
+ node_type_name = Column(Text)
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def capability_templates(cls):
+ return cls.many_to_many_relationship('mapping_template',
+ table_prefix='capability_templates',
+ relationship_kwargs=dict(lazy='dynamic'))
+
+ @declared_attr
+ def requirement_templates(cls):
+ return cls.many_to_many_relationship('mapping_template',
+ table_prefix='requirement_templates',
+ relationship_kwargs=dict(lazy='dynamic'))
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('node_type_name', self.node_type_name),
+ ('capability_templates', formatting.as_raw_list(self.capability_templates)),
+ ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
+
+ def instantiate(self, context, container):
+ substitution = instance_elements.SubstitutionBase(self.node_type_name)
+ utils.instantiate_dict(context, container, substitution.capabilities,
+ self.capability_templates)
+ utils.instantiate_dict(context, container, substitution.requirements,
+ self.requirement_templates)
+ return substitution
+
+ def validate(self, context):
+ if context.modeling.node_types.get_descendant(self.node_type_name) is None:
+ context.validation.report('substitution template has an unknown type: %s'
+ % formatting.safe_repr(self.node_type_name),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.capability_templates)
+ utils.validate_dict_values(context, self.requirement_templates)
+
+ def coerce_values(self, context, container, report_issues):
+ utils.coerce_dict_values(context, self, self.capability_templates, report_issues)
+ utils.coerce_dict_values(context, self, self.requirement_templates, report_issues)
+
+ def dump(self, context):
+ console.puts('Substitution template:')
+ with context.style.indent:
+ console.puts('Node type: %s' % context.style.type(self.node_type_name))
+ utils.dump_dict_values(context, self.capability_templates,
+ 'Capability template mappings')
+ utils.dump_dict_values(context, self.requirement_templates,
+ 'Requirement template mappings')
+
+
+# endregion
+
+# region Node templates
+
+class NodeTemplateBase(structure.ModelMixin):
+ __tablename__ = 'node_template'
+
+ __private_fields__ = ['service_template_fk',
+ 'host_fk']
+
+ # region foreign_keys
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls.foreign_key('service_template')
+
+ @declared_attr
+ def host_fk(cls):
+ return cls.foreign_key('node_template', nullable=True)
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ default_instances = Column(Integer, default=1)
+ min_instances = Column(Integer, default=0)
+ max_instances = Column(Integer, default=None)
+ target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
+
+ # region orchestrator required columns
+
+ plugins = Column(aria_type.List)
+ type_hierarchy = Column(aria_type.List)
+
+ @declared_attr
+ def host(cls):
+ return cls.relationship_to_self('host_fk')
+
+ @declared_attr
+ def service_template_name(cls):
+ return association_proxy('service_template', cls.name_column_name())
+
+ # endregion
+
+ # region many-to-one relationship
+ @declared_attr
+ def service_template(cls):
+ return cls.many_to_one_relationship('service_template')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ def is_target_node_valid(self, target_node_template):
+ if self.target_node_template_constraints:
+ for node_type_constraint in self.target_node_template_constraints:
+ if not node_type_constraint(target_node_template, self):
+ return False
+ return True
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('default_instances', self.default_instances),
+ ('min_instances', self.min_instances),
+ ('max_instances', self.max_instances),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('interface_templates', formatting.as_raw_list(self.interface_templates)),
+ ('artifact_templates', formatting.as_raw_list(self.artifact_templates)),
+ ('capability_templates', formatting.as_raw_list(self.capability_templates)),
+ ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
+
+ def instantiate(self, context, *args, **kwargs):
+ node = instance_elements.NodeBase(context, self.type_name, self.name)
+ utils.instantiate_dict(context, node, node.properties, self.properties)
+ utils.instantiate_dict(context, node, node.interfaces, self.interface_templates)
+ utils.instantiate_dict(context, node, node.artifacts, self.artifact_templates)
+ utils.instantiate_dict(context, node, node.capabilities, self.capability_templates)
+ return node
+
+ def validate(self, context):
+ if context.modeling.node_types.get_descendant(self.type_name) is None:
+ context.validation.report('node template "%s" has an unknown type: %s'
+ % (self.name,
+ formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.interface_templates)
+ utils.validate_dict_values(context, self.artifact_templates)
+ 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: %s' % context.style.node(self.name))
+ if self.description:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ console.puts('Instances: %d (%d%s)'
+ % (self.default_instances,
+ self.min_instances,
+ (' to %d' % self.max_instances
+ if self.max_instances is not None
+ else ' or more')))
+ dump_parameters(context, self.properties)
+ utils.dump_interfaces(context, self.interface_templates)
+ utils.dump_dict_values(context, self.artifact_templates, 'Artifact tempaltes')
+ utils.dump_dict_values(context, self.capability_templates, 'Capability templates')
+ utils.dump_list_values(context, self.requirement_templates, 'Requirement templates')
+
+
+class GroupTemplateBase(structure.ModelMixin):
+ """
+ A template for creating zero or more :class:`Group` instances.
+
+ Groups are logical containers for zero or more nodes that allow applying zero or more
+ :class:`GroupPolicy` instances to the nodes together.
+
+ Properties:
+
+ * :code:`name`: Name (will be used as a prefix for group IDs)
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ * :code:`interface_templates`: Dict of :class:`InterfaceTemplate`
+ * :code:`policy_templates`: Dict of :class:`GroupPolicyTemplate`
+ * :code:`member_node_template_names`: Must be represented in the :class:`ServiceModel`
+ * :code:`member_group_template_names`: Must be represented in the :class:`ServiceModel`
+ """
+ __tablename__ = 'group_template'
+
+ __private_fields__ = ['service_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls.foreign_key('service_template')
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ member_node_template_names = Column(aria_type.StrictList(basestring))
+ member_group_template_names = Column(aria_type.StrictList(basestring))
+
+ # region many-to-one relationship
+ @declared_attr
+ def service_template(cls):
+ return cls.many_to_one_relationship('service_template')
+
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('properties', formatting.as_raw_dict(self.properties)),
+ ('interface_templates', formatting.as_raw_list(self.interface_templates)),
+ ('policy_templates', formatting.as_raw_list(self.policy_templates)),
+ ('member_node_template_names', self.member_node_template_names),
+ ('member_group_template_names', self.member_group_template_names1)))
+
+ def instantiate(self, context, *args, **kwargs):
+ group = instance_elements.GroupBase(context, self.type_name, self.name)
+ utils.instantiate_dict(context, self, group.properties, self.properties)
+ utils.instantiate_dict(context, self, group.interfaces, self.interface_templates)
+ utils.instantiate_dict(context, self, group.policies, self.policy_templates)
+ for member_node_template_name in self.member_node_template_names:
+ group.member_node_ids += \
+ context.modeling.instance.get_node_ids(member_node_template_name)
+ for member_group_template_name in self.member_group_template_names:
+ group.member_group_ids += \
+ context.modeling.instance.get_group_ids(member_group_template_name)
+ return group
+
+ def validate(self, context):
+ if context.modeling.group_types.get_descendant(self.type_name) is None:
+ context.validation.report('group template "%s" has an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+
+ utils.validate_dict_values(context, self.properties)
+ utils.validate_dict_values(context, self.interface_templates)
+ utils.validate_dict_values(context, self.policy_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.policy_templates, report_issues)
+
+ def dump(self, context):
+ console.puts('Group template: %s' % context.style.node(self.name))
+ if self.description:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ if self.type_name:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ dump_parameters(context, self.properties)
+ utils.dump_interfaces(context, self.interface_templates)
+ utils.dump_dict_values(context, self.policy_templates, 'Policy templates')
+ if self.member_node_template_names:
+ console.puts('Member node templates: %s' % ', '.join(
+ (str(context.style.node(v)) for v in self.member_node_template_names)))
+
+
+# endregion
+
+# region Relationship templates
+
+class RequirementTemplateBase(structure.ModelMixin):
+ """
+ A requirement for a :class:`NodeTemplate`. During instantiation will be matched with a
+ capability of another
+ node.
+
+ Requirements may optionally contain a :class:`RelationshipTemplate` that will be created between
+ the nodes.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`target_node_type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`target_node_template_name`: Must be represented in the :class:`ServiceModel`
+ * :code:`target_node_template_constraints`: List of :class:`FunctionType`
+ * :code:`target_capability_type_name`: Type of capability in target node
+ * :code:`target_capability_name`: Name of capability in target node
+ * :code:`relationship_template`: :class:`RelationshipTemplate`
+ """
+ __tablename__ = 'requirement_template'
+
+ __private_fields__ = ['node_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def node_template_fk(cls):
+ return cls.foreign_key('node_template', nullable=True)
+
+ # endregion
+
+
+ target_node_type_name = Column(Text)
+ target_node_template_name = Column(Text)
+ target_node_template_constraints = Column(aria_type.StrictList(FunctionType))
+ target_capability_type_name = Column(Text)
+ target_capability_name = Column(Text)
+ # CHECK: ???
+ relationship_template = Column(Text) # optional
+
+ # region many-to-one relationship
+ @declared_attr
+ def node_template(cls):
+ return cls.many_to_one_relationship('node_template')
+ # endregion
+
+ def instantiate(self, context, container):
+ raise NotImplementedError
+
+ def find_target(self, context, source_node_template):
+ # We might already have a specific node template, so we'll just verify it
+ if self.target_node_template_name is not None:
+ target_node_template = \
+ context.modeling.model.node_templates.get(self.target_node_template_name)
+
+ if not source_node_template.is_target_node_valid(target_node_template):
+ context.validation.report('requirement "%s" of node template "%s" is for node '
+ 'template "%s" but it does not match constraints'
+ % (self.name,
+ self.target_node_template_name,
+ source_node_template.name),
+ level=validation.Issue.BETWEEN_TYPES)
+ return None, None
+
+ if self.target_capability_type_name is not None \
+ or self.target_capability_name is not None:
+ target_node_capability = self.find_target_capability(context,
+ source_node_template,
+ target_node_template)
+ if target_node_capability is None:
+ return None, None
+ else:
+ target_node_capability = None
+
+ return target_node_template, target_node_capability
+
+ # Find first node that matches the type
+ elif self.target_node_type_name is not None:
+ for target_node_template in context.modeling.model.node_templates.itervalues():
+ if not context.modeling.node_types.is_descendant(self.target_node_type_name,
+ target_node_template.type_name):
+ continue
+
+ if not source_node_template.is_target_node_valid(target_node_template):
+ continue
+
+ target_node_capability = self.find_target_capability(context,
+ source_node_template,
+ target_node_template)
+ if target_node_capability is None:
+ continue
+
+ return target_node_template, target_node_capability
+
+ return None, None
+
+ def find_target_capability(self, context, source_node_template, target_node_template):
+ for capability_template in target_node_template.capability_templates.itervalues():
+ if capability_template.satisfies_requirement(context,
+ source_node_template,
+ self,
+ target_node_template):
+ return capability_template
+ return None
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('target_node_type_name', self.target_node_type_name),
+ ('target_node_template_name', self.target_node_template_name),
+ ('target_capability_type_name', self.target_capability_type_name),
+ ('target_capability_name', self.target_capability_name),
+ ('relationship_template', formatting.as_raw(self.relationship_template))))
+
+ def validate(self, context):
+ node_types = context.modeling.node_types
+ capability_types = context.modeling.capability_types
+ if self.target_node_type_name \
+ and node_types.get_descendant(self.target_node_type_name) is None:
+ context.validation.report('requirement "%s" refers to an unknown node type: %s'
+ % (self.name,
+ formatting.safe_repr(self.target_node_type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+ if self.target_capability_type_name and \
+ capability_types.get_descendant(self.target_capability_type_name is None):
+ context.validation.report('requirement "%s" refers to an unknown capability type: %s'
+ % (self.name,
+ formatting.safe_repr(self.target_capability_type_name)),
+ level=validation.Issue.BETWEEN_TYPES)
+ 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))
+ else:
+ console.puts('Requirement:')
+ with context.style.indent:
+ if self.target_node_type_name is not None:
+ console.puts('Target node type: %s'
+ % context.style.type(self.target_node_type_name))
+ elif self.target_node_template_name is not None:
+ console.puts('Target node template: %s'
+ % context.style.node(self.target_node_template_name))
+ if self.target_capability_type_name is not None:
+ console.puts('Target capability type: %s'
+ % context.style.type(self.target_capability_type_name))
+ elif self.target_capability_name is not None:
+ console.puts('Target capability name: %s'
+ % context.style.node(self.target_capability_name))
+ if self.target_node_template_constraints:
+ console.puts('Target node template constraints:')
+ with context.style.indent:
+ for constraint in self.target_node_template_constraints:
+ console.puts(context.style.literal(constraint))
+ if self.relationship_template:
+ console.puts('Relationship:')
+ with context.style.indent:
+ self.relationship_template.dump(context)
+
+
+class CapabilityTemplateBase(structure.ModelMixin):
+ """
+ A capability of a :class:`NodeTemplate`. Nodes expose zero or more capabilities that can be
+ matched with :class:`Requirement` instances of other nodes.
+
+ Properties:
+
+ * :code:`name`: Name
+ * :code:`description`: Description
+ * :code:`type_name`: Must be represented in the :class:`ModelingContext`
+ * :code:`min_occurrences`: Minimum number of requirement matches required
+ * :code:`max_occurrences`: Maximum number of requirement matches allowed
+ * :code:`valid_source_node_type_names`: Must be represented in the :class:`ModelingContext`
+ * :code:`properties`: Dict of :class:`Parameter`
+ """
+ __tablename__ = 'capability_template'
+
+ __private_fields__ = ['node_template_fk']
+
+ # region foreign keys
+
+ @declared_attr
+ def node_template_fk(cls):
+ return cls.foreign_key('node_template', nullable=True)
+
+ # endregion
+
+ description = Column(Text)
+ type_name = Column(Text)
+ min_occurrences = Column(Integer, default=None) # optional
+ max_occurrences = Column(Integer, default=None) # optional
+ # CHECK: type?
+ valid_source_node_type_names = Column(Text)
+
+ # region many-to-one relationship
+ @declared_attr
+ def node_template(cls):
+ return cls.many_to_one_relationship('node_template')
+ # endregion
+
+ # region many-to-many relationships
+
+ @declared_attr
+ def properties(cls):
+ return cls.many_to_many_relationship('parameter', table_prefix='properties')
+
+ # endregion
+
+ def satisfies_requirement(self,
+ context,
+ source_node_template,
+ requirement,
+ target_node_template):
+ # Do we match the required capability type?
+ capability_types = context.modeling.capability_types
+ if not capability_types.is_descendant(requirement.target_capability_type_name,
+ self.type_name):
+ return False
+
+ # Are we in valid_source_node_type_names?
+ if self.valid_source_node_type_names:
+ for valid_source_node_type_name in self.valid_source_node_type_names:
+ if not context.modeling.node_types.is_descendant(valid_source_node_type_name,
+ source_node_template.type_name):
+ return False
+
+ # Apply requirement constraints
+ if requirement.target_node_template_constraints:
+ for node_type_constraint in requirement.target_node_template_constraints:
+ if not node_type_constraint(target_node_template, source_node_template):
+ return False
+
+ return True
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('type_name', self.type_name),
+ ('min_occurrences', self.min_occurrences),
+ ('max_occurrences', self.max_occurrences),
+ ('valid_source_node_type_names', self.valid_source_node_type_names),
+ ('properties', formatting.as_raw_dict(self.properties))))
+
+ def instantiate(self, context, container):
+ capability = instance_elements.CapabilityBase(self.name, self.type_name)
+ capability.min_occurrences = self.min_occurrences
+ capability.max_occurrences = self.max_occurrences
+ utils.instantiate_dict(context, container, capability.properties, self.properties)
+ return capability
+
+ def validate(self, context):
+ if context.modeling.capability_types.get_descendant(self.type_name) is None:
+ context.validation.report('capability "%s" refers to an unknown type: %s'
+ % (self.name, formatting.safe_repr(self.type)), # pylint: disable=no-member
+ # TODO fix self.type reference
+ level=validation.Issue.BETWEEN_TYPES)
+
+ 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:
+ console.puts(context.style.meta(self.description))
+ with context.style.indent:
+ console.puts('Type: %s' % context.style.type(self.type_name))
+ console.puts(
+ 'Occurrences: %d%s'
+ % (self.min_occurrences or 0, (' to %d' % self.max_occurrences)
+ if self.max_occurrences is not None else ' or more'))
+ if self.valid_source_node_type_names:
+ console.puts('Valid source node types: %s'
+ % ', '.join((str(context.style.type(v))
+ for v in self.valid_source_node_type_names)))
+ dump_parameters(context, self.properties)
+
+# endregion
+
+
+def dump_parameters(context, parameters, name='Properties'):
+ if not parameters:
+ return
+ console.puts('%s:' % name)
+ with context.style.indent:
+ for parameter_name, parameter in parameters.items():
+ if parameter.type_name is not None:
+ console.puts('%s = %s (%s)' % (context.style.property(parameter_name),
+ context.style.literal(parameter.value),
+ context.style.type(parameter.type_name)))
+ else:
+ console.puts('%s = %s' % (context.style.property(parameter_name),
+ context.style.literal(parameter.value)))
+ if parameter.description:
+ console.puts(context.style.meta(parameter.description))
+
+
+# TODO (left for tal): Move following two methods to some place parser specific
+def deepcopy_with_locators(value):
+ """
+ Like :code:`deepcopy`, but also copies over locators.
+ """
+
+ res = deepcopy(value)
+ copy_locators(res, value)
+ return res
+
+
+def copy_locators(target, source):
+ """
+ Copies over :code:`_locator` for all elements, recursively.
+
+ Assumes that target and source have exactly the same list/dict structure.
+ """
+
+ locator = getattr(source, '_locator', None)
+ if locator is not None:
+ try:
+ setattr(target, '_locator', locator)
+ except AttributeError:
+ pass
+
+ if isinstance(target, list) and isinstance(source, list):
+ for i, _ in enumerate(target):
+ copy_locators(target[i], source[i])
+ elif isinstance(target, dict) and isinstance(source, dict):
+ for k, v in target.items():
+ copy_locators(v, source[k])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/type.py b/aria/storage/modeling/type.py
new file mode 100644
index 0000000..9e3de3d
--- /dev/null
+++ b/aria/storage/modeling/type.py
@@ -0,0 +1,302 @@
+# 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 json
+from collections import namedtuple
+
+from sqlalchemy import (
+ TypeDecorator,
+ VARCHAR,
+ event
+)
+from sqlalchemy.ext import mutable
+
+from .. import exceptions
+
+
+class _MutableType(TypeDecorator):
+ """
+ Dict representation of type.
+ """
+ @property
+ def python_type(self):
+ raise NotImplementedError
+
+ def process_literal_param(self, value, dialect):
+ pass
+
+ impl = VARCHAR
+
+ def process_bind_param(self, value, dialect):
+ if value is not None:
+ value = json.dumps(value)
+ return value
+
+ def process_result_value(self, value, dialect):
+ if value is not None:
+ value = json.loads(value)
+ return value
+
+
+class Dict(_MutableType):
+ @property
+ def python_type(self):
+ return dict
+
+
+class List(_MutableType):
+ @property
+ def python_type(self):
+ return list
+
+
+class _StrictDictMixin(object):
+
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+ try:
+ if not isinstance(value, cls):
+ if isinstance(value, dict):
+ for k, v in value.items():
+ cls._assert_strict_key(k)
+ cls._assert_strict_value(v)
+ return cls(value)
+ return mutable.MutableDict.coerce(key, value)
+ else:
+ return value
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+ def __setitem__(self, key, value):
+ self._assert_strict_key(key)
+ self._assert_strict_value(value)
+ super(_StrictDictMixin, self).__setitem__(key, value)
+
+ def setdefault(self, key, value):
+ self._assert_strict_key(key)
+ self._assert_strict_value(value)
+ super(_StrictDictMixin, self).setdefault(key, value)
+
+ def update(self, *args, **kwargs):
+ for k, v in kwargs.items():
+ self._assert_strict_key(k)
+ self._assert_strict_value(v)
+ super(_StrictDictMixin, self).update(*args, **kwargs)
+
+ @classmethod
+ def _assert_strict_key(cls, key):
+ if cls._key_cls is not None and not isinstance(key, cls._key_cls):
+ raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+ cls._key_cls, type(key)
+ ))
+
+ @classmethod
+ def _assert_strict_value(cls, value):
+ if cls._value_cls is not None and not isinstance(value, cls._value_cls):
+ raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
+ cls._value_cls, type(value)
+ ))
+
+
+class _MutableDict(mutable.MutableDict):
+ """
+ Enables tracking for dict values.
+ """
+
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+ try:
+ return mutable.MutableDict.coerce(key, value)
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+
+class _StrictListMixin(object):
+
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+ try:
+ if not isinstance(value, cls):
+ if isinstance(value, list):
+ for item in value:
+ cls._assert_item(item)
+ return cls(value)
+ return mutable.MutableList.coerce(key, value)
+ else:
+ return value
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+ def __setitem__(self, index, value):
+ """Detect list set events and emit change events."""
+ self._assert_item(value)
+ super(_StrictListMixin, self).__setitem__(index, value)
+
+ def append(self, item):
+ self._assert_item(item)
+ super(_StrictListMixin, self).append(item)
+
+ def extend(self, item):
+ self._assert_item(item)
+ super(_StrictListMixin, self).extend(item)
+
+ def insert(self, index, item):
+ self._assert_item(item)
+ super(_StrictListMixin, self).insert(index, item)
+
+ @classmethod
+ def _assert_item(cls, item):
+ if cls._item_cls is not None and not isinstance(item, cls._item_cls):
+ raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+ cls._item_cls, type(item)
+ ))
+
+
+class _MutableList(mutable.MutableList):
+
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+ try:
+ return mutable.MutableList.coerce(key, value)
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+_StrictDictID = namedtuple('_StrictDictID', 'key_cls, value_cls')
+_StrictValue = namedtuple('_StrictValue', 'type_cls, listener_cls')
+
+
+class _StrictDict(object):
+ """
+ This entire class functions as a factory for strict dicts and their listeners.
+ No type class, and no listener type class is created more than once. If a relevant type class
+ exists it is returned.
+ """
+ _strict_map = {}
+
+ def __call__(self, key_cls=None, value_cls=None):
+ strict_dict_map_key = _StrictDictID(key_cls=key_cls, value_cls=value_cls)
+ if strict_dict_map_key not in self._strict_map:
+ key_cls_name = getattr(key_cls, '__name__', str(key_cls))
+ value_cls_name = getattr(value_cls, '__name__', str(value_cls))
+ # Creating the type class itself. this class would be returned (used by the sqlalchemy
+ # Column).
+ strict_dict_cls = type(
+ 'StrictDict_{0}_{1}'.format(key_cls_name, value_cls_name),
+ (Dict, ),
+ {}
+ )
+ # Creating the type listening class.
+ # The new class inherits from both the _MutableDict class and the _StrictDictMixin,
+ # while setting the necessary _key_cls and _value_cls as class attributes.
+ listener_cls = type(
+ 'StrictMutableDict_{0}_{1}'.format(key_cls_name, value_cls_name),
+ (_StrictDictMixin, _MutableDict),
+ {'_key_cls': key_cls, '_value_cls': value_cls}
+ )
+ self._strict_map[strict_dict_map_key] = _StrictValue(type_cls=strict_dict_cls,
+ listener_cls=listener_cls)
+
+ return self._strict_map[strict_dict_map_key].type_cls
+
+StrictDict = _StrictDict()
+
+
+class _StrictList(object):
+ """
+ This entire class functions as a factory for strict lists and their listeners.
+ No type class, and no listener type class is created more than once. If a relevant type class
+ exists it is returned.
+ """
+ _strict_map = {}
+
+ def __call__(self, item_cls=None):
+
+ if item_cls not in self._strict_map:
+ item_cls_name = getattr(item_cls, '__name__', str(item_cls))
+ # Creating the type class itself. this class would be returned (used by the sqlalchemy
+ # Column).
+ strict_list_cls = type(
+ 'StrictList_{0}'.format(item_cls_name),
+ (List, ),
+ {}
+ )
+ # Creating the type listening class.
+ # The new class inherits from both the _MutableList class and the _StrictListMixin,
+ # while setting the necessary _item_cls as class attribute.
+ listener_cls = type(
+ 'StrictMutableList_{0}'.format(item_cls_name),
+ (_StrictListMixin, _MutableList),
+ {'_item_cls': item_cls}
+ )
+ self._strict_map[item_cls] = _StrictValue(type_cls=strict_list_cls,
+ listener_cls=listener_cls)
+
+ return self._strict_map[item_cls].type_cls
+
+StrictList = _StrictList()
+
+
+def _mutable_association_listener(mapper, cls):
+ strict_dict_type_to_listener = \
+ dict((v.type_cls, v.listener_cls) for v in _StrictDict._strict_map.values())
+
+ strict_list_type_to_listener = \
+ dict((v.type_cls, v.listener_cls) for v in _StrictList._strict_map.values())
+
+ for prop in mapper.column_attrs:
+ column_type = prop.columns[0].type
+ # Dict Listeners
+ if type(column_type) in strict_dict_type_to_listener: # pylint: disable=unidiomatic-typecheck
+ strict_dict_type_to_listener[type(column_type)].associate_with_attribute(
+ getattr(cls, prop.key))
+ elif isinstance(column_type, Dict):
+ _MutableDict.associate_with_attribute(getattr(cls, prop.key))
+
+ # List Listeners
+ if type(column_type) in strict_list_type_to_listener: # pylint: disable=unidiomatic-typecheck
+ strict_list_type_to_listener[type(column_type)].associate_with_attribute(
+ getattr(cls, prop.key))
+ elif isinstance(column_type, List):
+ _MutableList.associate_with_attribute(getattr(cls, prop.key))
+_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
+
+
+def _register_mutable_association_listener():
+ event.listen(*_LISTENER_ARGS)
+
+
+def remove_mutable_association_listener():
+ """
+ Remove the event listener that associates ``Dict`` and ``List`` column types with
+ ``MutableDict`` and ``MutableList``, respectively.
+
+ This call must happen before any model instance is instantiated.
+ This is because once it does, that would trigger the listener we are trying to remove.
+ Once it is triggered, many other listeners will then be registered.
+ At that point, it is too late.
+
+ The reason this function exists is that the association listener, interferes with ARIA change
+ tracking instrumentation, so a way to disable it is required.
+
+ Note that the event listener this call removes is registered by default.
+ """
+ if event.contains(*_LISTENER_ARGS):
+ event.remove(*_LISTENER_ARGS)
+
+_register_mutable_association_listener()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/storage/modeling/utils.py b/aria/storage/modeling/utils.py
new file mode 100644
index 0000000..75e34f5
--- /dev/null
+++ b/aria/storage/modeling/utils.py
@@ -0,0 +1,139 @@
+# 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 random import randrange
+
+from shortuuid import ShortUUID
+
+from ...utils.console import puts
+
+
+# UUID = ShortUUID() # default alphabet is base57, which is alphanumeric without visually ambiguous
+# characters; ID length is 22
+UUID = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123456789') # alphanumeric; ID length is 25
+
+
+def generate_id_string(length=None):
+ """
+ A random string with a strong guarantee of universal uniqueness (uses UUID).
+
+ The default length is 25 characters.
+ """
+
+ the_id = UUID.uuid()
+ if length is not None:
+ the_id = the_id[:length]
+ return the_id
+
+
+def generate_hex_string():
+ """
+ A random string of 5 hex digits with no guarantee of universal uniqueness.
+ """
+
+ return '%05x' % randrange(16 ** 5)
+
+
+def validate_dict_values(context, the_dict):
+ if not the_dict:
+ return
+ validate_list_values(context, the_dict.itervalues())
+
+
+def validate_list_values(context, the_list):
+ if not the_list:
+ return
+ for value in 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
+ for name, value in from_dict.iteritems():
+ value = value.instantiate(context, container)
+ if value is not None:
+ the_dict[name] = value
+
+
+def dump_list_values(context, the_list, name):
+ if not the_list:
+ return
+ puts('%s:' % name)
+ with context.style.indent:
+ for value in the_list:
+ value.dump(context)
+
+
+def dump_dict_values(context, the_dict, name):
+ if not the_dict:
+ return
+ dump_list_values(context, the_dict.itervalues(), name)
+
+
+def dump_parameters(context, parameters, name='Properties'):
+ if not parameters:
+ return
+ puts('%s:' % name)
+ with context.style.indent:
+ for parameter_name, parameter in parameters.iteritems():
+ if parameter.type_name is not None:
+ puts('%s = %s (%s)' % (context.style.property(parameter_name),
+ context.style.literal(parameter.value),
+ context.style.type(parameter.type_name)))
+ else:
+ puts('%s = %s' % (context.style.property(parameter_name),
+ context.style.literal(parameter.value)))
+ if parameter.description:
+ puts(context.style.meta(parameter.description))
+
+
+def dump_interfaces(context, interfaces, name='Interfaces'):
+ if not interfaces:
+ return
+ puts('%s:' % name)
+ with context.style.indent:
+ for interface in interfaces.itervalues():
+ interface.dump(context)
+
+
+def pluralize(noun):
+ if noun.endswith('s'):
+ return '{0}es'.format(noun)
+ elif noun.endswith('y'):
+ return '{0}ies'.format(noun[:-1])
+ else:
+ return '{0}s'.format(noun)
+
+
+class classproperty(object): # pylint: disable=invalid-name
+ def __init__(self, f):
+ self._func = f
+
+ def __get__(self, instance, owner):
+ return self._func(owner)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/sql_mapi.py
----------------------------------------------------------------------
diff --git a/aria/storage/sql_mapi.py b/aria/storage/sql_mapi.py
index 9dae08a..b80ac8e 100644
--- a/aria/storage/sql_mapi.py
+++ b/aria/storage/sql_mapi.py
@@ -142,8 +142,12 @@ class SQLAlchemyModelAPI(api.ModelAPI):
def _establish_connection(self):
pass
- def create(self, checkfirst=True, **kwargs):
+ def create(self, checkfirst=True, create_all=True, **kwargs):
self.model_cls.__table__.create(self._engine, checkfirst=checkfirst)
+ if create_all:
+ # In order to create any models created dynamically (e.g. many-to-many helper tables are
+ # created at runtime).
+ self.model_cls.metadata.create_all(bind=self._engine, checkfirst=checkfirst)
def drop(self):
"""
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/structure.py b/aria/storage/structure.py
deleted file mode 100644
index fa592ac..0000000
--- a/aria/storage/structure.py
+++ /dev/null
@@ -1,190 +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:
- * Field - represents a single field.
- * IterField - represents an iterable field.
- * PointerField - represents a single pointer field.
- * IterPointerField - represents an iterable pointers field.
- * Model - abstract model implementation.
-"""
-
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.ext import associationproxy
-from sqlalchemy import (
- Column,
- ForeignKey,
- Integer,
- Text
-)
-
-
-class ModelMixin(object):
-
- @classmethod
- def id_column_name(cls):
- raise NotImplementedError
-
- @classmethod
- def name_column_name(cls):
- raise NotImplementedError
-
- @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 in (getattr(table_cls, '__name__', None),
- getattr(table_cls, '__tablename__', None)):
- return table_cls
-
- @classmethod
- def foreign_key(cls, table, nullable=False):
- """Return a ForeignKey object with the relevant
-
- :param table: Unique id column in the parent table
- :param nullable: Should the column be allowed to remain empty
- """
- table_cls = cls._get_cls_by_tablename(table.__tablename__)
- foreign_key_str = '{tablename}.{unique_id}'.format(tablename=table_cls.__tablename__,
- unique_id=table_cls.id_column_name())
- column = Column(ForeignKey(foreign_key_str, ondelete='CASCADE'),
- nullable=nullable)
- column.__remote_table_name = table_cls.__name__
- return column
-
- @classmethod
- def one_to_many_relationship(cls,
- foreign_key_column,
- backreference=None,
- backref_kwargs=None,
- **kwargs):
- """Return a one-to-many SQL relationship object
- Meant to be used from inside the *child* object
-
- :param parent_class: Class of the parent table
- :param cls: Class of the child table
- :param foreign_key_column: 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)
- """
- backref_kwargs = backref_kwargs or {}
- parent_table = cls._get_cls_by_tablename(
- getattr(cls, foreign_key_column).__remote_table_name)
- primaryjoin_str = '{parent_class_name}.{parent_unique_id} == ' \
- '{child_class.__name__}.{foreign_key_column}'\
- .format(
- parent_class_name=parent_table.__name__,
- parent_unique_id=parent_table.id_column_name(),
- child_class=cls,
- foreign_key_column=foreign_key_column
- )
- return relationship(
- parent_table.__name__,
- primaryjoin=primaryjoin_str,
- foreign_keys=[getattr(cls, foreign_key_column)],
- # The following line make sure that when the *parent* is
- # deleted, all its connected children are deleted as well
- backref=backref(backreference or cls.__tablename__, cascade='all', **backref_kwargs),
- **kwargs
- )
-
- @classmethod
- def relationship_to_self(cls, local_column):
-
- remote_side_str = '{cls.__name__}.{remote_column}'.format(
- cls=cls,
- remote_column=cls.id_column_name()
- )
- primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format(
- remote_side_str=remote_side_str,
- cls=cls,
- local_column=local_column)
- return relationship(cls.__name__,
- primaryjoin=primaryjoin_str,
- remote_side=remote_side_str,
- post_update=True)
-
- 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 _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', []))
-
- def __repr__(self):
- return '<{__class__.__name__} id=`{id}`>'.format(
- __class__=self.__class__,
- id=getattr(self, self.name_column_name()))
-
-
-class ModelIDMixin(object):
- id = Column(Integer, primary_key=True, autoincrement=True)
- name = Column(Text, nullable=True, index=True)
-
- @classmethod
- def id_column_name(cls):
- return 'id'
-
- @classmethod
- def name_column_name(cls):
- return 'name'
[2/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model
Posted by mx...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/type.py b/aria/storage/type.py
deleted file mode 100644
index ac695b1..0000000
--- a/aria/storage/type.py
+++ /dev/null
@@ -1,299 +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 json
-from collections import namedtuple
-
-from sqlalchemy import (
- TypeDecorator,
- VARCHAR,
- event
-)
-from sqlalchemy.ext import mutable
-
-from . import exceptions
-
-
-class _MutableType(TypeDecorator):
- """
- Dict representation of type.
- """
- @property
- def python_type(self):
- raise NotImplementedError
-
- def process_literal_param(self, value, dialect):
- pass
-
- impl = VARCHAR
-
- def process_bind_param(self, value, dialect):
- if value is not None:
- value = json.dumps(value)
- return value
-
- def process_result_value(self, value, dialect):
- if value is not None:
- value = json.loads(value)
- return value
-
-
-class Dict(_MutableType):
- @property
- def python_type(self):
- return dict
-
-
-class List(_MutableType):
- @property
- def python_type(self):
- return list
-
-
-class _StrictDictMixin(object):
-
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
- try:
- if not isinstance(value, cls):
- if isinstance(value, dict):
- for k, v in value.items():
- cls._assert_strict_key(k)
- cls._assert_strict_value(v)
- return cls(value)
- return mutable.MutableDict.coerce(key, value)
- else:
- return value
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
- def __setitem__(self, key, value):
- self._assert_strict_key(key)
- self._assert_strict_value(value)
- super(_StrictDictMixin, self).__setitem__(key, value)
-
- def setdefault(self, key, value):
- self._assert_strict_key(key)
- self._assert_strict_value(value)
- super(_StrictDictMixin, self).setdefault(key, value)
-
- def update(self, *args, **kwargs):
- for k, v in kwargs.items():
- self._assert_strict_key(k)
- self._assert_strict_value(v)
- super(_StrictDictMixin, self).update(*args, **kwargs)
-
- @classmethod
- def _assert_strict_key(cls, key):
- if cls._key_cls is not None and not isinstance(key, cls._key_cls):
- raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
- cls._key_cls, type(key)
- ))
-
- @classmethod
- def _assert_strict_value(cls, value):
- if cls._value_cls is not None and not isinstance(value, cls._value_cls):
- raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
- cls._value_cls, type(value)
- ))
-
-
-class _MutableDict(mutable.MutableDict):
- """
- Enables tracking for dict values.
- """
-
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
- try:
- return mutable.MutableDict.coerce(key, value)
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-
-class _StrictListMixin(object):
-
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
- try:
- if not isinstance(value, cls):
- if isinstance(value, list):
- for item in value:
- cls._assert_item(item)
- return cls(value)
- return mutable.MutableList.coerce(key, value)
- else:
- return value
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
- def __setitem__(self, index, value):
- """Detect list set events and emit change events."""
- self._assert_item(value)
- super(_StrictListMixin, self).__setitem__(index, value)
-
- def append(self, item):
- self._assert_item(item)
- super(_StrictListMixin, self).append(item)
-
- def extend(self, item):
- self._assert_item(item)
- super(_StrictListMixin, self).extend(item)
-
- def insert(self, index, item):
- self._assert_item(item)
- super(_StrictListMixin, self).insert(index, item)
-
- @classmethod
- def _assert_item(cls, item):
- if cls._item_cls is not None and not isinstance(item, cls._item_cls):
- raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
- cls._item_cls, type(item)
- ))
-
-
-class _MutableList(mutable.MutableList):
-
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
- try:
- return mutable.MutableList.coerce(key, value)
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
-
-_StrictDictID = namedtuple('_StrictDictID', 'key_cls, value_cls')
-_StrictValue = namedtuple('_StrictValue', 'type_cls, listener_cls')
-
-
-class _StrictDict(object):
- """
- This entire class functions as a factory for strict dicts and their listeners.
- No type class, and no listener type class is created more than once. If a relevant type class
- exists it is returned.
- """
- _strict_map = {}
-
- def __call__(self, key_cls=None, value_cls=None):
- strict_dict_map_key = _StrictDictID(key_cls=key_cls, value_cls=value_cls)
- if strict_dict_map_key not in self._strict_map:
- # Creating the type class itself. this class would be returned (used by the sqlalchemy
- # Column).
- strict_dict_cls = type(
- 'StrictDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
- (Dict, ),
- {}
- )
- # Creating the type listening class.
- # The new class inherits from both the _MutableDict class and the _StrictDictMixin,
- # while setting the necessary _key_cls and _value_cls as class attributes.
- listener_cls = type(
- 'StrictMutableDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
- (_StrictDictMixin, _MutableDict),
- {'_key_cls': key_cls, '_value_cls': value_cls}
- )
- self._strict_map[strict_dict_map_key] = _StrictValue(type_cls=strict_dict_cls,
- listener_cls=listener_cls)
-
- return self._strict_map[strict_dict_map_key].type_cls
-
-StrictDict = _StrictDict()
-
-
-class _StrictList(object):
- """
- This entire class functions as a factory for strict lists and their listeners.
- No type class, and no listener type class is created more than once. If a relevant type class
- exists it is returned.
- """
- _strict_map = {}
-
- def __call__(self, item_cls=None):
-
- if item_cls not in self._strict_map:
- # Creating the type class itself. this class would be returned (used by the sqlalchemy
- # Column).
- strict_list_cls = type(
- 'StrictList_{0}'.format(item_cls.__name__),
- (List, ),
- {}
- )
- # Creating the type listening class.
- # The new class inherits from both the _MutableList class and the _StrictListMixin,
- # while setting the necessary _item_cls as class attribute.
- listener_cls = type(
- 'StrictMutableList_{0}'.format(item_cls.__name__),
- (_StrictListMixin, _MutableList),
- {'_item_cls': item_cls}
- )
- self._strict_map[item_cls] = _StrictValue(type_cls=strict_list_cls,
- listener_cls=listener_cls)
-
- return self._strict_map[item_cls].type_cls
-
-StrictList = _StrictList()
-
-
-def _mutable_association_listener(mapper, cls):
- strict_dict_type_to_listener = \
- dict((v.type_cls, v.listener_cls) for v in _StrictDict._strict_map.values())
-
- strict_list_type_to_listener = \
- dict((v.type_cls, v.listener_cls) for v in _StrictList._strict_map.values())
-
- for prop in mapper.column_attrs:
- column_type = prop.columns[0].type
- # Dict Listeners
- if type(column_type) in strict_dict_type_to_listener: # pylint: disable=unidiomatic-typecheck
- strict_dict_type_to_listener[type(column_type)].associate_with_attribute(
- getattr(cls, prop.key))
- elif isinstance(column_type, Dict):
- _MutableDict.associate_with_attribute(getattr(cls, prop.key))
-
- # List Listeners
- if type(column_type) in strict_list_type_to_listener: # pylint: disable=unidiomatic-typecheck
- strict_list_type_to_listener[type(column_type)].associate_with_attribute(
- getattr(cls, prop.key))
- elif isinstance(column_type, List):
- _MutableList.associate_with_attribute(getattr(cls, prop.key))
-_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener)
-
-
-def _register_mutable_association_listener():
- event.listen(*_LISTENER_ARGS)
-
-
-def remove_mutable_association_listener():
- """
- Remove the event listener that associates ``Dict`` and ``List`` column types with
- ``MutableDict`` and ``MutableList``, respectively.
-
- This call must happen before any model instance is instantiated.
- This is because once it does, that would trigger the listener we are trying to remove.
- Once it is triggered, many other listeners will then be registered.
- At that point, it is too late.
-
- The reason this function exists is that the association listener, interferes with ARIA change
- tracking instrumentation, so a way to disable it is required.
-
- Note that the event listener this call removes is registered by default.
- """
- if event.contains(*_LISTENER_ARGS):
- event.remove(*_LISTENER_ARGS)
-
-_register_mutable_association_listener()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage_initializer.py
----------------------------------------------------------------------
diff --git a/aria/storage_initializer.py b/aria/storage_initializer.py
new file mode 100644
index 0000000..aea5ec8
--- /dev/null
+++ b/aria/storage_initializer.py
@@ -0,0 +1,135 @@
+# 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 threading import RLock
+
+from .storage import model
+from .orchestrator import operation
+from .utils.formatting import safe_repr
+from .utils.console import puts, Colored
+
+
+def initialize_storage(context, model_storage, deployment_id):
+ blueprint = _create_blueprint(context)
+ model_storage.blueprint.put(blueprint)
+
+ deployment = _create_deployment(context, blueprint, deployment_id)
+ model_storage.deployment.put(deployment)
+
+ # Create nodes and node instances
+ for node_template in context.modeling.model.node_templates.values():
+ model_storage.node_template.put(node_template)
+
+ for a_node in context.modeling.instance.find_nodes(node_template.name):
+ node = _create_node_instance(deployment, node_template, a_node)
+ model_storage.node.put(node)
+
+ # Create relationships
+ for node_template in context.modeling.model.node_templates.values():
+ for index, requirement_template in enumerate(node_template.requirement_templates):
+ # We are currently limited only to requirements for specific node templates!
+ if requirement_template.target_node_template_name:
+ model_storage.requirement_template.put(requirement_template)
+
+ for node in context.modeling.instance.find_nodes(node_template.name):
+ for relationship_model in node.relationships:
+ if relationship_model.source_requirement_index == index:
+ source_instance = \
+ model_storage.node.get_by_name(node.id)
+ target_instance = \
+ model_storage.node.get_by_name(
+ relationship_model.target_node_id)
+ relationship = \
+ _create_relationship_instance(source_instance, target_instance)
+ model_storage.relationship.put(relationship)
+
+
+def _create_blueprint(context):
+ now = datetime.utcnow()
+ main_file_name = unicode(context.presentation.location)
+ try:
+ name = context.modeling.model.metadata.values.get('template_name')
+ except AttributeError:
+ name = None
+
+ return model.ServiceTemplate(
+ plan={},
+ name=name or main_file_name,
+ description=context.modeling.model.description or '',
+ created_at=now,
+ updated_at=now,
+ main_file_name=main_file_name
+ )
+
+
+def _create_deployment(context, service_template, service_instance_id):
+ now = datetime.utcnow()
+ return model.ServiceInstance(
+ name='{0}_{1}'.format(service_template.name, service_instance_id),
+ service_template=service_template,
+ description=context.modeling.instance.description or '',
+ created_at=now,
+ updated_at=now,
+ workflows={},
+ permalink='',
+ policy_triggers={},
+ scaling_groups={}
+ )
+
+
+def _create_node_instance(service_instance, node, node_model):
+ return model.Node(
+ service_instance=service_instance,
+ name=node_model.id,
+ runtime_properties={},
+ version=None,
+ node_template=node,
+ state='',
+ scaling_groups=[]
+ )
+
+
+def _create_relationship_instance(source_instance, target_instance):
+ return model.Relationship(
+ source_node=source_instance,
+ target_node=target_instance
+ )
+
+
+_TERMINAL_LOCK = RLock()
+
+
+@operation
+def _dry_node(ctx, _plugin, _implementation, **kwargs):
+ with _TERMINAL_LOCK:
+ print '> node instance: %s' % Colored.red(ctx.node.name)
+ _dump_implementation(_plugin, _implementation)
+
+
+@operation
+def _dry_relationship(ctx, _plugin, _implementation, **kwargs):
+ with _TERMINAL_LOCK:
+ puts('> relationship instance: %s -> %s' % (
+ Colored.red(ctx.relationship.source_node_instance.name),
+ Colored.red(ctx.relationship.target_node_instance.name)))
+ _dump_implementation(_plugin, _implementation)
+
+
+def _dump_implementation(plugin, implementation):
+ if plugin:
+ print ' plugin: %s' % Colored.magenta(plugin)
+ if implementation:
+ print ' implementation: %s' % Colored.yellow(safe_repr(implementation))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/utils/application.py
----------------------------------------------------------------------
diff --git a/aria/utils/application.py b/aria/utils/application.py
index 113e054..2f40825 100644
--- a/aria/utils/application.py
+++ b/aria/utils/application.py
@@ -102,14 +102,14 @@ class StorageManager(LoggerMixin):
assert hasattr(self.model_storage, 'blueprint')
self.logger.debug('creating blueprint resource storage entry')
- self.resource_storage.blueprint.upload(
+ self.resource_storage.service_template.upload(
entry_id=self.blueprint_id,
source=os.path.dirname(source))
self.logger.debug('created blueprint resource storage entry')
self.logger.debug('creating blueprint model storage entry')
now = datetime.utcnow()
- blueprint = self.model_storage.blueprint.model_cls(
+ blueprint = self.model_storage.service_template.model_cls(
plan=self.blueprint_plan,
id=self.blueprint_id,
description=self.blueprint_plan.get('description'),
@@ -117,7 +117,7 @@ class StorageManager(LoggerMixin):
updated_at=now,
main_file_name=main_file_name,
)
- self.model_storage.blueprint.put(blueprint)
+ self.model_storage.service_template.put(blueprint)
self.logger.debug('created blueprint model storage entry')
def create_nodes_storage(self):
@@ -164,10 +164,10 @@ class StorageManager(LoggerMixin):
self.logger.debug('creating deployment resource storage entry')
temp_dir = tempfile.mkdtemp()
try:
- self.resource_storage.blueprint.download(
+ self.resource_storage.service_template.download(
entry_id=self.blueprint_id,
destination=temp_dir)
- self.resource_storage.deployment.upload(
+ self.resource_storage.service_instance.upload(
entry_id=self.deployment_id,
source=temp_dir)
finally:
@@ -176,7 +176,7 @@ class StorageManager(LoggerMixin):
self.logger.debug('creating deployment model storage entry')
now = datetime.utcnow()
- deployment = self.model_storage.deployment.model_cls(
+ deployment = self.model_storage.service_instance.model_cls(
id=self.deployment_id,
blueprint_id=self.blueprint_id,
description=self.deployment_plan['description'],
@@ -190,7 +190,7 @@ class StorageManager(LoggerMixin):
created_at=now,
updated_at=now
)
- self.model_storage.deployment.put(deployment)
+ self.model_storage.service_instance.put(deployment)
self.logger.debug('created deployment model storage entry')
def create_node_instances_storage(self):
@@ -207,24 +207,24 @@ class StorageManager(LoggerMixin):
relationship_instances = []
for index, relationship_instance in enumerate(node_instance['relationships']):
- relationship_instance_model = self.model_storage.relationship_instance.model_cls(
+ relationship_instance_model = self.model_storage.relationship.model_cls(
relationship=node_model.relationships[index],
target_name=relationship_instance['target_name'],
type=relationship_instance['type'],
target_id=relationship_instance['target_id'])
relationship_instances.append(relationship_instance_model)
- self.model_storage.relationship_instance.put(relationship_instance_model)
+ self.model_storage.relationship.put(relationship_instance_model)
- node_instance_model = self.model_storage.node_instance.model_cls(
+ node_instance_model = self.model_storage.node.model_cls(
node=node_model,
id=node_instance['id'],
runtime_properties={},
- state=self.model_storage.node_instance.model_cls.UNINITIALIZED,
+ state=self.model_storage.node.model_cls.UNINITIALIZED,
deployment_id=self.deployment_id,
version='1.0',
relationship_instances=relationship_instances)
- self.model_storage.node_instance.put(node_instance_model)
+ self.model_storage.node.put(node_instance_model)
self.logger.debug('created node-instances model storage entries')
def create_plugin_storage(self, plugin_id, source):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
index 08da2f3..a252a7c 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
@@ -24,5 +24,5 @@ group_types:
description: >-
This is the default (root) TOSCA Group Type definition that all other TOSCA base Group Types derive from.
interfaces:
- standard:
+ Standard:
type: tosca.interfaces.node.lifecycle.Standard
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/context.py
----------------------------------------------------------------------
diff --git a/tests/mock/context.py b/tests/mock/context.py
index cb040ae..cf1a32d 100644
--- a/tests/mock/context.py
+++ b/tests/mock/context.py
@@ -38,13 +38,13 @@ def simple(tmpdir, inmemory=False, context_kwargs=None):
api_kwargs=dict(directory=os.path.join(tmpdir, 'resources'))
)
- deployment_id = create_simple_topology_two_nodes(model_storage)
+ service_instance_id = create_simple_topology_two_nodes(model_storage)
final_kwargs = dict(
name='simple_context',
model_storage=model_storage,
resource_storage=resource_storage,
- deployment_id=deployment_id,
+ service_instance_id=service_instance_id,
workflow_name=models.WORKFLOW_NAME,
task_max_attempts=models.TASK_MAX_ATTEMPTS,
task_retry_interval=models.TASK_RETRY_INTERVAL
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 8229038..047526a 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -15,8 +15,7 @@
from datetime import datetime
-from aria.storage import model
-
+from aria.storage.modeling import model
from . import operations
DEPLOYMENT_NAME = 'test_deployment_id'
@@ -35,84 +34,78 @@ RELATIONSHIP_INSTANCE_NAME = 'relationship_instance'
def get_dependency_node(deployment):
- return model.Node(
+ return model.NodeTemplate(
name=DEPENDENCY_NODE_NAME,
- type='test_node_type',
+ type_name='test_node_type',
type_hierarchy=[],
- number_of_instances=1,
- planned_number_of_instances=1,
- deploy_number_of_instances=1,
- properties={},
- operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
- min_number_of_instances=1,
- max_number_of_instances=1,
- deployment_fk=deployment.id
+ default_instances=1,
+ min_instances=1,
+ max_instances=1,
+ service_template=deployment.service_template,
)
-def get_dependency_node_instance(dependency_node):
- return model.NodeInstance(
+def get_dependency_node_instance(dependency_node, deployment):
+ return model.Node(
name=DEPENDENCY_NODE_INSTANCE_NAME,
+ service_instance=deployment,
runtime_properties={'ip': '1.1.1.1'},
version=None,
- node_fk=dependency_node.id,
+ node_template=dependency_node,
state='',
scaling_groups=[]
)
-def get_relationship(source=None, target=None):
- return model.Relationship(
- source_node_fk=source.id,
- target_node_fk=target.id,
- source_interfaces={},
- source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- target_interfaces={},
- target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
- type='rel_type',
- type_hierarchy=[],
- properties={},
- )
+def get_relationship(target):
+ requirement_template = model.RequirementTemplate(target_node_template_name=target.name)
+ capability_template = model.CapabilityTemplate()
+
+ return requirement_template, capability_template
-def get_relationship_instance(source_instance, target_instance, relationship):
- return model.RelationshipInstance(
- relationship_fk=relationship.id,
- target_node_instance_fk=target_instance.id,
- source_node_instance_fk=source_instance.id,
+def get_relationship_instance(source_instance, target_instance):
+ return model.Relationship(
+ target_node=target_instance,
+ source_node=source_instance,
)
-def get_dependent_node(deployment):
- return model.Node(
+def get_dependent_node(deployment, requirement_template, capability_template):
+ operation_templates = [model.OperationTemplate(implementation=op,
+ service_template=deployment.service_template)
+ for op in operations.NODE_OPERATIONS]
+ interface_template = model.InterfaceTemplate(operation_templates=operation_templates)
+
+ return model.NodeTemplate(
name=DEPENDENT_NODE_NAME,
- deployment_fk=deployment.id,
- type='test_node_type',
+ type_name='test_node_type',
type_hierarchy=[],
- number_of_instances=1,
- planned_number_of_instances=1,
- deploy_number_of_instances=1,
- properties={},
- operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
- min_number_of_instances=1,
- max_number_of_instances=1,
+ default_instances=1,
+ min_instances=1,
+ max_instances=1,
+ service_template=deployment.service_template,
+ interface_templates=[interface_template],
+ requirement_templates=[requirement_template],
+ capability_templates=[capability_template],
)
-def get_dependent_node_instance(dependent_node):
- return model.NodeInstance(
+def get_dependent_node_instance(dependent_node, deployment):
+ return model.Node(
name=DEPENDENT_NODE_INSTANCE_NAME,
+ service_instance=deployment,
runtime_properties={},
version=None,
- node_fk=dependent_node.id,
+ node_template=dependent_node,
state='',
- scaling_groups=[]
+ scaling_groups=[],
)
def get_blueprint():
now = datetime.now()
- return model.Blueprint(
+ return model.ServiceTemplate(
plan={},
name=BLUEPRINT_NAME,
description=None,
@@ -124,7 +117,7 @@ def get_blueprint():
def get_execution(deployment):
return model.Execution(
- deployment_fk=deployment.id,
+ service_instance=deployment,
status=model.Execution.STARTED,
workflow_name=WORKFLOW_NAME,
started_at=datetime.utcnow(),
@@ -134,19 +127,16 @@ def get_execution(deployment):
def get_deployment(blueprint):
now = datetime.utcnow()
- return model.Deployment(
+ return model.ServiceInstance(
name=DEPLOYMENT_NAME,
- blueprint_fk=blueprint.id,
+ service_template=blueprint,
description='',
created_at=now,
updated_at=now,
workflows={},
- inputs={},
- groups={},
permalink='',
policy_triggers={},
policy_types={},
- outputs={},
scaling_groups={},
)
@@ -165,3 +155,28 @@ def get_plugin(package_name='package', package_version='0.1'):
uploaded_at=datetime.now(),
wheels=[],
)
+
+
+def get_interface_template(operation_name, operation_kwargs=None, interface_kwargs=None):
+ operation_template = model.OperationTemplate(
+ name=operation_name,
+ **(operation_kwargs or {})
+
+ )
+ return model.InterfaceTemplate(
+ operation_templates=[operation_template],
+ name=operation_name.rsplit('.', 1)[0],
+ **(interface_kwargs or {})
+ )
+
+
+def get_interface(operation_name,
+ operation_kwargs=None,
+ interface_kwargs=None,
+ edge=None):
+ operation = model.Operation(name=operation_name, **(operation_kwargs or {}))
+ interface_name = operation_name.rsplit('.', 1)[0]
+ return model.Interface(operations=[operation],
+ name=interface_name,
+ edge=edge,
+ **(interface_kwargs or {}))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/mock/topology.py
----------------------------------------------------------------------
diff --git a/tests/mock/topology.py b/tests/mock/topology.py
index e219c33..b04fb46 100644
--- a/tests/mock/topology.py
+++ b/tests/mock/topology.py
@@ -13,84 +13,74 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from datetime import datetime
-
from aria.storage import model
from . import models
-def create_simple_topology_single_node(model_storage, deployment_id, create_operation):
- now = datetime.utcnow()
-
- blueprint = model.Blueprint(name='mock-blueprint',
- created_at=now,
- updated_at=now,
- plan={},
- main_file_name='mock-file')
- model_storage.blueprint.put(blueprint)
-
- deployment = model.Deployment(name='mock-deployment-%d' % deployment_id,
- blueprint_fk=blueprint.id,
- created_at=now,
- updated_at=now)
- model_storage.deployment.put(deployment)
-
- node = model.Node(name='mock-node',
- type='tosca.nodes.Compute',
- operations={
- 'tosca.interfaces.node.lifecycle.Standard.create': {
- 'operation': create_operation,
- 'inputs': {
- 'key': 'create',
- 'value': True}}},
- number_of_instances=1,
- planned_number_of_instances=1,
- deploy_number_of_instances=1,
- min_number_of_instances=1,
- max_number_of_instances=1,
- deployment_fk=deployment.id)
+def create_simple_topology_single_node(model_storage, create_operation):
+ service_template = models.get_blueprint()
+ model_storage.service_template.put(service_template)
+
+ service_instance = models.get_deployment(service_template)
+ model_storage.service_instance.put(service_instance)
+
+ node_template = models.get_dependency_node(service_instance)
+ node_template.interface_templates = [models.get_interface_template(
+ 'tosca.interfaces.node.lifecycle.Standard.create',
+ operation_kwargs=dict(
+ implementation=create_operation,
+ inputs=[model.Parameter(name='key', str_value='create', type='str'),
+ model.Parameter(name='value', str_value=str(True), type='bool')]
+ )
+ )]
+ model_storage.node_template.put(node_template)
+
+ node = models.get_dependency_node_instance(node_template, service_instance)
+ node.interfaces = [models.get_interface(
+ 'tosca.interfaces.node.lifecycle.Standard.create',
+ operation_kwargs=dict(
+ implementation=create_operation,
+ inputs=[model.Parameter(name='key', str_value='create', type='str'),
+ model.Parameter(name='value', str_value=str(True), type='bool')])
+ )]
model_storage.node.put(node)
- node_instance = model.NodeInstance(name='mock-node-instance',
- state='',
- node_fk=node.id)
- model_storage.node_instance.put(node_instance)
-
def create_simple_topology_two_nodes(model_storage):
blueprint = models.get_blueprint()
- model_storage.blueprint.put(blueprint)
+ model_storage.service_template.put(blueprint)
deployment = models.get_deployment(blueprint)
- model_storage.deployment.put(deployment)
+ model_storage.service_instance.put(deployment)
#################################################################################
# Creating a simple deployment with node -> node as a graph
dependency_node = models.get_dependency_node(deployment)
- model_storage.node.put(dependency_node)
- storage_dependency_node = model_storage.node.get(dependency_node.id)
+ model_storage.node_template.put(dependency_node)
+ storage_dependency_node = model_storage.node_template.get(dependency_node.id)
+
+ dependency_node_instance = models.get_dependency_node_instance(storage_dependency_node,
+ deployment)
+ model_storage.node.put(dependency_node_instance)
+ storage_dependency_node_instance = model_storage.node.get(dependency_node_instance.id)
- dependency_node_instance = models.get_dependency_node_instance(storage_dependency_node)
- model_storage.node_instance.put(dependency_node_instance)
- storage_dependency_node_instance = model_storage.node_instance.get(dependency_node_instance.id)
+ req_template, cap_template = models.get_relationship(storage_dependency_node)
+ model_storage.requirement_template.put(req_template)
+ model_storage.capability_template.put(cap_template)
- dependent_node = models.get_dependent_node(deployment)
- model_storage.node.put(dependent_node)
- storage_dependent_node = model_storage.node.get(dependent_node.id)
+ dependent_node = models.get_dependent_node(deployment, req_template, cap_template)
+ model_storage.node_template.put(dependent_node)
+ storage_dependent_node = model_storage.node_template.get(dependent_node.id)
- dependent_node_instance = models.get_dependent_node_instance(storage_dependent_node)
- model_storage.node_instance.put(dependent_node_instance)
- storage_dependent_node_instance = model_storage.node_instance.get(dependent_node_instance.id)
+ dependent_node_instance = models.get_dependent_node_instance(storage_dependent_node, deployment)
+ model_storage.node.put(dependent_node_instance)
+ storage_dependent_node_instance = model_storage.node.get(dependent_node_instance.id)
- relationship = models.get_relationship(storage_dependent_node, storage_dependency_node)
- model_storage.relationship.put(relationship)
- storage_relationship = model_storage.relationship.get(relationship.id)
relationship_instance = models.get_relationship_instance(
- relationship=storage_relationship,
target_instance=storage_dependency_node_instance,
source_instance=storage_dependent_node_instance
)
- model_storage.relationship_instance.put(relationship_instance)
+ model_storage.relationship.put(relationship_instance)
return deployment.id
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_operation.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index e8c7cca..3f39979 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -58,92 +58,94 @@ def executor():
def test_node_operation_task_execution(ctx, executor):
operation_name = 'aria.interfaces.lifecycle.create'
- node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- node.operations[operation_name] = {
- 'operation': op_path(my_operation, module_path=__name__)
-
- }
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ interface = mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__))
+ )
+ node.interfaces = [interface]
ctx.model.node.update(node)
- node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-
inputs = {'putput': True}
@workflow
def basic_workflow(graph, **_):
graph.add_tasks(
- api.task.OperationTask.node_instance(
+ api.task.OperationTask.node(
name=operation_name,
- instance=node_instance,
+ instance=node,
inputs=inputs
)
)
execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
- operation_context = global_test_holder[op_name(node_instance, operation_name)]
+ operation_context = global_test_holder[op_name(node, operation_name)]
assert isinstance(operation_context, context.operation.NodeOperationContext)
# Task bases assertions
- assert operation_context.task.actor == node_instance
- assert operation_context.task.name == op_name(node_instance, operation_name)
- assert operation_context.task.operation_mapping == node.operations[operation_name]['operation']
+ assert operation_context.task.actor == node
+ assert operation_context.task.name == op_name(node, operation_name)
+ operations = interface.operations.filter_by(name=operation_name) # pylint: disable=no-member
+ assert operations.count() == 1
+ assert operation_context.task.implementation == operations[0].implementation
assert operation_context.task.inputs == inputs
# Context based attributes (sugaring)
- assert operation_context.node == node_instance.node
- assert operation_context.node_instance == node_instance
+ assert operation_context.node_template == node.node_template
+ assert operation_context.node == node
def test_relationship_operation_task_execution(ctx, executor):
- operation_name = 'aria.interfaces.relationship_lifecycle.postconfigure'
+ operation_name = 'aria.interfaces.relationship_lifecycle.post_configure'
relationship = ctx.model.relationship.list()[0]
- relationship.source_operations[operation_name] = {
- 'operation': op_path(my_operation, module_path=__name__)
- }
- ctx.model.relationship.update(relationship)
- relationship_instance = ctx.model.relationship_instance.list()[0]
- dependency_node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- dependency_node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- dependent_node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- dependent_node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+ interface = mock.models.get_interface(
+ operation_name=operation_name,
+ operation_kwargs=dict(implementation=op_path(my_operation, module_path=__name__)),
+ edge='source'
+ )
+ relationship.interfaces = [interface]
+ ctx.model.relationship.update(relationship)
inputs = {'putput': True}
@workflow
def basic_workflow(graph, **_):
graph.add_tasks(
- api.task.OperationTask.relationship_instance(
- instance=relationship_instance,
+ api.task.OperationTask.relationship(
+ instance=relationship,
name=operation_name,
- operation_end=api.task.OperationTask.SOURCE_OPERATION,
- inputs=inputs
+ inputs=inputs,
+ edge='source'
)
)
execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
- operation_context = global_test_holder[op_name(relationship_instance, operation_name)]
+ operation_context = global_test_holder[op_name(relationship,
+ operation_name)]
assert isinstance(operation_context, context.operation.RelationshipOperationContext)
# Task bases assertions
- assert operation_context.task.actor == relationship_instance
- assert operation_context.task.name == op_name(relationship_instance, operation_name)
- assert operation_context.task.operation_mapping == \
- relationship.source_operations[operation_name]['operation']
+ assert operation_context.task.actor == relationship
+ assert operation_context.task.name.startswith(operation_name)
+ operation = interface.operations.filter_by(name=operation_name) # pylint: disable=no-member
+ assert operation_context.task.implementation == operation.all()[0].implementation
assert operation_context.task.inputs == inputs
# Context based attributes (sugaring)
+ dependency_node_template = ctx.model.node_template.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+ dependency_node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ dependent_node_template = ctx.model.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+ dependent_node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+
+ assert operation_context.target_node_template == dependency_node_template
assert operation_context.target_node == dependency_node
- assert operation_context.target_node_instance == dependency_node_instance
assert operation_context.relationship == relationship
- assert operation_context.relationship_instance == relationship_instance
+ assert operation_context.source_node_template == dependent_node_template
assert operation_context.source_node == dependent_node
- assert operation_context.source_node_instance == dependent_node_instance
def test_invalid_task_operation_id(ctx, executor):
@@ -155,39 +157,42 @@ def test_invalid_task_operation_id(ctx, executor):
:return:
"""
operation_name = 'aria.interfaces.lifecycle.create'
- other_node_instance, node_instance = ctx.model.node_instance.list()
- assert other_node_instance.id == 1
- assert node_instance.id == 2
+ other_node, node = ctx.model.node.list()
+ assert other_node.id == 1
+ assert node.id == 2
- node = node_instance.node
- node.operations[operation_name] = {
- 'operation': op_path(get_node_instance_id, module_path=__name__)
-
- }
+ interface = mock.models.get_interface(
+ operation_name=operation_name,
+ operation_kwargs=dict(implementation=op_path(get_node_instance_id, module_path=__name__))
+ )
+ node.interfaces = [interface]
ctx.model.node.update(node)
@workflow
def basic_workflow(graph, **_):
graph.add_tasks(
- api.task.OperationTask.node_instance(name=operation_name, instance=node_instance)
+ api.task.OperationTask.node(name=operation_name, instance=node)
)
execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
- op_node_instance_id = global_test_holder[op_name(node_instance, operation_name)]
- assert op_node_instance_id == node_instance.id
- assert op_node_instance_id != other_node_instance.id
+ op_node_instance_id = global_test_holder[op_name(node, operation_name)]
+ assert op_node_instance_id == node.id
+ assert op_node_instance_id != other_node.id
def test_plugin_workdir(ctx, executor, tmpdir):
op = 'test.op'
plugin_name = 'mock_plugin'
- node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- node.operations[op] = {'operation': '{0}.{1}'.format(__name__, _test_plugin_workdir.__name__),
- 'plugin': plugin_name}
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(
+ op,
+ operation_kwargs=dict(
+ implementation='{0}.{1}'.format(__name__, _test_plugin_workdir.__name__),
+ plugin=plugin_name)
+ )]
node.plugins = [{'name': plugin_name}]
ctx.model.node.update(node)
- node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
filename = 'test_file'
content = 'file content'
@@ -195,11 +200,13 @@ def test_plugin_workdir(ctx, executor, tmpdir):
@workflow
def basic_workflow(graph, **_):
- graph.add_tasks(api.task.OperationTask.node_instance(
- name=op, instance=node_instance, inputs=inputs))
+ graph.add_tasks(api.task.OperationTask.node(
+ name=op, instance=node, inputs=inputs))
execute(workflow_func=basic_workflow, workflow_context=ctx, executor=executor)
- expected_file = tmpdir.join('workdir', 'plugins', str(ctx.deployment.id), plugin_name, filename)
+ expected_file = tmpdir.join('workdir', 'plugins', str(ctx.service_instance.id),
+ plugin_name,
+ filename)
assert expected_file.read() == content
@@ -210,7 +217,7 @@ def my_operation(ctx, **_):
@operation
def get_node_instance_id(ctx, **_):
- global_test_holder[ctx.name] = ctx.node_instance.id
+ global_test_holder[ctx.name] = ctx.node.id
@operation
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_resource_render.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_resource_render.py b/tests/orchestrator/context/test_resource_render.py
index ded18c8..aef439c 100644
--- a/tests/orchestrator/context/test_resource_render.py
+++ b/tests/orchestrator/context/test_resource_render.py
@@ -17,7 +17,7 @@ import pytest
from tests import mock, storage
-_IMPLICIT_CTX_TEMPLATE = '{{ctx.deployment.name}}'
+_IMPLICIT_CTX_TEMPLATE = '{{ctx.service_instance.name}}'
_IMPLICIT_CTX_TEMPLATE_PATH = 'implicit-ctx.template'
_VARIABLES_TEMPLATE = '{{variable}}'
_VARIABLES_TEMPLATE_PATH = 'variables.template'
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_serialize.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_serialize.py b/tests/orchestrator/context/test_serialize.py
index ee123a7..1fdcb1a 100644
--- a/tests/orchestrator/context/test_serialize.py
+++ b/tests/orchestrator/context/test_serialize.py
@@ -40,16 +40,16 @@ def test_serialize_operation_context(context, executor, tmpdir):
eng.execute()
-
@workflow
def _mock_workflow(ctx, graph):
- op = 'test.op'
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
plugin_name = 'mock_plugin'
- node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node = node_instance.node
- node.operations[op] = {'operation': _operation_mapping(), 'plugin': plugin_name}
+ node.interfaces = [mock.models.get_interface(
+ 'test.op',
+ operation_kwargs=dict(implementation=_operation_mapping(), plugin=plugin_name)
+ )]
node.plugins = [{'name': plugin_name}]
- task = api.task.OperationTask.node_instance(instance=node_instance, name=op)
+ task = api.task.OperationTask.node(instance=node, name='test.op')
graph.add_tasks(task)
return graph
@@ -59,14 +59,14 @@ def _mock_operation(ctx):
# We test several things in this operation
# ctx.task, ctx.node, etc... tell us that the model storage was properly re-created
# a correct ctx.task.operation_mapping tells us we kept the correct task_id
- assert ctx.task.operation_mapping == _operation_mapping()
+ assert ctx.task.implementation == _operation_mapping()
# a correct ctx.node.name tells us we kept the correct actor_id
- assert ctx.node.name == mock.models.DEPENDENCY_NODE_NAME
+ assert ctx.node.name == mock.models.DEPENDENCY_NODE_INSTANCE_NAME
# a correct ctx.name tells us we kept the correct name
assert ctx.name is not None
assert ctx.name == ctx.task.name
# a correct ctx.deployment.name tells us we kept the correct deployment_id
- assert ctx.deployment.name == mock.models.DEPLOYMENT_NAME
+ assert ctx.service_instance.name == mock.models.DEPLOYMENT_NAME
# Here we test that the resource storage was properly re-created
test_file_content = ctx.resource.blueprint.read(TEST_FILE_ENTRY_ID, TEST_FILE_NAME)
assert test_file_content == TEST_FILE_CONTENT
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_toolbelt.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_toolbelt.py b/tests/orchestrator/context/test_toolbelt.py
index beb5730..28bd3d3 100644
--- a/tests/orchestrator/context/test_toolbelt.py
+++ b/tests/orchestrator/context/test_toolbelt.py
@@ -48,45 +48,46 @@ def executor():
def _get_elements(workflow_context):
- dependency_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- dependency_node.host = dependency_node
- workflow_context.model.node.update(dependency_node)
+ dependency_node_template = workflow_context.model.node_template.get_by_name(
+ mock.models.DEPENDENCY_NODE_NAME)
+ dependency_node_template.host = dependency_node_template
+ workflow_context.model.node.update(dependency_node_template)
- dependency_node_instance = workflow_context.model.node_instance.get_by_name(
+ dependency_node = workflow_context.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- dependency_node_instance.host_fk = dependency_node_instance.id
- workflow_context.model.node_instance.update(dependency_node_instance)
+ dependency_node.host_fk = dependency_node.id
+ workflow_context.model.node.update(dependency_node)
- dependent_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- dependent_node.host_fk = dependency_node.id
- workflow_context.model.node.update(dependent_node)
+ dependent_node_template = workflow_context.model.node_template.get_by_name(
+ mock.models.DEPENDENT_NODE_NAME)
+ dependent_node_template.host = dependency_node_template
+ workflow_context.model.node_template.update(dependent_node_template)
- dependent_node_instance = workflow_context.model.node_instance.get_by_name(
+ dependent_node = workflow_context.model.node.get_by_name(
mock.models.DEPENDENT_NODE_INSTANCE_NAME)
- dependent_node_instance.host_fk = dependent_node_instance.id
- workflow_context.model.node_instance.update(dependent_node_instance)
+ dependent_node.host = dependent_node
+ workflow_context.model.node.update(dependent_node)
relationship = workflow_context.model.relationship.list()[0]
- relationship_instance = workflow_context.model.relationship_instance.list()[0]
- return dependency_node, dependency_node_instance, dependent_node, dependent_node_instance, \
- relationship, relationship_instance
+ return dependency_node_template, dependency_node, dependent_node_template, dependent_node, \
+ relationship
def test_host_ip(workflow_context, executor):
operation_name = 'aria.interfaces.lifecycle.create'
- dependency_node, dependency_node_instance, _, _, _, _ = _get_elements(workflow_context)
- dependency_node.operations[operation_name] = {
- 'operation': op_path(host_ip, module_path=__name__)
-
- }
- workflow_context.model.node.put(dependency_node)
+ _, dependency_node, _, _, _ = _get_elements(workflow_context)
+ dependency_node.interfaces = [mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(implementation=op_path(host_ip, module_path=__name__))
+ )]
+ workflow_context.model.node.update(dependency_node)
inputs = {'putput': True}
@workflow
def basic_workflow(graph, **_):
graph.add_tasks(
- api.task.OperationTask.node_instance(
- instance=dependency_node_instance,
+ api.task.OperationTask.node(
+ instance=dependency_node,
name=operation_name,
inputs=inputs
)
@@ -94,35 +95,37 @@ def test_host_ip(workflow_context, executor):
execute(workflow_func=basic_workflow, workflow_context=workflow_context, executor=executor)
- assert global_test_holder.get('host_ip') == \
- dependency_node_instance.runtime_properties.get('ip')
+ assert global_test_holder.get('host_ip') == dependency_node.runtime_properties.get('ip')
def test_relationship_tool_belt(workflow_context, executor):
- operation_name = 'aria.interfaces.relationship_lifecycle.postconfigure'
- _, _, _, _, relationship, relationship_instance = \
- _get_elements(workflow_context)
- relationship.source_operations[operation_name] = {
- 'operation': op_path(relationship_operation, module_path=__name__)
- }
- workflow_context.model.relationship.put(relationship)
+ operation_name = 'aria.interfaces.relationship_lifecycle.post_configure'
+ _, _, _, _, relationship = _get_elements(workflow_context)
+ relationship.interfaces = [
+ mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(
+ implementation=op_path(relationship_operation, module_path=__name__)),
+ edge='source')
+ ]
+ workflow_context.model.relationship.update(relationship)
inputs = {'putput': True}
@workflow
def basic_workflow(graph, **_):
graph.add_tasks(
- api.task.OperationTask.relationship_instance(
- instance=relationship_instance,
+ api.task.OperationTask.relationship(
+ instance=relationship,
name=operation_name,
- operation_end=api.task.OperationTask.SOURCE_OPERATION,
+ edge='source',
inputs=inputs
)
)
execute(workflow_func=basic_workflow, workflow_context=workflow_context, executor=executor)
- assert isinstance(global_test_holder.get(op_name(relationship_instance, operation_name)),
+ assert isinstance(global_test_holder.get(op_name(relationship, operation_name)),
RelationshipToolBelt)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/context/test_workflow.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_workflow.py b/tests/orchestrator/context/test_workflow.py
index bb54037..4e8eca4 100644
--- a/tests/orchestrator/context/test_workflow.py
+++ b/tests/orchestrator/context/test_workflow.py
@@ -29,9 +29,11 @@ class TestWorkflowContext(object):
def test_execution_creation_on_workflow_context_creation(self, storage):
ctx = self._create_ctx(storage)
execution = storage.execution.get(ctx.execution.id) # pylint: disable=no-member
- assert execution.deployment == storage.deployment.get_by_name(models.DEPLOYMENT_NAME)
+ assert execution.service_instance == storage.service_instance.get_by_name(
+ models.DEPLOYMENT_NAME)
assert execution.workflow_name == models.WORKFLOW_NAME
- assert execution.blueprint == storage.blueprint.get_by_name(models.BLUEPRINT_NAME)
+ assert execution.service_template == storage.service_template.get_by_name(
+ models.BLUEPRINT_NAME)
assert execution.status == storage.execution.model_cls.PENDING
assert execution.parameters == {}
assert execution.created_at <= datetime.utcnow()
@@ -51,7 +53,7 @@ class TestWorkflowContext(object):
name='simple_context',
model_storage=storage,
resource_storage=None,
- deployment_id=storage.deployment.get_by_name(models.DEPLOYMENT_NAME).id,
+ service_instance_id=storage.service_instance.get_by_name(models.DEPLOYMENT_NAME).id,
workflow_name=models.WORKFLOW_NAME,
task_max_attempts=models.TASK_MAX_ATTEMPTS,
task_retry_interval=models.TASK_RETRY_INTERVAL
@@ -62,8 +64,8 @@ class TestWorkflowContext(object):
def storage():
workflow_storage = application_model_storage(
sql_mapi.SQLAlchemyModelAPI, initiator=test_storage.init_inmemory_model_storage)
- workflow_storage.blueprint.put(models.get_blueprint())
- blueprint = workflow_storage.blueprint.get_by_name(models.BLUEPRINT_NAME)
- workflow_storage.deployment.put(models.get_deployment(blueprint))
+ workflow_storage.service_template.put(models.get_blueprint())
+ blueprint = workflow_storage.service_template.get_by_name(models.BLUEPRINT_NAME)
+ workflow_storage.service_instance.put(models.get_deployment(blueprint))
yield workflow_storage
test_storage.release_sqlite_storage(workflow_storage)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 86f2aa7..9e9540f 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -42,10 +42,10 @@ class TestLocalRunScript(object):
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties map.key value
+ ctx node runtime-properties map.key value
''',
windows_script='''
- ctx node-instance runtime-properties map.key value
+ ctx node runtime-properties map.key value
''')
props = self._run(
executor, workflow_context,
@@ -56,12 +56,12 @@ class TestLocalRunScript(object):
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties map.key1 $key1
- ctx node-instance runtime-properties map.key2 $key2
+ ctx node runtime-properties map.key1 $key1
+ ctx node runtime-properties map.key2 $key2
''',
windows_script='''
- ctx node-instance runtime-properties map.key1 %key1%
- ctx node-instance runtime-properties map.key2 %key2%
+ ctx node runtime-properties map.key1 %key1%
+ ctx node runtime-properties map.key2 %key2%
''')
props = self._run(
executor, workflow_context,
@@ -80,10 +80,10 @@ class TestLocalRunScript(object):
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties map.cwd $PWD
+ ctx node runtime-properties map.cwd $PWD
''',
windows_script='''
- ctx node-instance runtime-properties map.cwd %CD%
+ ctx node runtime-properties map.cwd %CD%
''')
tmpdir = str(tmpdir)
props = self._run(
@@ -96,7 +96,7 @@ class TestLocalRunScript(object):
assert p_map['cwd'] == tmpdir
def test_process_command_prefix(self, executor, workflow_context, tmpdir):
- use_ctx = 'ctx node-instance runtime-properties map.key value'
+ use_ctx = 'ctx node runtime-properties map.key value'
python_script = ['import subprocess',
'subprocess.Popen("{0}".split(' ')).communicate()[0]'.format(use_ctx)]
python_script = '\n'.join(python_script)
@@ -120,12 +120,12 @@ class TestLocalRunScript(object):
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties map.arg1 "$1"
- ctx node-instance runtime-properties map.arg2 $2
+ ctx node runtime-properties map.arg1 "$1"
+ ctx node runtime-properties map.arg2 $2
''',
windows_script='''
- ctx node-instance runtime-properties map.arg1 %1
- ctx node-instance runtime-properties map.arg2 %2
+ ctx node runtime-properties map.arg1 %1
+ ctx node runtime-properties map.arg2 %2
''')
props = self._run(
executor, workflow_context,
@@ -186,7 +186,7 @@ class TestLocalRunScript(object):
script = '''
from aria.orchestrator.execution_plugin import ctx, inputs
if __name__ == '__main__':
- ctx.node_instance.runtime_properties['key'] = inputs['key']
+ ctx.node.runtime_properties['key'] = inputs['key']
'''
suffix = '.py'
script_path = self._create_script(
@@ -208,10 +208,10 @@ if __name__ == '__main__':
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties key "${input_as_env_var}"
+ ctx node runtime-properties key "${input_as_env_var}"
''',
windows_script='''
- ctx node-instance runtime-properties key "%input_as_env_var%"
+ ctx node runtime-properties key "%input_as_env_var%"
''')
props = self._run(
executor, workflow_context,
@@ -226,10 +226,10 @@ if __name__ == '__main__':
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties key "${input_as_env_var}"
+ ctx node runtime-properties key "${input_as_env_var}"
''',
windows_script='''
- ctx node-instance runtime-properties key "%input_as_env_var%"
+ ctx node runtime-properties key "%input_as_env_var%"
''')
props = self._run(
@@ -248,10 +248,10 @@ if __name__ == '__main__':
script_path = self._create_script(
tmpdir,
linux_script='''#! /bin/bash -e
- ctx node-instance runtime-properties nonexistent
+ ctx node runtime-properties nonexistent
''',
windows_script='''
- ctx node-instance runtime-properties nonexistent
+ ctx node runtime-properties nonexistent
''')
exception = self._run_and_get_task_exception(
executor, workflow_context,
@@ -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.deployment.id),
+ entry_id=str(workflow_context.service_instance.id),
source=local_script_path,
path=script_path)
@@ -476,13 +476,18 @@ if __name__ == '__main__':
@workflow
def mock_workflow(ctx, graph):
op = 'test.op'
- node_instance = ctx.model.node_instance.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node_instance.node.operations[op] = {
- 'operation': '{0}.{1}'.format(operations.__name__,
- operations.run_script_locally.__name__)}
- graph.add_tasks(api.task.OperationTask.node_instance(
- instance=node_instance,
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(
+ 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,
inputs=inputs))
return graph
@@ -492,7 +497,7 @@ if __name__ == '__main__':
workflow_context=workflow_context,
tasks_graph=tasks_graph)
eng.execute()
- return workflow_context.model.node_instance.get_by_name(
+ return workflow_context.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
@pytest.fixture
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 65195c8..2e270bb 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.deployment.name}}')
+ resource.write('{{ctx.service_instance.name}}')
self._upload(str(resource), 'test_resource')
props = self._execute()
- assert props['test_value'] == self._workflow_context.deployment.name
+ assert props['test_value'] == self._workflow_context.service_instance.name
@pytest.mark.parametrize('value', ['string-value', [1, 2, 3], {'key': 'value'}])
def test_run_script_inputs_as_env_variables_no_override(self, value):
@@ -217,12 +217,13 @@ class TestWithActualSSHServer(object):
@workflow
def mock_workflow(ctx, graph):
op = 'test.op'
- node_instance = ctx.model.node_instance.get_by_name(
- mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node_instance.node.operations[op] = {
- 'operation': '{0}.{1}'.format(operations.__name__, operation.__name__)}
- graph.sequence(*[api.task.OperationTask.node_instance(
- instance=node_instance,
+ 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__))
+ )]
+ graph.sequence(*[api.task.OperationTask.node(
+ instance=node,
name=op,
inputs={
'script_path': script_path,
@@ -241,7 +242,7 @@ class TestWithActualSSHServer(object):
workflow_context=self._workflow_context,
tasks_graph=tasks_graph)
eng.execute()
- return self._workflow_context.model.node_instance.get_by_name(
+ return self._workflow_context.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME).runtime_properties
def _execute_and_get_task_exception(self, *args, **kwargs):
@@ -253,7 +254,7 @@ class TestWithActualSSHServer(object):
def _upload(self, source, path):
self._workflow_context.resource.deployment.upload(
- entry_id=str(self._workflow_context.deployment.id),
+ entry_id=str(self._workflow_context.service_instance.id),
source=source,
path=path)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/test_runner.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/test_runner.py b/tests/orchestrator/test_runner.py
index 1d46e91..17230df 100644
--- a/tests/orchestrator/test_runner.py
+++ b/tests/orchestrator/test_runner.py
@@ -47,10 +47,10 @@ def test_runner_no_tasks():
def test_runner_tasks():
@workflow
def workflow_fn(ctx, graph):
- for node_instance in ctx.model.node_instance.iter():
+ for node in ctx.model.node:
graph.add_tasks(
- OperationTask.node_instance(instance=node_instance,
- name='tosca.interfaces.node.lifecycle.Standard.create'))
+ OperationTask.node(instance=node,
+ name='tosca.interfaces.node.lifecycle.Standard.create'))
_test_runner(workflow_fn)
@@ -60,8 +60,7 @@ def test_runner_tasks():
def _initialize_model_storage_fn(model_storage):
mock.topology.create_simple_topology_single_node(
model_storage,
- 1,
- '%s.%s' % (__name__, mock_create_operation.__name__)
+ '{0}.{1}'.format(__name__, mock_create_operation.__name__)
)
@@ -70,5 +69,5 @@ def _test_runner(workflow_fn):
workflow_fn=workflow_fn,
inputs={},
initialize_model_storage_fn=_initialize_model_storage_fn,
- deployment_id=1)
+ service_instance_id=1)
runner.run()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/__init__.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/__init__.py b/tests/orchestrator/workflows/__init__.py
index fe04b2f..7f0fd56 100644
--- a/tests/orchestrator/workflows/__init__.py
+++ b/tests/orchestrator/workflows/__init__.py
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from . import api, builtin, core
+from . import api, core
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 bb629ef..79618c1 100644
--- a/tests/orchestrator/workflows/api/test_task.py
+++ b/tests/orchestrator/workflows/api/test_task.py
@@ -16,10 +16,9 @@
import pytest
-from aria.storage import model
from aria.orchestrator import context
from aria.orchestrator.workflows import api
-
+from aria.storage.modeling import model
from tests import mock, storage
@@ -31,7 +30,7 @@ def ctx(tmpdir):
:return:
"""
simple_context = mock.context.simple(str(tmpdir), inmemory=False)
- simple_context.model.execution.put(mock.models.get_execution(simple_context.deployment))
+ simple_context.model.execution.put(mock.models.get_execution(simple_context.service_instance))
yield simple_context
storage.release_sqlite_storage(simple_context.model)
@@ -40,32 +39,33 @@ class TestOperationTask(object):
def test_node_operation_task_creation(self, ctx):
operation_name = 'aria.interfaces.lifecycle.create'
- op_details = {'operation': True, 'plugin': 'plugin'}
- node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- node.operations[operation_name] = op_details
+ interface = mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(plugin='plugin', implementation='op_path'))
+
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+ node.interfaces = [interface]
node.plugins = [{'name': 'plugin',
'package_name': 'package',
'package_version': '0.1'}]
- ctx.model.node.update(node)
- node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
- inputs = {'inputs': True}
+ ctx.model.node_template.update(node)
+ inputs = {'name': True}
max_attempts = 10
retry_interval = 10
ignore_failure = True
with context.workflow.current.push(ctx):
- api_task = api.task.OperationTask.node_instance(
+ api_task = api.task.OperationTask.node(
name=operation_name,
- instance=node_instance,
+ instance=node,
inputs=inputs,
max_attempts=max_attempts,
retry_interval=retry_interval,
ignore_failure=ignore_failure)
- assert api_task.name == '{0}.{1}'.format(operation_name, node_instance.id)
- assert api_task.operation_mapping is True
- assert api_task.actor == node_instance
+ assert api_task.name == '{0}.{1}'.format(operation_name, node.id)
+ assert api_task.implementation == 'op_path'
+ assert api_task.actor == node
assert api_task.inputs == inputs
assert api_task.retry_interval == retry_interval
assert api_task.max_attempts == max_attempts
@@ -77,29 +77,34 @@ class TestOperationTask(object):
def test_source_relationship_operation_task_creation(self, ctx):
operation_name = 'aria.interfaces.relationship_lifecycle.preconfigure'
- op_details = {'operation': True, 'plugin': 'plugin'}
+
+ interface = mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(implementation='op_path', plugin='plugin'),
+ edge='source'
+ )
+
relationship = ctx.model.relationship.list()[0]
- relationship.source_operations[operation_name] = op_details
+ relationship.interfaces = [interface]
relationship.source_node.plugins = [{'name': 'plugin',
'package_name': 'package',
'package_version': '0.1'}]
- relationship_instance = ctx.model.relationship_instance.list()[0]
- inputs = {'inputs': True}
+ inputs = {'name': True}
max_attempts = 10
retry_interval = 10
with context.workflow.current.push(ctx):
- api_task = api.task.OperationTask.relationship_instance(
+ api_task = api.task.OperationTask.relationship(
name=operation_name,
- instance=relationship_instance,
- operation_end=api.task.OperationTask.SOURCE_OPERATION,
+ instance=relationship,
+ edge='source',
inputs=inputs,
max_attempts=max_attempts,
retry_interval=retry_interval)
- assert api_task.name == '{0}.{1}'.format(operation_name, relationship_instance.id)
- assert api_task.operation_mapping is True
- assert api_task.actor == relationship_instance
+ assert api_task.name == '{0}.{1}'.format(operation_name, relationship.id)
+ assert api_task.implementation == 'op_path'
+ assert api_task.actor == relationship
assert api_task.inputs == inputs
assert api_task.retry_interval == retry_interval
assert api_task.max_attempts == max_attempts
@@ -110,29 +115,33 @@ class TestOperationTask(object):
def test_target_relationship_operation_task_creation(self, ctx):
operation_name = 'aria.interfaces.relationship_lifecycle.preconfigure'
- op_details = {'operation': True, 'plugin': 'plugin'}
+ interface = mock.models.get_interface(
+ operation_name,
+ operation_kwargs=dict(implementation='op_path', plugin='plugin'),
+ edge='target'
+ )
+
relationship = ctx.model.relationship.list()[0]
- relationship.target_operations[operation_name] = op_details
+ relationship.interfaces = [interface]
relationship.target_node.plugins = [{'name': 'plugin',
'package_name': 'package',
'package_version': '0.1'}]
- relationship_instance = ctx.model.relationship_instance.list()[0]
- inputs = {'inputs': True}
+ inputs = {'name': True}
max_attempts = 10
retry_interval = 10
with context.workflow.current.push(ctx):
- api_task = api.task.OperationTask.relationship_instance(
+ api_task = api.task.OperationTask.relationship(
name=operation_name,
- instance=relationship_instance,
- operation_end=api.task.OperationTask.TARGET_OPERATION,
+ instance=relationship,
+ edge='target',
inputs=inputs,
max_attempts=max_attempts,
retry_interval=retry_interval)
- assert api_task.name == '{0}.{1}'.format(operation_name, relationship_instance.id)
- assert api_task.operation_mapping is True
- assert api_task.actor == relationship_instance
+ assert api_task.name == '{0}.{1}'.format(operation_name, relationship.id)
+ assert api_task.implementation == 'op_path'
+ assert api_task.actor == relationship
assert api_task.inputs == inputs
assert api_task.retry_interval == retry_interval
assert api_task.max_attempts == max_attempts
@@ -142,12 +151,12 @@ class TestOperationTask(object):
assert api_task.runs_on == model.Task.RUNS_ON_TARGET
def test_operation_task_default_values(self, ctx):
- dependency_node_instance = ctx.model.node_instance.get_by_name(
+ dependency_node_instance = ctx.model.node.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
with context.workflow.current.push(ctx):
task = api.task.OperationTask(
name='stub',
- operation_mapping='',
+ implementation='',
actor=dependency_node_instance)
assert task.inputs == {}
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 87e3425..cc9a8a8 100644
--- a/tests/orchestrator/workflows/builtin/test_execute_operation.py
+++ b/tests/orchestrator/workflows/builtin/test_execute_operation.py
@@ -30,8 +30,9 @@ def ctx(tmpdir):
def test_execute_operation(ctx):
- node_instance = ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
-
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(mock.operations.NODE_OPERATIONS_INSTALL[0])]
+ ctx.model.node.update(node)
operation_name = mock.operations.NODE_OPERATIONS_INSTALL[0]
execute_tasks = list(
@@ -43,13 +44,13 @@ def test_execute_operation(ctx):
allow_kwargs_override=False,
run_by_dependency_order=False,
type_names=[],
- node_ids=[],
- node_instance_ids=[node_instance.id]
+ node_template_ids=[],
+ node_ids=[node.id]
).topological_order()
)
assert len(execute_tasks) == 1
- assert execute_tasks[0].name == '{0}.{1}'.format(operation_name, node_instance.id)
+ assert execute_tasks[0].name == '{0}.{1}'.format(operation_name, node.id)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 3e4498f..b553049 100644
--- a/tests/orchestrator/workflows/builtin/test_heal.py
+++ b/tests/orchestrator/workflows/builtin/test_heal.py
@@ -34,9 +34,9 @@ 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_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
+ ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
dependent_node_instance.host_fk = dependent_node_instance.id
- ctx.model.node_instance.update(dependent_node_instance)
+ ctx.model.node.update(dependent_node_instance)
heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependent_node_instance.id)
assert len(list(heal_graph.tasks)) == 2
@@ -64,9 +64,9 @@ 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_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
dependency_node_instance.host_fk = dependency_node_instance.id
- ctx.model.node_instance.update(dependency_node_instance)
+ ctx.model.node.update(dependency_node_instance)
heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependency_node_instance.id)
# both subgraphs should contain un\install for both the dependent and the dependency
assert len(list(heal_graph.tasks)) == 2
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/tests/orchestrator/workflows/core/test_engine.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/workflows/core/test_engine.py b/tests/orchestrator/workflows/core/test_engine.py
index 05a3d90..b2b67fc 100644
--- a/tests/orchestrator/workflows/core/test_engine.py
+++ b/tests/orchestrator/workflows/core/test_engine.py
@@ -23,7 +23,7 @@ from aria.orchestrator import (
workflow,
operation,
)
-from aria.storage import model
+from aria.storage.modeling import model
from aria.orchestrator.workflows import (
api,
exceptions,
@@ -60,13 +60,14 @@ class BaseTest(object):
max_attempts=None,
retry_interval=None,
ignore_failure=None):
- node_instance = \
- ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- node_instance.node.operations['aria.interfaces.lifecycle.create'] = {
- 'operation': '{name}.{func.__name__}'.format(name=__name__, func=func)
- }
- return api.task.OperationTask.node_instance(
- instance=node_instance,
+ node = ctx.model.node.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
+ node.interfaces = [mock.models.get_interface(
+ 'aria.interfaces.lifecycle.create',
+ operation_kwargs=dict(implementation='{name}.{func.__name__}'.format(name=__name__,
+ func=func))
+ )]
+ return api.task.OperationTask.node(
+ instance=node,
name='aria.interfaces.lifecycle.create',
inputs=inputs,
max_attempts=max_attempts,
@@ -219,8 +220,12 @@ class TestCancel(BaseTest):
@workflow
def mock_workflow(ctx, graph):
- return graph.sequence(*(self._op(mock_sleep_task, ctx, inputs={'seconds': 0.1})
- for _ in range(number_of_tasks)))
+ operations = (
+ self._op(mock_sleep_task, ctx, inputs=dict(seconds=0.1))
+ for _ in range(number_of_tasks)
+ )
+ return graph.sequence(*operations)
+
eng = self._engine(workflow_func=mock_workflow,
workflow_context=workflow_context,
executor=executor)
[6/7] incubator-ariatosca git commit: ARIA-44 Merge parser and
storage model
Posted by mx...@apache.org.
ARIA-44 Merge parser and storage model
Additional Changes:
* As a part of this task the name to address the model has been changes to the __tablename__ (or __mapiname__ if specified)
* All of the relationships return a query, so accessing the entire list should be done via all()
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/b6193359
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/b6193359
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/b6193359
Branch: refs/heads/ARIA-106-Create-sqla-logging-handler
Commit: b6193359239ec673f3d7b313dd04122302e75ba6
Parents: 1498ad3
Author: mxmrlv <mx...@gmail.com>
Authored: Thu Jan 19 11:39:36 2017 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Sun Feb 12 18:12:13 2017 +0200
----------------------------------------------------------------------
aria/__init__.py | 49 +-
aria/cli/args_parser.py | 4 +-
aria/cli/commands.py | 15 +-
aria/orchestrator/context/common.py | 22 +-
aria/orchestrator/context/operation.py | 43 +-
aria/orchestrator/context/toolbelt.py | 2 +-
aria/orchestrator/context/workflow.py | 22 +-
aria/orchestrator/runner.py | 11 +-
aria/orchestrator/workflows/api/task.py | 149 +-
.../workflows/builtin/execute_operation.py | 54 +-
aria/orchestrator/workflows/builtin/heal.py | 14 +-
aria/orchestrator/workflows/builtin/install.py | 16 +-
aria/orchestrator/workflows/builtin/start.py | 6 +-
aria/orchestrator/workflows/builtin/stop.py | 6 +-
.../orchestrator/workflows/builtin/uninstall.py | 18 +-
aria/orchestrator/workflows/builtin/utils.py | 52 +-
.../orchestrator/workflows/builtin/workflows.py | 175 ++-
aria/orchestrator/workflows/core/engine.py | 2 +-
aria/orchestrator/workflows/core/task.py | 26 +-
aria/orchestrator/workflows/executor/process.py | 14 +-
aria/orchestrator/workflows/executor/thread.py | 2 +-
aria/parser/modeling/__init__.py | 8 +-
aria/parser/modeling/storage.py | 224 ++-
aria/parser/modeling/utils.py | 4 +-
aria/storage/__init__.py | 13 +-
aria/storage/api.py | 6 +-
aria/storage/base_model.py | 757 ----------
aria/storage/core.py | 7 +-
aria/storage/instrumentation.py | 15 +-
aria/storage/model.py | 110 --
aria/storage/modeling/__init__.py | 35 +
aria/storage/modeling/elements.py | 106 ++
aria/storage/modeling/instance_elements.py | 1286 ++++++++++++++++
aria/storage/modeling/model.py | 219 +++
aria/storage/modeling/orchestrator_elements.py | 468 ++++++
aria/storage/modeling/structure.py | 320 ++++
aria/storage/modeling/template_elements.py | 1387 ++++++++++++++++++
aria/storage/modeling/type.py | 302 ++++
aria/storage/modeling/utils.py | 139 ++
aria/storage/sql_mapi.py | 6 +-
aria/storage/structure.py | 190 ---
aria/storage/type.py | 299 ----
aria/storage_initializer.py | 135 ++
aria/utils/application.py | 24 +-
.../profiles/tosca-simple-1.0/groups.yaml | 2 +-
tests/mock/context.py | 4 +-
tests/mock/models.py | 123 +-
tests/mock/topology.py | 100 +-
tests/orchestrator/context/test_operation.py | 125 +-
.../context/test_resource_render.py | 2 +-
tests/orchestrator/context/test_serialize.py | 18 +-
tests/orchestrator/context/test_toolbelt.py | 75 +-
tests/orchestrator/context/test_workflow.py | 14 +-
.../orchestrator/execution_plugin/test_local.py | 63 +-
tests/orchestrator/execution_plugin/test_ssh.py | 21 +-
tests/orchestrator/test_runner.py | 11 +-
tests/orchestrator/workflows/__init__.py | 2 +-
tests/orchestrator/workflows/api/test_task.py | 83 +-
.../workflows/builtin/test_execute_operation.py | 11 +-
.../orchestrator/workflows/builtin/test_heal.py | 8 +-
.../orchestrator/workflows/core/test_engine.py | 25 +-
tests/orchestrator/workflows/core/test_task.py | 80 +-
.../test_task_graph_into_exececution_graph.py | 20 +-
.../workflows/executor/test_executor.py | 9 +-
.../workflows/executor/test_process_executor.py | 8 +-
.../executor/test_process_executor_extension.py | 15 +-
.../test_process_executor_tracked_changes.py | 51 +-
tests/resources/scripts/test_ssh.sh | 30 +-
.../service_templates/node-cellar/workflows.py | 9 +-
tests/storage/__init__.py | 11 +-
tests/storage/test_instrumentation.py | 17 +-
tests/storage/test_model_storage.py | 45 +-
tests/storage/test_models.py | 643 ++++----
tests/storage/test_structures.py | 135 +-
74 files changed, 5797 insertions(+), 2725 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/__init__.py
----------------------------------------------------------------------
diff --git a/aria/__init__.py b/aria/__init__.py
index 8b87473..18eaa56 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -61,26 +61,47 @@ def application_model_storage(api, api_kwargs=None, initiator=None, initiator_kw
"""
Initiate model storage
"""
- models = [
- storage.model.Plugin,
+ models_to_register = [
+ storage.modeling.model.Parameter,
- storage.model.Blueprint,
- storage.model.Deployment,
- storage.model.DeploymentUpdate,
- storage.model.DeploymentUpdateStep,
- storage.model.DeploymentModification,
+ storage.modeling.model.MappingTemplate,
+ storage.modeling.model.SubstitutionTemplate,
+ storage.modeling.model.ServiceTemplate,
+ storage.modeling.model.NodeTemplate,
+ storage.modeling.model.GroupTemplate,
+ storage.modeling.model.InterfaceTemplate,
+ storage.modeling.model.OperationTemplate,
+ storage.modeling.model.ArtifactTemplate,
+ storage.modeling.model.PolicyTemplate,
+ storage.modeling.model.GroupPolicyTemplate,
+ storage.modeling.model.GroupPolicyTriggerTemplate,
+ storage.modeling.model.RequirementTemplate,
+ storage.modeling.model.CapabilityTemplate,
- storage.model.Node,
- storage.model.NodeInstance,
- storage.model.Relationship,
- storage.model.RelationshipInstance,
+ storage.modeling.model.Mapping,
+ storage.modeling.model.Substitution,
+ storage.modeling.model.ServiceInstance,
+ storage.modeling.model.Node,
+ storage.modeling.model.Group,
+ storage.modeling.model.Interface,
+ storage.modeling.model.Operation,
+ storage.modeling.model.Capability,
+ storage.modeling.model.Artifact,
+ storage.modeling.model.Policy,
+ storage.modeling.model.GroupPolicy,
+ storage.modeling.model.GroupPolicyTrigger,
+ storage.modeling.model.Relationship,
- storage.model.Execution,
- storage.model.Task,
+ storage.modeling.model.Execution,
+ storage.modeling.model.ServiceInstanceUpdate,
+ storage.modeling.model.ServiceInstanceUpdateStep,
+ storage.modeling.model.ServiceInstanceModification,
+ storage.modeling.model.Plugin,
+ storage.modeling.model.Task
]
return storage.ModelStorage(api_cls=api,
api_kwargs=api_kwargs,
- items=models,
+ items=models_to_register,
initiator=initiator,
initiator_kwargs=initiator_kwargs or {})
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/cli/args_parser.py
----------------------------------------------------------------------
diff --git a/aria/cli/args_parser.py b/aria/cli/args_parser.py
index e661620..50fec39 100644
--- a/aria/cli/args_parser.py
+++ b/aria/cli/args_parser.py
@@ -138,9 +138,9 @@ def add_workflow_parser(workflow):
default='install',
help='The workflow name')
workflow.add_argument(
- '-d', '--deployment-id',
+ '-i', '--service-instance-id',
required=False,
- help='A unique ID for the deployment')
+ help='A unique ID for the service instance')
@sub_parser_decorator(
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/cli/commands.py
----------------------------------------------------------------------
diff --git a/aria/cli/commands.py b/aria/cli/commands.py
index 0890cd1..91d748f 100644
--- a/aria/cli/commands.py
+++ b/aria/cli/commands.py
@@ -42,7 +42,7 @@ from ..parser.consumption import (
Instance
)
from ..parser.loading import LiteralLocation, UriLocation
-from ..parser.modeling import initialize_storage
+from ..parser.modeling.storage import initialize_storage
from ..utils.application import StorageManager
from ..utils.caching import cachedmethod
from ..utils.console import (puts, Colored, indent)
@@ -211,10 +211,10 @@ class WorkflowCommand(BaseCommand):
def __call__(self, args_namespace, unknown_args):
super(WorkflowCommand, self).__call__(args_namespace, unknown_args)
- deployment_id = args_namespace.deployment_id or 1
+ service_instance_id = args_namespace.service_instance_id or 1
context = self._parse(args_namespace.uri)
workflow_fn, inputs = self._get_workflow(context, args_namespace.workflow)
- self._run(context, args_namespace.workflow, workflow_fn, inputs, deployment_id)
+ self._run(context, args_namespace.workflow, workflow_fn, inputs, service_instance_id)
def _parse(self, uri):
# Parse
@@ -259,13 +259,14 @@ class WorkflowCommand(BaseCommand):
return workflow_fn, inputs
- def _run(self, context, workflow_name, workflow_fn, inputs, deployment_id):
+ def _run(self, context, workflow_name, workflow_fn, inputs, service_instance_id):
# Storage
def _initialize_storage(model_storage):
- initialize_storage(context, model_storage, deployment_id)
+ initialize_storage(context, model_storage, service_instance_id)
# Create runner
- runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage, deployment_id)
+ runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage,
+ service_instance_id)
# Run
runner.run()
@@ -366,7 +367,7 @@ class ExecuteCommand(BaseCommand):
FileSystemResourceDriver(local_resource_storage()))
model_storage = application_model_storage(
FileSystemModelDriver(local_model_storage()))
- deployment = model_storage.deployment.get(args_namespace.deployment_id)
+ deployment = model_storage.service_instance.get(args_namespace.deployment_id)
try:
workflow = deployment.workflows[args_namespace.workflow_id]
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/common.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 6ab27ef..37482cf 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -31,7 +31,7 @@ class BaseContext(logger.LoggerMixin):
def __init__(
self,
name,
- deployment_id,
+ service_instance_id,
model_storage,
resource_storage,
workdir=None,
@@ -41,13 +41,13 @@ class BaseContext(logger.LoggerMixin):
self._id = str(uuid4())
self._model = model_storage
self._resource = resource_storage
- self._deployment_id = deployment_id
+ self._service_instance_id = service_instance_id
self._workdir = workdir
def __repr__(self):
return (
'{name}(name={self.name}, '
- 'deployment_id={self._deployment_id}, '
+ 'deployment_id={self._service_instance_id}, '
.format(name=self.__class__.__name__, self=self))
@property
@@ -67,18 +67,18 @@ class BaseContext(logger.LoggerMixin):
return self._resource
@property
- def blueprint(self):
+ def service_template(self):
"""
The blueprint model
"""
- return self.deployment.blueprint
+ return self.service_instance.service_template
@property
- def deployment(self):
+ def service_instance(self):
"""
The deployment model
"""
- return self.model.deployment.get(self._deployment_id)
+ return self.model.service_instance.get(self._service_instance_id)
@property
def name(self):
@@ -101,11 +101,11 @@ class BaseContext(logger.LoggerMixin):
Download a blueprint resource from the resource storage
"""
try:
- self.resource.deployment.download(entry_id=str(self.deployment.id),
+ self.resource.deployment.download(entry_id=str(self.service_instance.id),
destination=destination,
path=path)
except exceptions.StorageError:
- self.resource.blueprint.download(entry_id=str(self.blueprint.id),
+ self.resource.blueprint.download(entry_id=str(self.service_template.id),
destination=destination,
path=path)
@@ -126,9 +126,9 @@ class BaseContext(logger.LoggerMixin):
Read a deployment resource as string from the resource storage
"""
try:
- return self.resource.deployment.read(entry_id=str(self.deployment.id), path=path)
+ return self.resource.deployment.read(entry_id=str(self.service_instance.id), path=path)
except exceptions.StorageError:
- return self.resource.blueprint.read(entry_id=str(self.blueprint.id), path=path)
+ return self.resource.deployment.read(entry_id=str(self.service_template.id), path=path)
def get_resource_and_render(self, path=None, variables=None):
"""
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/operation.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py
index d1f61b2..c5ac8f0 100644
--- a/aria/orchestrator/context/operation.py
+++ b/aria/orchestrator/context/operation.py
@@ -31,7 +31,7 @@ class BaseOperationContext(BaseContext):
name,
model_storage,
resource_storage,
- deployment_id,
+ service_instance_id,
task_id,
actor_id,
**kwargs):
@@ -39,7 +39,7 @@ class BaseOperationContext(BaseContext):
name=name,
model_storage=model_storage,
resource_storage=resource_storage,
- deployment_id=deployment_id,
+ service_instance_id=service_instance_id,
**kwargs)
self._task_id = task_id
self._actor_id = actor_id
@@ -69,7 +69,7 @@ class BaseOperationContext(BaseContext):
if not self.task.plugin_name:
return None
plugin_workdir = '{0}/plugins/{1}/{2}'.format(self._workdir,
- self.deployment.id,
+ self.service_instance.id,
self.task.plugin_name)
file.makedirs(plugin_workdir)
return plugin_workdir
@@ -79,7 +79,7 @@ class BaseOperationContext(BaseContext):
context_cls = self.__class__
context_dict = {
'name': self.name,
- 'deployment_id': self._deployment_id,
+ 'service_instance_id': self._service_instance_id,
'task_id': self._task_id,
'actor_id': self._actor_id,
'workdir': self._workdir,
@@ -106,20 +106,20 @@ class NodeOperationContext(BaseOperationContext):
Context for node based operations.
"""
@property
- def node(self):
+ def node_template(self):
"""
the node of the current operation
:return:
"""
- return self.node_instance.node
+ return self.node.node_template
@property
- def node_instance(self):
+ def node(self):
"""
The node instance of the current operation
:return:
"""
- return self.model.node_instance.get(self._actor_id)
+ return self.model.node.get(self._actor_id)
class RelationshipOperationContext(BaseOperationContext):
@@ -127,50 +127,41 @@ class RelationshipOperationContext(BaseOperationContext):
Context for relationship based operations.
"""
@property
- def source_node(self):
+ def source_node_template(self):
"""
The source node
:return:
"""
- return self.relationship.source_node
+ return self.source_node.node_template
@property
- def source_node_instance(self):
+ def source_node(self):
"""
The source node instance
:return:
"""
- return self.relationship_instance.source_node_instance
+ return self.relationship.source_node
@property
- def target_node(self):
+ def target_node_template(self):
"""
The target node
:return:
"""
- return self.relationship.target_node
+ return self.target_node.node_template
@property
- def target_node_instance(self):
+ def target_node(self):
"""
The target node instance
:return:
"""
- return self.relationship_instance.target_node_instance
+ return self.relationship.target_node
@property
def relationship(self):
"""
- The relationship of the current operation
- :return:
- """
-
- return self.relationship_instance.relationship
-
- @property
- def relationship_instance(self):
- """
The relationship instance of the current operation
:return:
"""
- return self.model.relationship_instance.get(self._actor_id)
+ return self.model.relationship.get(self._actor_id)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/toolbelt.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/toolbelt.py b/aria/orchestrator/context/toolbelt.py
index 301b013..def7d42 100644
--- a/aria/orchestrator/context/toolbelt.py
+++ b/aria/orchestrator/context/toolbelt.py
@@ -33,7 +33,7 @@ class NodeToolBelt(object):
:return:
"""
assert isinstance(self._op_context, operation.NodeOperationContext)
- host = self._op_context.node_instance.host
+ host = self._op_context.node.host
return host.runtime_properties.get('ip')
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/context/workflow.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/workflow.py b/aria/orchestrator/context/workflow.py
index a15790e..00ed974 100644
--- a/aria/orchestrator/context/workflow.py
+++ b/aria/orchestrator/context/workflow.py
@@ -49,18 +49,16 @@ class WorkflowContext(BaseContext):
def __repr__(self):
return (
- '{name}(deployment_id={self._deployment_id}, '
+ '{name}(deployment_id={self._service_instance_id}, '
'workflow_name={self._workflow_name}'.format(
name=self.__class__.__name__, self=self))
def _create_execution(self):
- execution_cls = self.model.execution.model_cls
now = datetime.utcnow()
execution = self.model.execution.model_cls(
- deployment=self.deployment,
+ service_instance=self.service_instance,
workflow_name=self._workflow_name,
created_at=now,
- status=execution_cls.PENDING,
parameters=self.parameters,
)
self.model.execution.put(execution)
@@ -81,27 +79,27 @@ class WorkflowContext(BaseContext):
self.model.execution.put(value)
@property
- def nodes(self):
+ def node_templates(self):
"""
Iterator over nodes
"""
- key = 'deployment_{0}'.format(self.model.node.model_cls.name_column_name())
+ key = 'service_instance_{0}'.format(self.model.node_template.model_cls.name_column_name())
- return self.model.node.iter(
+ return self.model.node_template.iter(
filters={
- key: getattr(self.deployment, self.deployment.name_column_name())
+ key: getattr(self.service_instance, self.service_instance.name_column_name())
}
)
@property
- def node_instances(self):
+ def nodes(self):
"""
Iterator over node instances
"""
- key = 'deployment_{0}'.format(self.model.node_instance.model_cls.name_column_name())
- return self.model.node_instance.iter(
+ key = 'service_instance_{0}'.format(self.model.node.model_cls.name_column_name())
+ return self.model.node.iter(
filters={
- key: getattr(self.deployment, self.deployment.name_column_name())
+ key: getattr(self.service_instance, self.service_instance.name_column_name())
}
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/runner.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/runner.py b/aria/orchestrator/runner.py
index 5950dc5..8927de9 100644
--- a/aria/orchestrator/runner.py
+++ b/aria/orchestrator/runner.py
@@ -50,7 +50,7 @@ class Runner(object):
"""
def __init__(self, workflow_name, workflow_fn, inputs, initialize_model_storage_fn,
- deployment_id, storage_path='', is_storage_temporary=True):
+ service_instance_id, storage_path='', is_storage_temporary=True):
if storage_path == '':
# Temporary file storage
the_file, storage_path = tempfile.mkstemp(suffix='.db', prefix='aria-')
@@ -61,7 +61,7 @@ class Runner(object):
self._storage_name = os.path.basename(storage_path)
self._is_storage_temporary = is_storage_temporary
- workflow_context = self.create_workflow_context(workflow_name, deployment_id,
+ workflow_context = self.create_workflow_context(workflow_name, service_instance_id,
initialize_model_storage_fn)
tasks_graph = workflow_fn(ctx=workflow_context, **inputs)
@@ -77,7 +77,10 @@ class Runner(object):
finally:
self.cleanup()
- def create_workflow_context(self, workflow_name, deployment_id, initialize_model_storage_fn):
+ def create_workflow_context(self,
+ workflow_name,
+ service_instance_id,
+ initialize_model_storage_fn):
self.cleanup()
model_storage = application_model_storage(
sql_mapi.SQLAlchemyModelAPI,
@@ -89,7 +92,7 @@ class Runner(object):
name=workflow_name,
model_storage=model_storage,
resource_storage=resource_storage,
- deployment_id=deployment_id,
+ service_instance_id=service_instance_id,
workflow_name=self.__class__.__name__,
task_max_attempts=1,
task_retry_interval=1)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 44715c1..6a00844 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -18,7 +18,7 @@ Provides the tasks to be entered into the task graph
"""
from uuid import uuid4
-from aria.storage import model
+from aria.storage.modeling import model
from ... import context
from .. import exceptions
@@ -57,13 +57,13 @@ class OperationTask(BaseTask):
Represents an operation task in the task_graph
"""
- SOURCE_OPERATION = 'source_operations'
- TARGET_OPERATION = 'target_operations'
+ SOURCE_OPERATION = 'source'
+ TARGET_OPERATION = 'target'
def __init__(self,
name,
actor,
- operation_mapping,
+ implementation,
max_attempts=None,
retry_interval=None,
ignore_failure=None,
@@ -76,12 +76,12 @@ class OperationTask(BaseTask):
:param actor: the operation host on which this operation is registered.
:param inputs: operation inputs.
"""
- assert isinstance(actor, (model.NodeInstance,
- model.RelationshipInstance))
+ assert isinstance(actor, (model.Node,
+ model.Relationship))
super(OperationTask, self).__init__()
self.actor = actor
self.name = '{name}.{actor.id}'.format(name=name, actor=actor)
- self.operation_mapping = operation_mapping
+ self.implementation = implementation
self.inputs = inputs or {}
self.plugin = plugin or {}
self.max_attempts = (self.workflow_context._task_max_attempts
@@ -93,70 +93,133 @@ class OperationTask(BaseTask):
self.runs_on = runs_on
@classmethod
- def node_instance(cls, instance, name, inputs=None, *args, **kwargs):
+ def _merge_inputs(cls, operation_inputs, additional_inputs=None):
+ final_inputs = dict((p.name, p.as_raw['value']) for p in operation_inputs)
+ final_inputs.update(additional_inputs or {})
+ return final_inputs
+
+ @classmethod
+ def node(cls, instance, name, inputs=None, *args, **kwargs):
"""
Represents a node based operation
:param instance: the node of which this operation belongs to.
:param name: the name of the operation.
"""
- assert isinstance(instance, model.NodeInstance)
- return cls._instance(instance=instance,
- name=name,
- operation_details=instance.node.operations[name],
- inputs=inputs,
- plugins=instance.node.plugins or [],
- runs_on=model.Task.RUNS_ON_NODE_INSTANCE,
- *args,
- **kwargs)
+ assert isinstance(instance, model.Node)
+ interface_name = _get_interface_name(name)
+ interfaces = instance.interfaces.filter_by(name=interface_name)
+ if interfaces.count() > 1:
+ raise exceptions.TaskException(
+ "More than one interface with the same name `{0}` found".format(name)
+ )
+ elif interfaces.count() == 0:
+ raise exceptions.TaskException(
+ "No Interface with the name `{interface_name}` found".format(
+ interface_name=interface_name)
+ )
+
+ operation_templates = interfaces[0].operations.filter_by(name=name)
+ if operation_templates.count() > 1:
+ raise exceptions.TaskException(
+ "More than one operation with the same name `{0}` were found".format(name)
+ )
+
+ elif operation_templates.count() == 0:
+ raise exceptions.TaskException(
+ "No interface with the name `{operation_name}` found".format(
+ operation_name=name)
+ )
+
+ return cls._instance(
+ instance=instance,
+ name=name,
+ operation_template=operation_templates[0],
+ plugins=instance.plugins or [],
+ runs_on=model.Task.RUNS_ON_NODE_INSTANCE,
+ inputs=cls._merge_inputs(operation_templates[0].inputs, inputs),
+ *args,
+ **kwargs)
@classmethod
- def relationship_instance(cls, instance, name, operation_end, inputs=None, *args, **kwargs):
+ def relationship(cls, instance, name, edge, runs_on=None, inputs=None, *args,
+ **kwargs):
"""
Represents a relationship based operation
:param instance: the relationship of which this operation belongs to.
:param name: the name of the operation.
- :param operation_end: source or target end of the relationship, this corresponds directly
- with 'source_operations' and 'target_operations'
+ :param edge: the edge of the interface ("source" or "target").
+ :param runs_on: where to run the operation ("source" or "target"); if None defaults to the
+ interface edge.
:param inputs any additional inputs to the operation
"""
- assert isinstance(instance, model.RelationshipInstance)
- if operation_end not in [cls.TARGET_OPERATION, cls.SOURCE_OPERATION]:
- raise exceptions.TaskException('The operation end should be {0} or {1}'.format(
- cls.TARGET_OPERATION, cls.SOURCE_OPERATION
- ))
- operation_details = getattr(instance.relationship, operation_end)[name]
- if operation_end == cls.SOURCE_OPERATION:
- plugins = instance.relationship.source_node.plugins
- runs_on = model.Task.RUNS_ON_SOURCE
+ assert isinstance(instance, model.Relationship)
+ interface_name = _get_interface_name(name)
+ interfaces = instance.interfaces.filter_by(name=interface_name, edge=edge)
+ count = interfaces.count()
+ if count > 1:
+ raise exceptions.TaskException(
+ "More than one interface with the same name `{interface_name}` found at `{edge}`"
+ + " edge".format(
+ interface_name=interface_name, edge=edge)
+ )
+ elif count == 0:
+ raise exceptions.TaskException(
+ "No interface with the name `{interface_name}` found at `{edge}` edge".format(
+ interface_name=interface_name, edge=edge)
+ )
+
+ operations = interfaces.all()[0].operations.filter_by(name=name)
+ count = operations.count()
+ if count > 1:
+ raise exceptions.TaskException(
+ "More than one operation with the same name `{0}` found".format(name)
+ )
+ elif count == 0:
+ raise exceptions.TaskException(
+ "No operation with the name `{operation_name}` found".format(
+ operation_name=name)
+ )
+
+ if not runs_on:
+ if edge == cls.SOURCE_OPERATION:
+ runs_on = model.Task.RUNS_ON_SOURCE
+ else:
+ runs_on = model.Task.RUNS_ON_TARGET
+
+ if runs_on == model.Task.RUNS_ON_SOURCE:
+ plugins = instance.source_node.plugins
else:
- plugins = instance.relationship.target_node.plugins
- runs_on = model.Task.RUNS_ON_TARGET
+ plugins = instance.target_node.plugins
+
return cls._instance(instance=instance,
name=name,
- operation_details=operation_details,
- inputs=inputs,
+ operation_template=operations[0],
plugins=plugins or [],
runs_on=runs_on,
+ inputs=cls._merge_inputs(operations[0].inputs, inputs),
*args,
**kwargs)
@classmethod
- def _instance(cls, instance, name, operation_details, inputs, plugins, runs_on, *args,
+ def _instance(cls,
+ instance,
+ name,
+ operation_template,
+ inputs,
+ plugins,
+ runs_on,
+ *args,
**kwargs):
- operation_mapping = operation_details.get('operation')
- operation_inputs = operation_details.get('inputs', {})
- operation_inputs.update(inputs or {})
- plugin_name = operation_details.get('plugin')
- matching_plugins = [p for p in plugins if p['name'] == plugin_name]
+ matching_plugins = [p for p in plugins if p['name'] == operation_template.plugin]
# All matching plugins should have identical package_name/package_version, so it's safe to
# take the first found.
plugin = matching_plugins[0] if matching_plugins else {}
return cls(actor=instance,
name=name,
- operation_mapping=operation_mapping,
- inputs=operation_inputs,
+ implementation=operation_template.implementation,
+ inputs=inputs,
plugin=plugin,
runs_on=runs_on,
*args,
@@ -197,3 +260,7 @@ class StubTask(BaseTask):
Enables creating empty tasks.
"""
pass
+
+
+def _get_interface_name(operation_name):
+ return operation_name.rsplit('.', 1)[0]
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/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 e76993d..5a7f6ce 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -30,8 +30,8 @@ def execute_operation(
allow_kwargs_override,
run_by_dependency_order,
type_names,
+ node_template_ids,
node_ids,
- node_instance_ids,
**kwargs):
"""
The execute_operation workflow
@@ -43,32 +43,32 @@ def execute_operation(
:param bool allow_kwargs_override:
:param bool run_by_dependency_order:
:param type_names:
+ :param node_template_ids:
:param node_ids:
- :param node_instance_ids:
:param kwargs:
:return:
"""
subgraphs = {}
# filtering node instances
- filtered_node_instances = list(_filter_node_instances(
+ filtered_nodes = list(_filter_node_instances(
context=ctx,
+ node_template_ids=node_template_ids,
node_ids=node_ids,
- node_instance_ids=node_instance_ids,
type_names=type_names))
if run_by_dependency_order:
filtered_node_instances_ids = set(node_instance.id
- for node_instance in filtered_node_instances)
- for node_instance in ctx.node_instances:
- if node_instance.id not in filtered_node_instances_ids:
- subgraphs[node_instance.id] = ctx.task_graph(
- name='execute_operation_stub_{0}'.format(node_instance.id))
+ for node_instance in filtered_nodes)
+ for node in ctx.node_instances:
+ if node.id not in filtered_node_instances_ids:
+ subgraphs[node.id] = ctx.task_graph(
+ name='execute_operation_stub_{0}'.format(node.id))
# registering actual tasks to sequences
- for node_instance in filtered_node_instances:
+ for node in filtered_nodes:
graph.add_tasks(
_create_node_instance_task(
- node_instance=node_instance,
+ nodes=node,
operation=operation,
operation_kwargs=operation_kwargs,
allow_kwargs_override=allow_kwargs_override
@@ -80,37 +80,37 @@ def execute_operation(
# adding tasks dependencies if required
if run_by_dependency_order:
- for node_instance in ctx.node_instances:
- for relationship_instance in node_instance.relationship_instances:
- graph.add_dependency(source_task=subgraphs[node_instance.id],
- after=[subgraphs[relationship_instance.target_id]])
+ for node in ctx.nodes:
+ for relationship in node.relationships:
+ graph.add_dependency(
+ source_task=subgraphs[node.id], after=[subgraphs[relationship.target_id]])
-def _filter_node_instances(context, node_ids=(), node_instance_ids=(), type_names=()):
+def _filter_node_instances(context, node_template_ids=(), node_ids=(), type_names=()):
def _is_node_by_id(node_id):
- return not node_ids or node_id in node_ids
+ return not node_template_ids or node_id in node_template_ids
def _is_node_instance_by_id(node_instance_id):
- return not node_instance_ids or node_instance_id in node_instance_ids
+ return not node_ids or node_instance_id in node_ids
def _is_node_by_type(node_type_hierarchy):
return not type_names or node_type_hierarchy in type_names
- for node_instance in context.node_instances:
- if all((_is_node_by_id(node_instance.node.id),
- _is_node_instance_by_id(node_instance.id),
- _is_node_by_type(node_instance.node.type_hierarchy))):
- yield node_instance
+ for node in context.nodes:
+ if all((_is_node_by_id(node.node_template.id),
+ _is_node_instance_by_id(node.id),
+ _is_node_by_type(node.node_template.type_hierarchy))):
+ yield node
def _create_node_instance_task(
- node_instance,
+ nodes,
operation,
operation_kwargs,
allow_kwargs_override):
"""
A workflow which executes a single operation
- :param node_instance: the node instance to install
+ :param nodes: the node instance to install
:param basestring operation: the operation name
:param dict operation_kwargs:
:param bool allow_kwargs_override:
@@ -120,7 +120,7 @@ def _create_node_instance_task(
if allow_kwargs_override is not None:
operation_kwargs['allow_kwargs_override'] = allow_kwargs_override
- return OperationTask.node_instance(
- instance=node_instance,
+ return OperationTask.node(
+ instance=nodes,
name=operation,
inputs=operation_kwargs)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/heal.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/heal.py b/aria/orchestrator/workflows/builtin/heal.py
index 87ac492..2592323 100644
--- a/aria/orchestrator/workflows/builtin/heal.py
+++ b/aria/orchestrator/workflows/builtin/heal.py
@@ -21,7 +21,7 @@ Builtin heal workflow
from aria import workflow
-from .workflows import (install_node_instance, uninstall_node_instance)
+from .workflows import (install_node, uninstall_node)
from ..api import task
@@ -35,8 +35,8 @@ def heal(ctx, graph, node_instance_id):
:param node_instance_id: the id of the node instance to heal
:return:
"""
- failing_node = ctx.model.node_instance.get(node_instance_id)
- host_node = ctx.model.node_instance.get(failing_node.host.id)
+ failing_node = ctx.model.node.get(node_instance_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)
@@ -78,7 +78,7 @@ def heal_uninstall(ctx, graph, failing_node_instances, targeted_node_instances):
# create install sub workflow for every node instance
for node_instance in failing_node_instances:
- node_instance_sub_workflow = task.WorkflowTask(uninstall_node_instance,
+ 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)
@@ -98,7 +98,7 @@ def heal_uninstall(ctx, graph, failing_node_instances, targeted_node_instances):
for relationship_instance in reversed(node_instance.outbound_relationship_instances):
target_node_instance = \
- ctx.model.node_instance.get(relationship_instance.target_node_instance.id)
+ 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)
@@ -131,7 +131,7 @@ def heal_install(ctx, graph, failing_node_instances, targeted_node_instances):
# create install sub workflow for every node instance
for node_instance in failing_node_instances:
- node_instance_sub_workflow = task.WorkflowTask(install_node_instance,
+ 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)
@@ -151,7 +151,7 @@ def heal_install(ctx, graph, failing_node_instances, 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_instance.get(
+ 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)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/install.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/install.py b/aria/orchestrator/workflows/builtin/install.py
index 81d1da2..2b9ec66 100644
--- a/aria/orchestrator/workflows/builtin/install.py
+++ b/aria/orchestrator/workflows/builtin/install.py
@@ -17,18 +17,16 @@
Builtin install workflow
"""
-from .workflows import install_node_instance
-from .utils import create_node_instance_task_dependencies
+from .workflows import install_node
+from .utils import create_node_task_dependencies
from ..api.task import WorkflowTask
from ... import workflow
@workflow
def install(ctx, graph):
- tasks_and_node_instances = []
- for node_instance in ctx.model.node_instance.iter():
- tasks_and_node_instances.append((
- WorkflowTask(install_node_instance, node_instance=node_instance),
- node_instance))
- graph.add_tasks([task for task, _ in tasks_and_node_instances])
- create_node_instance_task_dependencies(graph, tasks_and_node_instances)
+ tasks_and_nodes = []
+ for node in ctx.nodes:
+ tasks_and_nodes.append((WorkflowTask(install_node, node=node), node))
+ graph.add_tasks([task for task, _ in tasks_and_nodes])
+ create_node_task_dependencies(graph, tasks_and_nodes)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/start.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/start.py b/aria/orchestrator/workflows/builtin/start.py
index a044d73..ad67554 100644
--- a/aria/orchestrator/workflows/builtin/start.py
+++ b/aria/orchestrator/workflows/builtin/start.py
@@ -17,12 +17,12 @@
Builtin start workflow
"""
-from .workflows import start_node_instance
+from .workflows import start_node
from ..api.task import WorkflowTask
from ... import workflow
@workflow
def start(ctx, graph):
- for node_instance in ctx.model.node_instance.iter():
- graph.add_tasks(WorkflowTask(start_node_instance, node_instance=node_instance))
+ for node in ctx.model.node.iter():
+ graph.add_tasks(WorkflowTask(start_node, node=node))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/stop.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/stop.py b/aria/orchestrator/workflows/builtin/stop.py
index 584c0d3..23ac366 100644
--- a/aria/orchestrator/workflows/builtin/stop.py
+++ b/aria/orchestrator/workflows/builtin/stop.py
@@ -17,12 +17,12 @@
Builtin stop workflow
"""
-from .workflows import stop_node_instance
+from .workflows import stop_node
from ..api.task import WorkflowTask
from ... import workflow
@workflow
def stop(ctx, graph):
- for node_instance in ctx.model.node_instance.iter():
- graph.add_tasks(WorkflowTask(stop_node_instance, node_instance=node_instance))
+ for node in ctx.model.node.iter():
+ graph.add_tasks(WorkflowTask(stop_node, node=node))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/uninstall.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/uninstall.py b/aria/orchestrator/workflows/builtin/uninstall.py
index bfcc9ec..e4afcd9 100644
--- a/aria/orchestrator/workflows/builtin/uninstall.py
+++ b/aria/orchestrator/workflows/builtin/uninstall.py
@@ -17,18 +17,18 @@
Builtin uninstall workflow
"""
-from .workflows import uninstall_node_instance
-from .utils import create_node_instance_task_dependencies
+from .workflows import uninstall_node
+from .utils import create_node_task_dependencies
from ..api.task import WorkflowTask
from ... import workflow
@workflow
def uninstall(ctx, graph):
- tasks_and_node_instances = []
- for node_instance in ctx.model.node_instance.iter():
- tasks_and_node_instances.append((
- WorkflowTask(uninstall_node_instance, node_instance=node_instance),
- node_instance))
- graph.add_tasks([task for task, _ in tasks_and_node_instances])
- create_node_instance_task_dependencies(graph, tasks_and_node_instances, reverse=True)
+ tasks_and_nodes = []
+ for node in ctx.nodes:
+ tasks_and_nodes.append((
+ WorkflowTask(uninstall_node, node=node),
+ node))
+ graph.add_tasks([task for task, _ in tasks_and_nodes])
+ create_node_task_dependencies(graph, tasks_and_nodes, reverse=True)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/utils.py b/aria/orchestrator/workflows/builtin/utils.py
index 8b7a8bc..c9dbc6b 100644
--- a/aria/orchestrator/workflows/builtin/utils.py
+++ b/aria/orchestrator/workflows/builtin/utils.py
@@ -16,50 +16,49 @@
from ..api.task import OperationTask
-def create_node_instance_task(operation_name, node_instance):
+def create_node_task(operation_name, node):
"""
- Returns a new operation task if the operation exists in the node instance, otherwise returns
- None.
+ Returns a new operation task if the operation exists in the node, otherwise returns None.
"""
- if operation_name in node_instance.node.operations:
- return OperationTask.node_instance(instance=node_instance,
- name=operation_name)
+ if _has_operation(node.interfaces, operation_name):
+ return OperationTask.node(instance=node,
+ name=operation_name)
return None
-def create_relationship_instance_tasks(operation_name, operations_attr, node_instance):
+def create_relationship_tasks(operation_name, runs_on, node):
"""
- Returns a list of operation tasks for each outbound relationship of the node instance if
- the operation exists there.
+ Returns a list of operation tasks for each outbound relationship of the node if the operation
+ exists there.
"""
sequence = []
- for relationship_instance in node_instance.outbound_relationship_instances:
- if operation_name in getattr(relationship_instance.relationship, operations_attr):
+ for relationship in node.outbound_relationships:
+ if _has_operation(relationship.interfaces, operation_name):
sequence.append(
- OperationTask.relationship_instance(instance=relationship_instance,
- name=operation_name,
- operation_end=operations_attr))
+ OperationTask.relationship(instance=relationship,
+ name=operation_name,
+ edge='source',
+ runs_on=runs_on))
return sequence
-def create_node_instance_task_dependencies(graph, tasks_and_node_instances, reverse=False):
+def create_node_task_dependencies(graph, tasks_and_nodes, reverse=False):
"""
- Creates dependencies between tasks if there is an outbound relationship between their node
- instances.
+ Creates dependencies between tasks if there is a relationship (outbound) between their nodes.
"""
- def get_task(node_instance_id):
- for task, node_instance in tasks_and_node_instances:
- if node_instance.id == node_instance_id:
+ def get_task(node_id):
+ for task, node in tasks_and_nodes:
+ if node.id == node_id:
return task
return None
- for task, node_instance in tasks_and_node_instances:
+ for task, node in tasks_and_nodes:
dependencies = []
- for relationship_instance in node_instance.outbound_relationship_instances:
- dependency = get_task(relationship_instance.target_node_instance.id)
+ for relationship in node.outbound_relationships:
+ dependency = get_task(relationship.target_node.id)
if dependency:
dependencies.append(dependency)
if dependencies:
@@ -68,3 +67,10 @@ def create_node_instance_task_dependencies(graph, tasks_and_node_instances, reve
graph.add_dependency(dependency, task)
else:
graph.add_dependency(task, dependencies)
+
+
+def _has_operation(interfaces, operation_name):
+ for interface in interfaces:
+ if interface.operations.filter_by(name=operation_name).count() == 1:
+ return True
+ return False
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/builtin/workflows.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py
index c2fbded..180b4e9 100644
--- a/aria/orchestrator/workflows/builtin/workflows.py
+++ b/aria/orchestrator/workflows/builtin/workflows.py
@@ -17,122 +17,157 @@
A set of builtin workflows.
"""
-from .utils import (create_node_instance_task, create_relationship_instance_tasks)
+from .utils import (create_node_task, create_relationship_tasks)
from ... import workflow
+NORMATIVE_STANDARD_INTERFACE = 'Standard' # 'tosca.interfaces.node.lifecycle.Standard'
+NORMATIVE_CONFIGURE_INTERFACE = 'Configure' # 'tosca.interfaces.relationship.Configure'
+
+NORMATIVE_CREATE = NORMATIVE_STANDARD_INTERFACE + '.create'
+NORMATIVE_START = NORMATIVE_STANDARD_INTERFACE + '.start'
+NORMATIVE_STOP = NORMATIVE_STANDARD_INTERFACE + '.stop'
+NORMATIVE_DELETE = NORMATIVE_STANDARD_INTERFACE + '.delete'
+
+NORMATIVE_CONFIGURE = NORMATIVE_STANDARD_INTERFACE + '.configure'
+NORMATIVE_PRE_CONFIGURE_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.pre_configure_source'
+NORMATIVE_PRE_CONFIGURE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.pre_configure_target'
+NORMATIVE_POST_CONFIGURE_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.post_configure_source'
+NORMATIVE_POST_CONFIGURE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.post_configure_target'
+
+NORMATIVE_ADD_SOURCE = NORMATIVE_CONFIGURE_INTERFACE + '.add_source'
+NORMATIVE_ADD_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.add_target'
+NORMATIVE_REMOVE_TARGET = NORMATIVE_CONFIGURE_INTERFACE + '.remove_target'
+NORMATIVE_TARGET_CHANGED = NORMATIVE_CONFIGURE_INTERFACE + '.target_changed'
+
+
__all__ = (
- 'install_node_instance',
- 'uninstall_node_instance',
- 'start_node_instance',
- 'stop_node_instance',
+ 'NORMATIVE_STANDARD_INTERFACE',
+ 'NORMATIVE_CONFIGURE_INTERFACE',
+ 'NORMATIVE_CREATE',
+ 'NORMATIVE_START',
+ 'NORMATIVE_STOP',
+ 'NORMATIVE_DELETE',
+ 'NORMATIVE_CONFIGURE',
+ 'NORMATIVE_PRE_CONFIGURE_SOURCE',
+ 'NORMATIVE_PRE_CONFIGURE_TARGET',
+ 'NORMATIVE_POST_CONFIGURE_SOURCE',
+ 'NORMATIVE_POST_CONFIGURE_TARGET',
+ 'NORMATIVE_ADD_SOURCE',
+ 'NORMATIVE_ADD_TARGET',
+ 'NORMATIVE_REMOVE_TARGET',
+ 'NORMATIVE_TARGET_CHANGED',
+ 'install_node',
+ 'uninstall_node',
+ 'start_node',
+ 'stop_node',
)
-@workflow(suffix_template='{node_instance.id}')
-def install_node_instance(graph, node_instance, **kwargs):
+@workflow(suffix_template='{node.id}')
+def install_node(graph, node, **kwargs):
sequence = []
# Create
sequence.append(
- create_node_instance_task(
- 'tosca.interfaces.node.lifecycle.Standard.create',
- node_instance))
+ create_node_task(
+ NORMATIVE_CREATE,
+ node))
# Configure
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.pre_configure_source',
- 'source_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_PRE_CONFIGURE_SOURCE,
+ 'source',
+ node)
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.pre_configure_target',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_PRE_CONFIGURE_TARGET,
+ 'target',
+ node)
sequence.append(
- create_node_instance_task(
- 'tosca.interfaces.node.lifecycle.Standard.configure',
- node_instance))
+ create_node_task(
+ NORMATIVE_CONFIGURE,
+ node))
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.post_configure_source',
- 'source_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_POST_CONFIGURE_SOURCE,
+ 'source',
+ node)
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.post_configure_target',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_POST_CONFIGURE_TARGET,
+ 'target',
+ node)
# Start
- sequence += _create_start_tasks(node_instance)
+ sequence += _create_start_tasks(node)
graph.sequence(*sequence)
-@workflow(suffix_template='{node_instance.id}')
-def uninstall_node_instance(graph, node_instance, **kwargs):
+@workflow(suffix_template='{node.id}')
+def uninstall_node(graph, node, **kwargs):
# Stop
- sequence = _create_stop_tasks(node_instance)
+ sequence = _create_stop_tasks(node)
# Delete
sequence.append(
- create_node_instance_task(
- 'tosca.interfaces.node.lifecycle.Standard.delete',
- node_instance))
+ create_node_task(
+ NORMATIVE_DELETE,
+ node))
graph.sequence(*sequence)
-@workflow(suffix_template='{node_instance.id}')
-def start_node_instance(graph, node_instance, **kwargs):
- graph.sequence(*_create_start_tasks(node_instance))
+@workflow(suffix_template='{node.id}')
+def start_node(graph, node, **kwargs):
+ graph.sequence(*_create_start_tasks(node))
-@workflow(suffix_template='{node_instance.id}')
-def stop_node_instance(graph, node_instance, **kwargs):
- graph.sequence(*_create_stop_tasks(node_instance))
+@workflow(suffix_template='{node.id}')
+def stop_node(graph, node, **kwargs):
+ graph.sequence(*_create_stop_tasks(node))
-def _create_start_tasks(node_instance):
+def _create_start_tasks(node):
sequence = []
sequence.append(
- create_node_instance_task(
- 'tosca.interfaces.node.lifecycle.Standard.start',
- node_instance))
+ create_node_task(
+ NORMATIVE_START,
+ node))
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.add_source',
- 'source_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_ADD_SOURCE,
+ 'source',
+ node)
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.add_target',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_ADD_TARGET,
+ 'target',
+ node)
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.target_changed',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_TARGET_CHANGED,
+ 'target',
+ node)
return sequence
-def _create_stop_tasks(node_instance):
+def _create_stop_tasks(node):
sequence = []
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.remove_target',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_REMOVE_TARGET,
+ 'target',
+ node)
sequence += \
- create_relationship_instance_tasks(
- 'tosca.interfaces.relationship.Configure.target_changed',
- 'target_operations',
- node_instance)
+ create_relationship_tasks(
+ NORMATIVE_TARGET_CHANGED,
+ 'target',
+ node)
sequence.append(
- create_node_instance_task(
- 'tosca.interfaces.node.lifecycle.Standard.stop',
- node_instance))
+ create_node_task(
+ NORMATIVE_STOP,
+ node))
return sequence
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/core/engine.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index fd83614..55b4159 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -23,7 +23,7 @@ from datetime import datetime
import networkx
from aria import logger
-from aria.storage import model
+from aria.storage.modeling import model
from aria.orchestrator import events
from .. import exceptions
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/core/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py
index f65fc0d..d0e1363 100644
--- a/aria/orchestrator/workflows/core/task.py
+++ b/aria/orchestrator/workflows/core/task.py
@@ -24,7 +24,7 @@ from functools import (
)
from aria import logger
-from aria.storage import model
+from aria.storage.modeling import model
from aria.orchestrator.context import operation as operation_context
from .. import exceptions
@@ -109,11 +109,11 @@ class OperationTask(BaseTask):
model_storage = api_task._workflow_context.model
base_task_model = model_storage.task.model_cls
- if isinstance(api_task.actor, model.NodeInstance):
- context_class = operation_context.NodeOperationContext
+ if isinstance(api_task.actor, model.Node):
+ context_cls = operation_context.NodeOperationContext
task_model_cls = base_task_model.as_node_instance
- elif isinstance(api_task.actor, model.RelationshipInstance):
- context_class = operation_context.RelationshipOperationContext
+ elif isinstance(api_task.actor, model.Relationship):
+ context_cls = operation_context.RelationshipOperationContext
task_model_cls = base_task_model.as_relationship_instance
else:
raise RuntimeError('No operation context could be created for {actor.model_cls}'
@@ -127,7 +127,7 @@ class OperationTask(BaseTask):
# package_name and package_version
operation_task = task_model_cls(
name=api_task.name,
- operation_mapping=api_task.operation_mapping,
+ implementation=api_task.implementation,
instance=api_task.actor,
inputs=api_task.inputs,
status=base_task_model.PENDING,
@@ -141,13 +141,13 @@ class OperationTask(BaseTask):
)
self._workflow_context.model.task.put(operation_task)
- self._ctx = context_class(name=api_task.name,
- model_storage=self._workflow_context.model,
- resource_storage=self._workflow_context.resource,
- deployment_id=self._workflow_context._deployment_id,
- task_id=operation_task.id,
- actor_id=api_task.actor.id,
- workdir=self._workflow_context._workdir)
+ self._ctx = context_cls(name=api_task.name,
+ model_storage=self._workflow_context.model,
+ resource_storage=self._workflow_context.resource,
+ service_instance_id=self._workflow_context._service_instance_id,
+ task_id=operation_task.id,
+ actor_id=api_task.actor.id,
+ workdir=self._workflow_context._workdir)
self._task_id = operation_task.id
self._update_fields = None
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index c4b8ba1..560ac43 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -19,8 +19,8 @@ Subprocess based executor
# pylint: disable=wrong-import-position
-import sys
import os
+import sys
# As part of the process executor implementation, subprocess are started with this module as their
# entry point. We thus remove this module's directory from the python path if it happens to be
@@ -47,7 +47,7 @@ from aria.utils import imports
from aria.utils import exceptions
from aria.orchestrator.workflows.executor import base
from aria.storage import instrumentation
-from aria.storage import type as storage_type
+from aria.storage.modeling import type as storage_type
_IS_WIN = os.name == 'nt'
@@ -190,7 +190,7 @@ class ProcessExecutor(base.BaseExecutor):
def _create_arguments_dict(self, task):
return {
'task_id': task.id,
- 'operation_mapping': task.operation_mapping,
+ 'implementation': task.implementation,
'operation_inputs': task.inputs,
'port': self._server_port,
'context': task.context.serialization_dict,
@@ -281,9 +281,9 @@ def _patch_session(ctx, messenger, instrument):
if not ctx.model:
return
- # We arbitrarily select the ``node_instance`` mapi to extract the session from it.
+ # We arbitrarily select the ``node`` mapi to extract the session from it.
# could have been any other mapi just as well
- session = ctx.model.node_instance._session
+ session = ctx.model.node._session
original_refresh = session.refresh
def patched_refresh(target):
@@ -317,7 +317,7 @@ def _main():
messenger = _Messenger(task_id=task_id, port=port)
messenger.started()
- operation_mapping = arguments['operation_mapping']
+ implementation = arguments['implementation']
operation_inputs = arguments['operation_inputs']
context_dict = arguments['context']
@@ -329,7 +329,7 @@ def _main():
try:
ctx = context_dict['context_cls'].deserialize_from_dict(**context_dict['context'])
_patch_session(ctx=ctx, messenger=messenger, instrument=instrument)
- task_func = imports.load_attribute(operation_mapping)
+ task_func = imports.load_attribute(implementation)
aria.install_aria_extensions()
for decorate in process_executor.decorate():
task_func = decorate(task_func)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/orchestrator/workflows/executor/thread.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/thread.py b/aria/orchestrator/workflows/executor/thread.py
index 1a6ad9f..7ae0217 100644
--- a/aria/orchestrator/workflows/executor/thread.py
+++ b/aria/orchestrator/workflows/executor/thread.py
@@ -57,7 +57,7 @@ class ThreadExecutor(BaseExecutor):
task = self._queue.get(timeout=1)
self._task_started(task)
try:
- task_func = imports.load_attribute(task.operation_mapping)
+ task_func = imports.load_attribute(task.implementation)
task_func(ctx=task.context, **task.inputs)
self._task_succeeded(task)
except BaseException as e:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/__init__.py b/aria/parser/modeling/__init__.py
index a1efd9f..cad25ca 100644
--- a/aria/parser/modeling/__init__.py
+++ b/aria/parser/modeling/__init__.py
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from .exceptions import CannotEvaluateFunctionException
from .context import IdType, ModelingContext
from .elements import Element, ModelElement, Function, Parameter, Metadata
from .instance_elements import (ServiceInstance, Node, Capability, Relationship, Artifact, Group,
@@ -24,10 +23,10 @@ from .model_elements import (ServiceModel, NodeTemplate, RequirementTemplate, Ca
GroupPolicyTemplate, GroupPolicyTriggerTemplate, MappingTemplate,
SubstitutionTemplate, InterfaceTemplate, OperationTemplate)
from .types import TypeHierarchy, Type, RelationshipType, PolicyType, PolicyTriggerType
-from .storage import initialize_storage
+from .exceptions import CannotEvaluateFunctionException
+
__all__ = (
- 'CannotEvaluateFunctionException',
'IdType',
'ModelingContext',
'Element',
@@ -67,4 +66,5 @@ __all__ = (
'RelationshipType',
'PolicyType',
'PolicyTriggerType',
- 'initialize_storage')
+ 'CannotEvaluateFunctionException',
+)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/storage.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/storage.py b/aria/parser/modeling/storage.py
index 46c3a7c..ff1e536 100644
--- a/aria/parser/modeling/storage.py
+++ b/aria/parser/modeling/storage.py
@@ -13,167 +13,129 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+"""
+This solution is temporary, as we plan to combine aria.parser.modeling and aria.storage.modeling
+into one package (aria.modeling?).
+"""
+
from datetime import datetime
from threading import RLock
-from ...storage import model
-from ...orchestrator import operation
+from ...storage.modeling import model
+from ...orchestrator.decorators import operation
from ...utils.console import puts, Colored
from ...utils.formatting import safe_repr
-def initialize_storage(context, model_storage, deployment_id):
- blueprint = create_blueprint(context)
- model_storage.blueprint.put(blueprint)
-
- deployment = create_deployment(context, blueprint, deployment_id)
- model_storage.deployment.put(deployment)
-
- # Create nodes and node instances
- for node_template in context.modeling.model.node_templates.itervalues():
- node = create_node(context, deployment, node_template)
- model_storage.node.put(node)
+def initialize_storage(context, model_storage, service_instance_id):
+ s_service_template = create_service_template(context)
+ model_storage.service_template.put(s_service_template)
- for a_node in context.modeling.instance.find_nodes(node_template.name):
- node_instance = create_node_instance(node, a_node)
- model_storage.node_instance.put(node_instance)
+ s_service_instance = create_service_instance(context, s_service_template, service_instance_id)
+ model_storage.service_instance.put(s_service_instance)
- # Create relationships
+ # Create node templates and nodes
for node_template in context.modeling.model.node_templates.itervalues():
- for index, requirement_template in enumerate(node_template.requirement_templates):
- # We are currently limited only to requirements for specific node templates!
- if requirement_template.target_node_template_name:
- source = model_storage.node.get_by_name(node_template.name)
- target = model_storage.node.get_by_name(
- requirement_template.target_node_template_name)
- relationship = create_relationship(context, source, target,
- requirement_template.relationship_template)
- model_storage.relationship.put(relationship)
-
- for node in context.modeling.instance.find_nodes(node_template.name):
- for relationship_model in node.relationships:
- if relationship_model.source_requirement_index == index:
- source_instance = \
- model_storage.node_instance.get_by_name(node.id)
- target_instance = \
- model_storage.node_instance.get_by_name(
- relationship_model.target_node_id)
- relationship_instance = \
- create_relationship_instance(relationship, source_instance,
- target_instance)
- model_storage.relationship_instance.put(relationship_instance)
-
-
-def create_blueprint(context):
+ s_node_template = create_node_template(s_service_template, node_template)
+ model_storage.node_template.put(s_node_template)
+
+ for node in context.modeling.instance.find_nodes(node_template.name):
+ s_node = create_node(s_service_instance, s_node_template, node)
+ model_storage.node.put(s_node)
+ create_interfaces(context, model_storage, node.interfaces,
+ s_node, 'node', None, '_dry_node')
+
+ # Create relationships between nodes
+ for source_node in context.modeling.instance.nodes.itervalues():
+ for relationship in source_node.relationships:
+ s_source_node = model_storage.node.get_by_name(source_node.id)
+ s_target_node = model_storage.node.get_by_name(relationship.target_node_id)
+ s_relationship = create_relationship(s_source_node, s_target_node)
+ model_storage.relationship.put(s_relationship)
+ # TOSCA always uses the "source" edge
+ create_interfaces(context, model_storage, relationship.source_interfaces,
+ s_relationship, 'relationship', 'source', '_dry_relationship')
+
+
+def create_service_template(context):
now = datetime.utcnow()
main_file_name = unicode(context.presentation.location)
try:
name = context.modeling.model.metadata.values.get('template_name')
except AttributeError:
name = None
- return model.Blueprint(
- plan={},
+ return model.ServiceTemplate(
name=name or main_file_name,
description=context.modeling.model.description or '',
created_at=now,
updated_at=now,
- main_file_name=main_file_name)
+ main_file_name=main_file_name,
+ plan={}
+ )
-def create_deployment(context, blueprint, deployment_id):
+def create_service_instance(context, service_template, service_instance_id):
now = datetime.utcnow()
- return model.Deployment(
- name='%s_%s' % (blueprint.name, deployment_id),
- blueprint_fk=blueprint.id,
+ return model.ServiceInstance(
+ name='{0}_{1}'.format(service_template.name, service_instance_id),
+ service_template=service_template,
description=context.modeling.instance.description or '',
created_at=now,
- updated_at=now,
- workflows={},
- inputs={},
- groups={},
- permalink='',
- policy_triggers={},
- policy_types={},
- outputs={},
- scaling_groups={})
-
-
-def create_node(context, deployment, node_template):
- operations = create_operations(context, node_template.interface_templates, '_dry_node')
- return model.Node(
- name=node_template.name,
- type=node_template.type_name,
- type_hierarchy=[],
- number_of_instances=node_template.default_instances,
- planned_number_of_instances=node_template.default_instances,
- deploy_number_of_instances=node_template.default_instances,
- properties={},
- operations=operations,
- min_number_of_instances=node_template.min_instances,
- max_number_of_instances=node_template.max_instances or 100,
- deployment_fk=deployment.id)
-
-
-def create_relationship(context, source, target, relationship_template):
- if relationship_template:
- source_operations = create_operations(context,
- relationship_template.source_interface_templates,
- '_dry_relationship')
- target_operations = create_operations(context,
- relationship_template.target_interface_templates,
- '_dry_relationship')
- else:
- source_operations = {}
- target_operations = {}
- return model.Relationship(
- source_node_fk=source.id,
- target_node_fk=target.id,
- source_interfaces={},
- source_operations=source_operations,
- target_interfaces={},
- target_operations=target_operations,
- type='rel_type',
- type_hierarchy=[],
- properties={})
-
-
-def create_node_instance(node, node_model):
- return model.NodeInstance(
- name=node_model.id,
- runtime_properties={},
- version=None,
- node_fk=node.id,
- state='',
- scaling_groups=[])
+ updated_at=now)
-def create_relationship_instance(relationship, source_instance, target_instance):
- return model.RelationshipInstance(
- relationship_fk=relationship.id,
- source_node_instance_fk=source_instance.id,
- target_node_instance_fk=target_instance.id)
+def create_node_template(service_template, node_template):
+ return model.NodeTemplate(
+ name=node_template.name,
+ type_name=node_template.type_name,
+ default_instances=node_template.default_instances,
+ min_instances=node_template.min_instances,
+ max_instances=node_template.max_instances or 100,
+ service_template=service_template)
-def create_operations(context, interfaces, fn_name):
- operations = {}
- for interface in interfaces.itervalues():
- operations[interface.type_name] = {}
- for oper in interface.operation_templates.itervalues():
- name = '%s.%s' % (interface.type_name, oper.name)
- operations[name] = {
- 'operation': '%s.%s' % (__name__, fn_name),
- 'inputs': {
- '_plugin': None,
- '_implementation': None}}
- if oper.implementation:
- plugin, implementation = _parse_implementation(context, oper.implementation)
- operations[name]['inputs']['_plugin'] = plugin
- operations[name]['inputs']['_implementation'] = implementation
+def create_node(service_instance, node_template, node):
+ return model.Node(
+ name=node.id,
+ state='',
+ node_template=node_template,
+ service_instance=service_instance)
- return operations
+
+def create_relationship(source_node, target_node):
+ return model.Relationship(
+ source_node=source_node,
+ target_node=target_node)
+
+
+def create_interfaces(context, model_storage, interfaces, node_or_relationship, type_name, edge,
+ fn_name):
+ for interface_name, interface in interfaces.iteritems():
+ s_interface = model.Interface(name=interface_name,
+ type_name=interface.type_name,
+ edge=edge)
+ setattr(s_interface, type_name, node_or_relationship)
+ model_storage.interface.put(s_interface)
+ for operation_name, oper in interface.operations.iteritems():
+ operation_name = '{0}.{1}'.format(interface_name, operation_name)
+ s_operation = model.Operation(name=operation_name,
+ implementation='{0}.{1}'.format(__name__, fn_name),
+ interface=s_interface)
+ plugin, implementation = _parse_implementation(context, oper.implementation)
+ # TODO: operation's user inputs
+ s_operation.inputs.append(model.Parameter(name='_plugin', # pylint: disable=no-member
+ str_value=str(plugin),
+ type='str'))
+ s_operation.inputs.append(model.Parameter(name='_implementation', # pylint: disable=no-member
+ str_value=str(implementation),
+ type='str'))
+ model_storage.operation.put(s_operation)
def _parse_implementation(context, implementation):
+ if not implementation:
+ return '', ''
+
index = implementation.find('>')
if index == -1:
return 'execution', implementation
@@ -204,7 +166,7 @@ _TERMINAL_LOCK = RLock()
@operation
def _dry_node(ctx, _plugin, _implementation, **kwargs):
with _TERMINAL_LOCK:
- print '> node instance: %s' % Colored.red(ctx.node_instance.name)
+ print '> node instance: %s' % Colored.red(ctx.node.name)
_dump_implementation(_plugin, _implementation)
@@ -212,8 +174,8 @@ def _dry_node(ctx, _plugin, _implementation, **kwargs):
def _dry_relationship(ctx, _plugin, _implementation, **kwargs):
with _TERMINAL_LOCK:
puts('> relationship instance: %s -> %s' % (
- Colored.red(ctx.relationship_instance.source_node_instance.name),
- Colored.red(ctx.relationship_instance.target_node_instance.name)))
+ Colored.red(ctx.relationship.source_node.name),
+ Colored.red(ctx.relationship.target_node.name)))
_dump_implementation(_plugin, _implementation)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/parser/modeling/utils.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/utils.py b/aria/parser/modeling/utils.py
index 906106e..21db433 100644
--- a/aria/parser/modeling/utils.py
+++ b/aria/parser/modeling/utils.py
@@ -57,7 +57,7 @@ def coerce_value(context, container, value, report_issues=False):
return [coerce_value(context, container, v, report_issues) for v in value]
elif isinstance(value, dict):
return OrderedDict((k, coerce_value(context, container, v, report_issues))
- for k, v in value.iteritems())
+ for k, v in value.items())
elif hasattr(value, '_evaluate'):
try:
value = value._evaluate(context, container)
@@ -73,7 +73,7 @@ def coerce_value(context, container, value, report_issues=False):
def validate_dict_values(context, the_dict):
if not the_dict:
return
- validate_list_values(context, the_dict.itervalues())
+ validate_list_values(context, the_dict.values())
def validate_list_values(context, the_list):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/__init__.py b/aria/storage/__init__.py
index a1c07d7..eaadc7e 100644
--- a/aria/storage/__init__.py
+++ b/aria/storage/__init__.py
@@ -42,14 +42,18 @@ from .core import (
ModelStorage,
ResourceStorage,
)
+from .modeling import (
+ structure,
+ model,
+ model_base,
+ type
+)
from . import (
exceptions,
api,
- structure,
core,
filesystem_rapi,
sql_mapi,
- model
)
__all__ = (
@@ -60,5 +64,8 @@ __all__ = (
'ResourceStorage',
'filesystem_rapi',
'sql_mapi',
- 'api'
+ 'api',
+ 'model',
+ 'model_base',
+ 'type',
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/b6193359/aria/storage/api.py
----------------------------------------------------------------------
diff --git a/aria/storage/api.py b/aria/storage/api.py
index f6da6de..09a4dd9 100644
--- a/aria/storage/api.py
+++ b/aria/storage/api.py
@@ -44,7 +44,7 @@ class ModelAPI(StorageAPI):
"""
super(ModelAPI, self).__init__(**kwargs)
self._model_cls = model_cls
- self._name = name or generate_lower_name(model_cls)
+ self._name = name or model_cls.__modelname__
@property
def name(self):
@@ -178,6 +178,4 @@ def generate_lower_name(model_cls):
:return: lower name
:rtype: basestring
"""
- return ''.join(
- character if character.islower() else '_{0}'.format(character.lower())
- for character in model_cls.__name__)[1:]
+ return getattr(model_cls, '__mapiname__', model_cls.__tablename__)