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 2016/12/22 16:49:47 UTC
[1/2] incubator-ariatosca git commit:
ARIA-39-Genericize-storage-models
Repository: incubator-ariatosca
Updated Branches:
refs/heads/master d143772d1 -> c9ecc54b9
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/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 baded7f..a6b55ba 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 models
+from aria.storage import model
from aria.orchestrator.workflows import (
api,
exceptions,
@@ -146,7 +146,7 @@ class TestEngine(BaseTest):
execution = workflow_context.execution
assert execution.started_at <= execution.ended_at <= datetime.utcnow()
assert execution.error is None
- assert execution.status == models.Execution.TERMINATED
+ assert execution.status == model.Execution.TERMINATED
def test_single_task_successful_execution(self, workflow_context, executor):
@workflow
@@ -175,7 +175,7 @@ class TestEngine(BaseTest):
execution = workflow_context.execution
assert execution.started_at <= execution.ended_at <= datetime.utcnow()
assert execution.error is not None
- assert execution.status == models.Execution.FAILED
+ assert execution.status == model.Execution.FAILED
def test_two_tasks_execution_order(self, workflow_context, executor):
@workflow
@@ -236,7 +236,7 @@ class TestCancel(BaseTest):
execution = workflow_context.execution
assert execution.started_at <= execution.ended_at <= datetime.utcnow()
assert execution.error is None
- assert execution.status == models.Execution.CANCELLED
+ assert execution.status == model.Execution.CANCELLED
def test_cancel_pending_execution(self, workflow_context, executor):
@workflow
@@ -247,7 +247,7 @@ class TestCancel(BaseTest):
executor=executor)
eng.cancel_execution()
execution = workflow_context.execution
- assert execution.status == models.Execution.CANCELLED
+ assert execution.status == model.Execution.CANCELLED
class TestRetries(BaseTest):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/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 fc11548..5381f5d 100644
--- a/tests/orchestrator/workflows/core/test_task.py
+++ b/tests/orchestrator/workflows/core/test_task.py
@@ -60,7 +60,7 @@ class TestOperationTask(object):
node.operations['aria.interfaces.lifecycle.create'] = {'plugin': 'plugin1'}
api_task, core_task = self._create_operation_task(ctx, node_instance)
storage_task = ctx.model.task.get_by_name(core_task.name)
- assert storage_task.execution_id == ctx.execution.id
+ assert storage_task.execution_name == ctx.execution.name
assert core_task.model_task == storage_task
assert core_task.name == api_task.name
assert core_task.operation_mapping == api_task.operation_mapping
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/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 5ded4fb..cd00cd5 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 models
+from aria.storage import model
from aria.orchestrator import events
from aria.orchestrator.workflows.executor import (
thread,
@@ -91,7 +91,7 @@ class MockContext(object):
class MockTask(object):
- INFINITE_RETRIES = models.Task.INFINITE_RETRIES
+ INFINITE_RETRIES = model.Task.INFINITE_RETRIES
def __init__(self, func, inputs=None):
self.states = []
@@ -106,9 +106,9 @@ class MockTask(object):
self.context = MockContext()
self.retry_count = 0
self.max_attempts = 1
- self.plugin_id = None
+ self.plugin_fk = None
- for state in models.Task.STATES:
+ for state in model.Task.STATES:
setattr(self, state.upper(), state)
@contextmanager
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/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 0098f30..e321388 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor.py
@@ -22,7 +22,7 @@ from contextlib import contextmanager
import pytest
from aria import application_model_storage
-from aria.storage import models
+from aria.storage import model as aria_model
from aria.utils.plugin import create as create_plugin
from aria.storage.sql_mapi import SQLAlchemyModelAPI
from aria.orchestrator import events
@@ -117,7 +117,7 @@ class MockContext(object):
class MockTask(object):
- INFINITE_RETRIES = models.Task.INFINITE_RETRIES
+ INFINITE_RETRIES = aria_model.Task.INFINITE_RETRIES
def __init__(self, plugin, operation):
self.id = str(uuid.uuid4())
@@ -128,10 +128,10 @@ class MockTask(object):
self.context = MockContext()
self.retry_count = 0
self.max_attempts = 1
- self.plugin_id = plugin.id
+ self.plugin_fk = plugin.id
self.plugin = plugin
- for state in models.Task.STATES:
+ for state in aria_model.Task.STATES:
setattr(self, state.upper(), state)
@contextmanager
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index edff982..9101fd0 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -17,14 +17,13 @@ import platform
from tempfile import mkdtemp
from shutil import rmtree
+from aria.storage import model
from sqlalchemy import (
create_engine,
orm)
from sqlalchemy.orm import scoped_session
from sqlalchemy.pool import StaticPool
-from aria.storage import structures
-
class TestFileSystem(object):
@@ -60,7 +59,7 @@ def get_sqlite_api_kwargs(base_dir=None, filename='db.sqlite'):
session_factory = orm.sessionmaker(bind=engine)
session = scoped_session(session_factory=session_factory) if base_dir else session_factory()
- structures.Model.metadata.create_all(engine)
+ model.DeclarativeBase.metadata.create_all(bind=engine)
return dict(engine=engine, session=session)
@@ -77,4 +76,4 @@ def release_sqlite_storage(storage):
session.rollback()
session.close()
for engine in set(mapi._engine for mapi in mapis):
- structures.Model.metadata.drop_all(engine)
+ model.DeclarativeBase.metadata.drop_all(engine)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/tests/storage/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 48cd02c..6f9527e 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -15,64 +15,119 @@
import pytest
+from sqlalchemy import Column, Text, Integer
+
from aria.storage import (
ModelStorage,
- models,
+ model,
exceptions,
sql_mapi,
+ structure,
+ type as aria_type,
)
from aria import application_model_storage
-from tests.storage import get_sqlite_api_kwargs, release_sqlite_storage
+from ..storage import get_sqlite_api_kwargs, release_sqlite_storage
+from ..mock import context as mock_context
+
+
+class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method
+ __tablename__ = 'mock_models'
+ model_dict = Column(aria_type.Dict)
+ model_list = Column(aria_type.List)
+ value = Column(Integer)
+ name = Column(Text)
@pytest.fixture
def storage():
base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, api_kwargs=get_sqlite_api_kwargs())
+ base_storage.register(MockModel)
yield base_storage
release_sqlite_storage(base_storage)
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+ model.DeclarativeBase.metadata.remove(MockModel.__table__) #pylint: disable=no-member
+
+
def test_storage_base(storage):
with pytest.raises(AttributeError):
storage.non_existent_attribute()
def test_model_storage(storage):
- storage.register(models.ProviderContext)
+ mock_model = MockModel(value=0, name='model_name')
+ storage.mock_model.put(mock_model)
- pc = models.ProviderContext(context={}, name='context_name')
- storage.provider_context.put(pc)
+ assert storage.mock_model.get_by_name('model_name') == mock_model
- assert storage.provider_context.get_by_name('context_name') == pc
+ assert [mm_from_storage for mm_from_storage in storage.mock_model.iter()] == [mock_model]
+ assert [mm_from_storage for mm_from_storage in storage.mock_model] == [mock_model]
- assert [pc_from_storage for pc_from_storage in storage.provider_context.iter()] == [pc]
- assert [pc_from_storage for pc_from_storage in storage.provider_context] == [pc]
+ storage.mock_model.delete(mock_model)
+ with pytest.raises(exceptions.StorageError):
+ storage.mock_model.get(mock_model.id)
- new_context = {'update_key': 0}
- pc.context = new_context
- storage.provider_context.update(pc)
- assert storage.provider_context.get(pc.id).context == new_context
- storage.provider_context.delete(pc)
- with pytest.raises(exceptions.StorageError):
- storage.provider_context.get(pc.id)
+def test_inner_dict_update(storage):
+ inner_dict = {'inner_value': 1}
+ mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
+ storage.mock_model.put(mock_model)
-def test_storage_driver(storage):
- storage.register(models.ProviderContext)
+ storage_mm = storage.mock_model.get(mock_model.id)
+ assert storage_mm == mock_model
- pc = models.ProviderContext(context={}, name='context_name')
- storage.registered['provider_context'].put(entry=pc)
+ storage_mm.model_dict['inner_dict']['inner_value'] = 2
+ storage_mm.model_dict['value'] = -1
+ storage.mock_model.update(storage_mm)
+ storage_mm = storage.mock_model.get(storage_mm.id)
- assert storage.registered['provider_context'].get_by_name('context_name') == pc
+ assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
+ assert storage_mm.model_dict['value'] == -1
- assert next(i for i in storage.registered['provider_context'].iter()) == pc
- assert [i for i in storage.provider_context] == [pc]
- storage.registered['provider_context'].delete(pc)
+def test_inner_list_update(storage):
+ mock_model = MockModel(model_list=[0, [1]])
+ storage.mock_model.put(mock_model)
- with pytest.raises(exceptions.StorageError):
- storage.registered['provider_context'].get(pc.id)
+ storage_mm = storage.mock_model.get(mock_model.id)
+ assert storage_mm == mock_model
+
+ storage_mm.model_list[1][0] = 'new_inner_value'
+ storage_mm.model_list[0] = 'new_value'
+ storage.mock_model.update(storage_mm)
+ storage_mm = storage.mock_model.get(storage_mm.id)
+
+ assert storage_mm.model_list[1][0] == 'new_inner_value'
+ assert storage_mm.model_list[0] == 'new_value'
+
+
+def test_model_to_dict():
+ context = mock_context.simple(get_sqlite_api_kwargs())
+ deployment = context.deployment
+ deployment_dict = deployment.to_dict()
+
+ expected_keys = [
+ 'created_at',
+ 'description',
+ 'inputs',
+ 'groups',
+ '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
def test_application_storage_factory():
@@ -87,6 +142,5 @@ def test_application_storage_factory():
assert storage.deployment_update_step
assert storage.deployment_modification
assert storage.execution
- assert storage.provider_context
release_sqlite_storage(storage)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/tests/storage/test_models.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_models.py b/tests/storage/test_models.py
index 0651957..df81f95 100644
--- a/tests/storage/test_models.py
+++ b/tests/storage/test_models.py
@@ -12,19 +12,21 @@
# 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 contextlib import contextmanager
from datetime import datetime
+from contextlib import contextmanager
+
import pytest
from aria import application_model_storage
-from aria.storage import exceptions
-from aria.storage import sql_mapi
-from aria.storage.models import (
+from aria.storage import (
+ exceptions,
+ sql_mapi,
+)
+from aria.storage.model import (
DeploymentUpdateStep,
Blueprint,
Execution,
Task,
- ProviderContext,
Plugin,
Deployment,
Node,
@@ -73,7 +75,7 @@ def _deployment_storage():
def _deployment_update_storage():
storage = _deployment_storage()
deployment_update = DeploymentUpdate(
- deployment_id=storage.deployment.list()[0].id,
+ deployment=storage.deployment.list()[0],
created_at=now,
deployment_plan={},
)
@@ -194,15 +196,15 @@ class TestBlueprint(object):
)
def test_blueprint_model_creation(self, empty_storage, is_valid, plan, description, created_at,
updated_at, main_file_name):
- if not is_valid:
- with pytest.raises(exceptions.StorageError):
- empty_storage.blueprint.put(Blueprint(plan=plan, description=description,
- created_at=created_at, updated_at=updated_at,
- main_file_name=main_file_name))
- else:
- empty_storage.blueprint.put(Blueprint(plan=plan, description=description,
- created_at=created_at, updated_at=updated_at,
- main_file_name=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))
class TestDeployment(object):
@@ -249,7 +251,7 @@ class TestDeployment(object):
model_cls=Deployment,
model_kwargs=dict(
name=name,
- blueprint_id=deployment_storage.blueprint.list()[0].id,
+ blueprint=deployment_storage.blueprint.list()[0],
created_at=created_at,
description=description,
inputs=inputs,
@@ -295,8 +297,7 @@ class TestExecution(object):
model_name='execution',
model_cls=Execution,
model_kwargs=dict(
- deployment_id=deployment_storage.deployment.list()[0].id,
- blueprint_id=deployment_storage.blueprint.list()[0].id,
+ deployment=deployment_storage.deployment.list()[0],
created_at=created_at,
started_at=started_at,
ended_at=ended_at,
@@ -380,20 +381,20 @@ class TestDeploymentUpdate(object):
'is_valid, created_at, deployment_plan, deployment_update_node_instances, '
'deployment_update_deployment, deployment_update_nodes, modified_entity_ids, state',
[
- (False, m_cls, {}, {}, {}, {}, {}, 'state'),
- (False, now, m_cls, {}, {}, {}, {}, 'state'),
- (False, now, {}, m_cls, {}, {}, {}, 'state'),
- (False, now, {}, {}, m_cls, {}, {}, 'state'),
+ (False, m_cls, {}, {}, {}, [], {}, 'state'),
+ (False, now, m_cls, {}, {}, [], {}, 'state'),
+ (False, now, {}, m_cls, {}, [], {}, 'state'),
+ (False, now, {}, {}, m_cls, [], {}, 'state'),
(False, now, {}, {}, {}, m_cls, {}, 'state'),
- (False, now, {}, {}, {}, {}, m_cls, 'state'),
- (False, now, {}, {}, {}, {}, {}, m_cls),
+ (False, now, {}, {}, {}, [], m_cls, 'state'),
+ (False, now, {}, {}, {}, [], {}, m_cls),
- (True, now, {}, {}, {}, {}, {}, 'state'),
- (True, now, {}, None, {}, {}, {}, 'state'),
- (True, now, {}, {}, None, {}, {}, 'state'),
+ (True, now, {}, {}, {}, [], {}, 'state'),
+ (True, now, {}, None, {}, [], {}, 'state'),
+ (True, now, {}, {}, None, [], {}, 'state'),
(True, now, {}, {}, {}, None, {}, 'state'),
- (True, now, {}, {}, {}, {}, None, 'state'),
- (True, now, {}, {}, {}, {}, {}, None),
+ (True, now, {}, {}, {}, [], None, 'state'),
+ (True, now, {}, {}, {}, [], {}, None),
]
)
def test_deployment_update_model_creation(self, deployment_storage, is_valid, created_at,
@@ -406,7 +407,7 @@ class TestDeploymentUpdate(object):
model_name='deployment_update',
model_cls=DeploymentUpdate,
model_kwargs=dict(
- deployment_id=deployment_storage.deployment.list()[0].id,
+ deployment=deployment_storage.deployment.list()[0],
created_at=created_at,
deployment_plan=deployment_plan,
deployment_update_node_instances=deployment_update_node_instances,
@@ -441,7 +442,7 @@ class TestDeploymentUpdateStep(object):
model_name='deployment_update_step',
model_cls=DeploymentUpdateStep,
model_kwargs=dict(
- deployment_update_id=deployment_update_storage.deployment_update.list()[0].id,
+ deployment_update=deployment_update_storage.deployment_update.list()[0],
action=action,
entity_id=entity_id,
entity_type=entity_type
@@ -517,7 +518,7 @@ class TestDeploymentModification(object):
model_name='deployment_modification',
model_cls=DeploymentModification,
model_kwargs=dict(
- deployment_id=deployment_storage.deployment.list()[0].id,
+ deployment=deployment_storage.deployment.list()[0],
context=context,
created_at=created_at,
ended_at=ended_at,
@@ -576,7 +577,7 @@ class TestNode(object):
operations=operations,
type=type,
type_hierarchy=type_hierarchy,
- deployment_id=deployment_storage.deployment.list()[0].id
+ deployment=deployment_storage.deployment.list()[0]
))
if is_valid:
assert node.deployment == deployment_storage.deployment.list()[0]
@@ -611,8 +612,8 @@ class TestRelationship(object):
model_name='relationship',
model_cls=Relationship,
model_kwargs=dict(
- source_node_id=nodes_storage.node.list()[1].id,
- target_node_id=nodes_storage.node.list()[0].id,
+ 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,
@@ -630,17 +631,17 @@ class TestNodeInstance(object):
@pytest.mark.parametrize(
'is_valid, name, runtime_properties, scaling_groups, state, version',
[
- (False, m_cls, {}, {}, 'state', 1),
- (False, 'name', m_cls, {}, 'state', 1),
+ (False, m_cls, {}, [], 'state', 1),
+ (False, 'name', m_cls, [], 'state', 1),
(False, 'name', {}, m_cls, 'state', 1),
- (False, 'name', {}, {}, m_cls, 1),
- (False, m_cls, {}, {}, 'state', m_cls),
+ (False, 'name', {}, [], m_cls, 1),
+ (False, m_cls, {}, [], 'state', m_cls),
- (True, 'name', {}, {}, 'state', 1),
- (True, None, {}, {}, 'state', 1),
- (True, 'name', None, {}, 'state', 1),
+ (True, 'name', {}, [], 'state', 1),
+ (True, None, {}, [], 'state', 1),
+ (True, 'name', None, [], 'state', 1),
(True, 'name', {}, None, 'state', 1),
- (True, 'name', {}, {}, 'state', None),
+ (True, 'name', {}, [], 'state', None),
]
)
def test_node_instance_model_creation(self, node_storage, is_valid, name, runtime_properties,
@@ -651,8 +652,7 @@ class TestNodeInstance(object):
model_name='node_instance',
model_cls=NodeInstance,
model_kwargs=dict(
- node_id=node_storage.node.list()[0].id,
- deployment_id=node_storage.deployment.list()[0].id,
+ node=node_storage.node.list()[0],
name=name,
runtime_properties=runtime_properties,
scaling_groups=scaling_groups,
@@ -681,33 +681,15 @@ class TestRelationshipInstance(object):
model_name='relationship_instance',
model_cls=RelationshipInstance,
model_kwargs=dict(
- relationship_id=relationship.id,
- source_node_instance_id=source_node_instance.id,
- target_node_instance_id=target_node_instance.id
+ relationship=relationship,
+ source_node_instance=source_node_instance,
+ target_node_instance=target_node_instance
))
assert relationship_instance.relationship == relationship
assert relationship_instance.source_node_instance == source_node_instance
assert relationship_instance.target_node_instance == target_node_instance
-class TestProviderContext(object):
- @pytest.mark.parametrize(
- 'is_valid, name, context',
- [
- (False, None, {}),
- (False, 'name', None),
- (True, 'name', {}),
- ]
- )
- def test_provider_context_model_creation(self, empty_storage, is_valid, name, context):
- _test_model(is_valid=is_valid,
- storage=empty_storage,
- model_name='provider_context',
- model_cls=ProviderContext,
- model_kwargs=dict(name=name, context=context)
- )
-
-
class TestPlugin(object):
@pytest.mark.parametrize(
'is_valid, archive_name, distribution, distribution_release, '
@@ -715,48 +697,48 @@ class TestPlugin(object):
'package_version, supported_platform, supported_py_versions, uploaded_at, wheels',
[
(False, m_cls, 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', m_cls, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', m_cls, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', m_cls, 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', m_cls, 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', m_cls, 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', m_cls,
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
'pak_ver', m_cls, [], now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', m_cls, now, []),
+ 'pak_ver', 'sup_plat', m_cls, now, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', [], m_cls, []),
+ 'pak_ver', 'sup_plat', [], m_cls, []),
(False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', [], now, m_cls),
+ 'pak_ver', 'sup_plat', [], now, m_cls),
- (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', [], now, []),
+ (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+ 'sup_plat', [], now, []),
(True, 'arc_name', None, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', None, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', None, 'pak_name', 'pak_src', 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', [], now, []),
+ 'pak_ver', 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', None, 'pak_ver',
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', None,
- 'sup_pla', [], now, []),
+ 'sup_plat', [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
'pak_ver', None, [], now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', None, now, []),
+ 'pak_ver', 'sup_plat', None, now, []),
(True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
- 'pak_ver', 'sup_pla', [], now, []),
+ 'pak_ver', 'sup_plat', [], now, []),
]
)
def test_plugin_model_creation(self, empty_storage, is_valid, archive_name, distribution,
@@ -823,7 +805,7 @@ class TestTask(object):
model_cls=Task,
model_kwargs=dict(
status=status,
- execution_id=execution_storage.execution.list()[0].id,
+ execution=execution_storage.execution.list()[0],
due_at=due_at,
started_at=started_at,
ended_at=ended_at,
@@ -834,16 +816,16 @@ class TestTask(object):
name=name,
operation_mapping=operation_mapping,
inputs=inputs,
- plugin_id=plugin_id,
+ plugin_fk=plugin_id,
))
if is_valid:
assert task.execution == execution_storage.execution.list()[0]
- if task.plugin_id:
+ if task.plugin:
assert task.plugin == execution_storage.plugin.list()[0]
def test_task_max_attempts_validation(self):
def create_task(max_attempts):
- Task(execution_id='eid',
+ Task(execution_fk='eid',
name='name',
operation_mapping='',
inputs={},
@@ -855,23 +837,3 @@ class TestTask(object):
create_task(max_attempts=0)
with pytest.raises(ValueError):
create_task(max_attempts=-2)
-
-
-def test_inner_dict_update(empty_storage):
- inner_dict = {'inner_value': 1}
- pc = ProviderContext(name='name', context={
- 'inner_dict': {'inner_value': inner_dict},
- 'value': 0
- })
- empty_storage.provider_context.put(pc)
-
- storage_pc = empty_storage.provider_context.get(pc.id)
- assert storage_pc == pc
-
- storage_pc.context['inner_dict']['inner_value'] = 2
- storage_pc.context['value'] = -1
- empty_storage.provider_context.update(storage_pc)
- storage_pc = empty_storage.provider_context.get(pc.id)
-
- assert storage_pc.context['inner_dict']['inner_value'] == 2
- assert storage_pc.context['value'] == -1
[2/2] incubator-ariatosca git commit:
ARIA-39-Genericize-storage-models
Posted by mx...@apache.org.
ARIA-39-Genericize-storage-models
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/c9ecc54b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/c9ecc54b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/c9ecc54b
Branch: refs/heads/master
Commit: c9ecc54b9f868717d1ef389a72d1bd0061307bfa
Parents: d143772
Author: mxmrlv <mx...@gmail.com>
Authored: Mon Dec 12 00:50:09 2016 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Thu Dec 22 18:44:55 2016 +0200
----------------------------------------------------------------------
aria/__init__.py | 25 +-
aria/orchestrator/context/workflow.py | 10 +-
aria/orchestrator/workflows/api/task.py | 10 +-
aria/orchestrator/workflows/builtin/heal.py | 6 +-
aria/orchestrator/workflows/core/engine.py | 17 +-
aria/orchestrator/workflows/core/task.py | 26 +-
aria/orchestrator/workflows/executor/process.py | 2 +-
aria/storage/__init__.py | 12 +-
aria/storage/base_model.py | 677 +++++++++++++++++++
aria/storage/core.py | 2 +-
aria/storage/model.py | 110 +++
aria/storage/models.py | 575 ----------------
aria/storage/structure.py | 180 +++++
aria/storage/structures.py | 244 -------
aria/storage/type.py | 122 ++++
tests/mock/models.py | 53 +-
tests/orchestrator/context/test_toolbelt.py | 8 +-
.../orchestrator/workflows/builtin/test_heal.py | 4 +-
.../orchestrator/workflows/core/test_engine.py | 10 +-
tests/orchestrator/workflows/core/test_task.py | 2 +-
.../workflows/executor/test_executor.py | 8 +-
.../workflows/executor/test_process_executor.py | 8 +-
tests/storage/__init__.py | 7 +-
tests/storage/test_model_storage.py | 106 ++-
tests/storage/test_models.py | 182 ++---
25 files changed, 1345 insertions(+), 1061 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/__init__.py
----------------------------------------------------------------------
diff --git a/aria/__init__.py b/aria/__init__.py
index cc362c0..248aa1a 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -62,22 +62,21 @@ def application_model_storage(api, api_kwargs=None):
Initiate model storage
"""
models = [
- storage.models.Plugin,
- storage.models.ProviderContext,
+ storage.model.Plugin,
- storage.models.Blueprint,
- storage.models.Deployment,
- storage.models.DeploymentUpdate,
- storage.models.DeploymentUpdateStep,
- storage.models.DeploymentModification,
+ storage.model.Blueprint,
+ storage.model.Deployment,
+ storage.model.DeploymentUpdate,
+ storage.model.DeploymentUpdateStep,
+ storage.model.DeploymentModification,
- storage.models.Node,
- storage.models.NodeInstance,
- storage.models.Relationship,
- storage.models.RelationshipInstance,
+ storage.model.Node,
+ storage.model.NodeInstance,
+ storage.model.Relationship,
+ storage.model.RelationshipInstance,
- storage.models.Execution,
- storage.models.Task,
+ storage.model.Execution,
+ storage.model.Task,
]
# if api not in _model_storage:
return storage.ModelStorage(api, items=models, api_kwargs=api_kwargs or {})
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/context/workflow.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/context/workflow.py b/aria/orchestrator/context/workflow.py
index e3be2d5..a15790e 100644
--- a/aria/orchestrator/context/workflow.py
+++ b/aria/orchestrator/context/workflow.py
@@ -57,8 +57,7 @@ class WorkflowContext(BaseContext):
execution_cls = self.model.execution.model_cls
now = datetime.utcnow()
execution = self.model.execution.model_cls(
- blueprint_id=self.blueprint.id,
- deployment_id=self.deployment.id,
+ deployment=self.deployment,
workflow_name=self._workflow_name,
created_at=now,
status=execution_cls.PENDING,
@@ -86,9 +85,11 @@ class WorkflowContext(BaseContext):
"""
Iterator over nodes
"""
+ key = 'deployment_{0}'.format(self.model.node.model_cls.name_column_name())
+
return self.model.node.iter(
filters={
- 'deployment_id': self.deployment.id
+ key: getattr(self.deployment, self.deployment.name_column_name())
}
)
@@ -97,9 +98,10 @@ class WorkflowContext(BaseContext):
"""
Iterator over node instances
"""
+ key = 'deployment_{0}'.format(self.model.node_instance.model_cls.name_column_name())
return self.model.node_instance.iter(
filters={
- 'deployment_id': self.deployment.id
+ key: getattr(self.deployment, self.deployment.name_column_name())
}
)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 4f025b6..70324a6 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 models
+from aria.storage import model
from ... import context
from .. import exceptions
@@ -75,8 +75,8 @@ class OperationTask(BaseTask):
:param actor: the operation host on which this operation is registered.
:param inputs: operation inputs.
"""
- assert isinstance(actor, (models.NodeInstance,
- models.RelationshipInstance))
+ assert isinstance(actor, (model.NodeInstance,
+ model.RelationshipInstance))
super(OperationTask, self).__init__()
self.actor = actor
self.name = '{name}.{actor.id}'.format(name=name, actor=actor)
@@ -98,7 +98,7 @@ class OperationTask(BaseTask):
:param instance: the node of which this operation belongs to.
:param name: the name of the operation.
"""
- assert isinstance(instance, models.NodeInstance)
+ assert isinstance(instance, model.NodeInstance)
return cls._instance(instance=instance,
name=name,
operation_details=instance.node.operations[name],
@@ -118,7 +118,7 @@ class OperationTask(BaseTask):
with 'source_operations' and 'target_operations'
:param inputs any additional inputs to the operation
"""
- assert isinstance(instance, models.RelationshipInstance)
+ 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
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/workflows/builtin/heal.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/builtin/heal.py b/aria/orchestrator/workflows/builtin/heal.py
index de07095..21da2ed 100644
--- a/aria/orchestrator/workflows/builtin/heal.py
+++ b/aria/orchestrator/workflows/builtin/heal.py
@@ -34,7 +34,7 @@ def heal(ctx, graph, node_instance_id):
:return:
"""
failing_node = ctx.model.node_instance.get(node_instance_id)
- host_node = ctx.model.node_instance.get(failing_node.host_id)
+ host_node = ctx.model.node_instance.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)
@@ -165,8 +165,8 @@ def heal_install(ctx, graph, failing_node_instances, targeted_node_instances):
def _get_contained_subgraph(context, host_node_instance):
contained_instances = [node_instance
for node_instance in context.node_instances
- if node_instance.host_id == host_node_instance.id and
- node_instance.id != node_instance.host_id]
+ if node_instance.host_fk == host_node_instance.id and
+ node_instance.host_fk != node_instance.id]
result = [host_node_instance]
if not contained_instances:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/workflows/core/engine.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index 7886b7a..47269a3 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 models
+from aria.storage import model
from aria.orchestrator import events
from .. import exceptions
@@ -82,18 +82,18 @@ class Engine(logger.LoggerMixin):
events.on_cancelling_workflow_signal.send(self._workflow_context)
def _is_cancel(self):
- return self._workflow_context.execution.status in [models.Execution.CANCELLING,
- models.Execution.CANCELLED]
+ return self._workflow_context.execution.status in [model.Execution.CANCELLING,
+ model.Execution.CANCELLED]
def _executable_tasks(self):
now = datetime.utcnow()
return (task for task in self._tasks_iter()
- if task.status in models.Task.WAIT_STATES and
+ if task.status in model.Task.WAIT_STATES and
task.due_at <= now and
not self._task_has_dependencies(task))
def _ended_tasks(self):
- return (task for task in self._tasks_iter() if task.status in models.Task.END_STATES)
+ return (task for task in self._tasks_iter() if task.status in model.Task.END_STATES)
def _task_has_dependencies(self, task):
return len(self._execution_graph.pred.get(task.id, {})) > 0
@@ -105,18 +105,19 @@ class Engine(logger.LoggerMixin):
for _, data in self._execution_graph.nodes_iter(data=True):
task = data['task']
if isinstance(task, engine_task.OperationTask):
- self._workflow_context.model.task.refresh(task.model_task)
+ if task.model_task.status not in model.Task.END_STATES:
+ self._workflow_context.model.task.refresh(task.model_task)
yield task
def _handle_executable_task(self, task):
if isinstance(task, engine_task.StubTask):
- task.status = models.Task.SUCCESS
+ task.status = model.Task.SUCCESS
else:
events.sent_task_signal.send(task)
self._executor.execute(task)
def _handle_ended_tasks(self, task):
- if task.status == models.Task.FAILED and not task.ignore_failure:
+ if task.status == model.Task.FAILED and not task.ignore_failure:
raise exceptions.ExecutorException('Workflow failed')
else:
self._execution_graph.remove_node(task.id)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/workflows/core/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py
index 663eeac..67be2ea 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 models
+from aria.storage import model
from aria.orchestrator.context import operation as operation_context
from .. import exceptions
@@ -66,7 +66,7 @@ class StubTask(BaseTask):
def __init__(self, *args, **kwargs):
super(StubTask, self).__init__(*args, **kwargs)
- self.status = models.Task.PENDING
+ self.status = model.Task.PENDING
self.due_at = datetime.utcnow()
@@ -106,36 +106,36 @@ class OperationTask(BaseTask):
def __init__(self, api_task, *args, **kwargs):
super(OperationTask, self).__init__(id=api_task.id, **kwargs)
self._workflow_context = api_task._workflow_context
- model = api_task._workflow_context.model
+ model_storage = api_task._workflow_context.model
- base_task_model = model.task.model_cls
- if isinstance(api_task.actor, models.NodeInstance):
+ base_task_model = model_storage.task.model_cls
+ if isinstance(api_task.actor, model.NodeInstance):
context_class = operation_context.NodeOperationContext
task_model_cls = base_task_model.as_node_instance
- elif isinstance(api_task.actor, models.RelationshipInstance):
+ elif isinstance(api_task.actor, model.RelationshipInstance):
context_class = 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}'
.format(actor=api_task.actor))
plugin = api_task.plugin
- plugins = model.plugin.list(filters={'package_name': plugin.get('package_name', ''),
- 'package_version': plugin.get('package_version', '')},
- include=['id'])
+ plugins = model_storage.plugin.list(filters={
+ 'package_name': plugin.get('package_name', ''),
+ 'package_version': plugin.get('package_version', '')
+ })
# Validation during installation ensures that at most one plugin can exists with provided
# package_name and package_version
- plugin_id = plugins[0].id if plugins else None
operation_task = task_model_cls(
name=api_task.name,
operation_mapping=api_task.operation_mapping,
- instance_id=api_task.actor.id,
+ instance=api_task.actor,
inputs=api_task.inputs,
status=base_task_model.PENDING,
max_attempts=api_task.max_attempts,
retry_interval=api_task.retry_interval,
ignore_failure=api_task.ignore_failure,
- plugin_id=plugin_id,
- execution_id=self._workflow_context.execution.id
+ plugin=plugins[0] if plugins else None,
+ execution=self._workflow_context.execution
)
self._workflow_context.model.task.put(operation_task)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/orchestrator/workflows/executor/process.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index 5da03dd..e0a8aeb 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -111,7 +111,7 @@ class ProcessExecutor(base.BaseExecutor):
env = os.environ.copy()
# See _update_env for plugin_prefix usage
- if task.plugin_id and self._plugin_manager:
+ if task.plugin_fk and self._plugin_manager:
plugin_prefix = self._plugin_manager.get_plugin_prefix(task.plugin)
else:
plugin_prefix = None
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/__init__.py
----------------------------------------------------------------------
diff --git a/aria/storage/__init__.py b/aria/storage/__init__.py
index fd69d47..a1c07d7 100644
--- a/aria/storage/__init__.py
+++ b/aria/storage/__init__.py
@@ -45,19 +45,19 @@ from .core import (
from . import (
exceptions,
api,
- structures,
+ structure,
core,
filesystem_rapi,
sql_mapi,
- models
+ model
)
__all__ = (
'exceptions',
- 'structures',
- # 'Storage',
- # 'ModelStorage',
- # 'ResourceStorage',
+ 'structure',
+ 'Storage',
+ 'ModelStorage',
+ 'ResourceStorage',
'filesystem_rapi',
'sql_mapi',
'api'
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/base_model.py
----------------------------------------------------------------------
diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py
new file mode 100644
index 0000000..c7eb27c
--- /dev/null
+++ b/aria/storage/base_model.py
@@ -0,0 +1,677 @@
+# 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 .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
+ }
+
+ @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 = ExecutionBase.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']
+
+ @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')
+
+ @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)
+
+ @declared_attr
+ def target_node(cls):
+ return cls.one_to_many_relationship('target_node_fk', backreference='inbound_relationships')
+
+ @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())
+
+
+class RelationshipInstanceBase(ModelMixin):
+ """
+ Relationship instance model representation.
+ """
+ __tablename__ = 'relationship_instances'
+ _private_fields = ['relationship_storage_fk',
+ 'source_node_instance_fk',
+ 'target_node_instance_fk']
+
+ @declared_attr
+ def source_node_instance_fk(cls):
+ return cls.foreign_key(NodeInstanceBase)
+
+ @declared_attr
+ def source_node_instance(cls):
+ return cls.one_to_many_relationship('source_node_instance_fk',
+ backreference='outbound_relationship_instances')
+
+ @declared_attr
+ def source_node_instance_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)
+
+ @declared_attr
+ def target_node_instance(cls):
+ return cls.one_to_many_relationship('target_node_instance_fk',
+ backreference='inbound_relationship_instances')
+
+ @declared_attr
+ def target_node_instance_name(cls):
+ return association_proxy('target_node_instance', 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 plugin_name(cls):
+ return association_proxy('plugin', 'name')
+
+ @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]
+
+ @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)
+
+ @property
+ def actor(self):
+ """
+ Return the actor of the task
+ :return:
+ """
+ return self.node_instance or self.relationship_instance
+
+ @classmethod
+ def as_node_instance(cls, instance, **kwargs):
+ return cls(node_instance=instance, **kwargs)
+
+ @classmethod
+ def as_relationship_instance(cls, instance, **kwargs):
+ return cls(relationship_instance=instance, **kwargs)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/core.py
----------------------------------------------------------------------
diff --git a/aria/storage/core.py b/aria/storage/core.py
index a5d3210..94b4fe0 100644
--- a/aria/storage/core.py
+++ b/aria/storage/core.py
@@ -53,9 +53,9 @@ class Storage(LoggerMixin):
Represents the storage
"""
def __init__(self, api_cls, api_kwargs=None, items=(), **kwargs):
- self._api_kwargs = api_kwargs or {}
super(Storage, self).__init__(**kwargs)
self.api = api_cls
+ self._api_kwargs = api_kwargs or {}
self.registered = {}
for item in items:
self.register(item)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/model.py
----------------------------------------------------------------------
diff --git a/aria/storage/model.py b/aria/storage/model.py
new file mode 100644
index 0000000..afca3e4
--- /dev/null
+++ b/aria/storage/model.py
@@ -0,0 +1,110 @@
+# 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/c9ecc54b/aria/storage/models.py
----------------------------------------------------------------------
diff --git a/aria/storage/models.py b/aria/storage/models.py
deleted file mode 100644
index 0a1027b..0000000
--- a/aria/storage/models.py
+++ /dev/null
@@ -1,575 +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 collections import namedtuple
-from datetime import datetime
-
-from sqlalchemy.ext.declarative.base import declared_attr
-
-from .structures import (
- SQLModelBase,
- Column,
- Integer,
- Text,
- DateTime,
- Boolean,
- Enum,
- String,
- Float,
- List,
- Dict,
- foreign_key,
- one_to_many_relationship,
- relationship_to_self,
- orm)
-
-__all__ = (
- 'Blueprint',
- 'Deployment',
- 'DeploymentUpdateStep',
- 'DeploymentUpdate',
- 'DeploymentModification',
- 'Execution',
- 'Node',
- 'Relationship',
- 'NodeInstance',
- 'RelationshipInstance',
- 'ProviderContext',
- 'Plugin',
-)
-
-
-#pylint: disable=no-self-argument
-
-
-class Blueprint(SQLModelBase):
- """
- Blueprint model representation.
- """
- __tablename__ = 'blueprints'
-
- name = Column(Text, index=True)
- 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 Deployment(SQLModelBase):
- """
- Deployment model representation.
- """
- __tablename__ = 'deployments'
-
- _private_fields = ['blueprint_id']
-
- blueprint_id = foreign_key(Blueprint.id)
-
- name = Column(Text, index=True)
- 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(cls):
- return one_to_many_relationship(cls, Blueprint, cls.blueprint_id)
-
-
-class Execution(SQLModelBase):
- """
- Execution model representation.
- """
- __tablename__ = 'executions'
-
- 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
- }
-
- @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 = Execution.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
-
- deployment_id = foreign_key(Deployment.id)
- blueprint_id = foreign_key(Blueprint.id)
- _private_fields = ['deployment_id', 'blueprint_id']
-
- 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, nullable=False)
-
- @declared_attr
- def deployment(cls):
- return one_to_many_relationship(cls, Deployment, cls.deployment_id)
-
- @declared_attr
- def blueprint(cls):
- return one_to_many_relationship(cls, Blueprint, cls.blueprint_id)
-
- def __str__(self):
- return '<{0} id=`{1}` (status={2})>'.format(
- self.__class__.__name__,
- self.id,
- self.status
- )
-
-
-class DeploymentUpdate(SQLModelBase):
- """
- Deployment update model representation.
- """
- __tablename__ = 'deployment_updates'
-
- deployment_id = foreign_key(Deployment.id)
- execution_id = foreign_key(Execution.id, nullable=True)
- _private_fields = ['execution_id', 'deployment_id']
-
- 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(Dict)
- modified_entity_ids = Column(Dict)
- state = Column(Text)
-
- @declared_attr
- def execution(cls):
- return one_to_many_relationship(cls, Execution, cls.execution_id)
-
- @declared_attr
- def deployment(cls):
- return one_to_many_relationship(cls, Deployment, cls.deployment_id)
-
- def to_dict(self, suppress_error=False, **kwargs):
- dep_update_dict = super(DeploymentUpdate, self).to_dict(suppress_error)
- # Taking care of the fact the DeploymentSteps are objects
- dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
- return dep_update_dict
-
-
-class DeploymentUpdateStep(SQLModelBase):
- """
- Deployment update step model representation.
- """
- __tablename__ = 'deployment_update_steps'
- _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'
- )
-
- deployment_update_id = foreign_key(DeploymentUpdate.id)
- _private_fields = ['deployment_update_id']
-
- 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(cls):
- return one_to_many_relationship(cls,
- DeploymentUpdate,
- cls.deployment_update_id,
- backreference='steps')
-
- def __hash__(self):
- return hash((self.id, 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 DeploymentModification(SQLModelBase):
- """
- Deployment modification model representation.
- """
- __tablename__ = 'deployment_modifications'
-
- STARTED = 'started'
- FINISHED = 'finished'
- ROLLEDBACK = 'rolledback'
-
- STATES = [STARTED, FINISHED, ROLLEDBACK]
- END_STATES = [FINISHED, ROLLEDBACK]
-
- deployment_id = foreign_key(Deployment.id)
- _private_fields = ['deployment_id']
-
- 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(cls):
- return one_to_many_relationship(cls,
- Deployment,
- cls.deployment_id,
- backreference='modifications')
-
-
-class Node(SQLModelBase):
- """
- Node model representation.
- """
- __tablename__ = 'nodes'
-
- # See base class for an explanation on these properties
- is_id_unique = False
-
- name = Column(Text, index=True)
- _private_fields = ['deployment_id', 'host_id']
- deployment_id = foreign_key(Deployment.id)
- host_id = foreign_key('nodes.id', nullable=True)
-
- @declared_attr
- def deployment(cls):
- return one_to_many_relationship(cls, Deployment, cls.deployment_id)
-
- deploy_number_of_instances = Column(Integer, nullable=False)
- # TODO: This probably should be a foreign key, but there's no guarantee
- # in the code, currently, that the host will be created beforehand
- 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)
-
- @declared_attr
- def host(cls):
- return relationship_to_self(cls, cls.host_id, cls.id)
-
-
-class Relationship(SQLModelBase):
- """
- Relationship model representation.
- """
- __tablename__ = 'relationships'
-
- _private_fields = ['source_node_id', 'target_node_id']
-
- source_node_id = foreign_key(Node.id)
- target_node_id = foreign_key(Node.id)
-
- @declared_attr
- def source_node(cls):
- return one_to_many_relationship(cls,
- Node,
- cls.source_node_id,
- 'outbound_relationships')
-
- @declared_attr
- def target_node(cls):
- return one_to_many_relationship(cls,
- Node,
- cls.target_node_id,
- 'inbound_relationships')
-
- 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 NodeInstance(SQLModelBase):
- """
- Node instance model representation.
- """
- __tablename__ = 'node_instances'
-
- node_id = foreign_key(Node.id)
- deployment_id = foreign_key(Deployment.id)
- host_id = foreign_key('node_instances.id', nullable=True)
-
- _private_fields = ['node_id', 'host_id']
-
- name = Column(Text, index=True)
- runtime_properties = Column(Dict)
- scaling_groups = Column(Dict)
- state = Column(Text, nullable=False)
- version = Column(Integer, default=1)
-
- @declared_attr
- def deployment(cls):
- return one_to_many_relationship(cls, Deployment, cls.deployment_id)
-
- @declared_attr
- def node(cls):
- return one_to_many_relationship(cls, Node, cls.node_id)
-
- @declared_attr
- def host(cls):
- return relationship_to_self(cls, cls.host_id, cls.id)
-
-
-class RelationshipInstance(SQLModelBase):
- """
- Relationship instance model representation.
- """
- __tablename__ = 'relationship_instances'
-
- relationship_id = foreign_key(Relationship.id)
- source_node_instance_id = foreign_key(NodeInstance.id)
- target_node_instance_id = foreign_key(NodeInstance.id)
-
- _private_fields = ['relationship_storage_id',
- 'source_node_instance_id',
- 'target_node_instance_id']
-
- @declared_attr
- def source_node_instance(cls):
- return one_to_many_relationship(cls,
- NodeInstance,
- cls.source_node_instance_id,
- 'outbound_relationship_instances')
-
- @declared_attr
- def target_node_instance(cls):
- return one_to_many_relationship(cls,
- NodeInstance,
- cls.target_node_instance_id,
- 'inbound_relationship_instances')
-
- @declared_attr
- def relationship(cls):
- return one_to_many_relationship(cls, Relationship, cls.relationship_id)
-
-
-class ProviderContext(SQLModelBase):
- """
- Provider context model representation.
- """
- __tablename__ = 'provider_context'
-
- name = Column(Text, nullable=False)
- context = Column(Dict, nullable=False)
-
-
-class Plugin(SQLModelBase):
- """
- 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 Task(SQLModelBase):
- """
- A Model which represents an task
- """
-
- __tablename__ = 'task'
- node_instance_id = foreign_key(NodeInstance.id, nullable=True)
- relationship_instance_id = foreign_key(RelationshipInstance.id, nullable=True)
- execution_id = foreign_key(Execution.id, nullable=True)
-
- _private_fields = ['node_instance_id',
- 'relationship_instance_id',
- 'execution_id']
-
- @declared_attr
- def node_instance(cls):
- return one_to_many_relationship(cls, NodeInstance, cls.node_instance_id)
-
- @declared_attr
- def relationship_instance(cls):
- return one_to_many_relationship(cls,
- RelationshipInstance,
- cls.relationship_instance_id)
-
- 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]
-
- @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 != Task.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
- name = Column(String)
- operation_mapping = Column(String)
- inputs = Column(Dict)
- plugin_id = foreign_key(Plugin.id, nullable=True)
-
- @declared_attr
- def plugin(cls):
- return one_to_many_relationship(cls, Plugin, cls.plugin_id)
-
- @declared_attr
- def execution(cls):
- return one_to_many_relationship(cls, Execution, cls.execution_id)
-
- @property
- def actor(self):
- """
- Return the actor of the task
- :return:
- """
- return self.node_instance or self.relationship_instance
-
- @classmethod
- def as_node_instance(cls, instance_id, **kwargs):
- return cls(node_instance_id=instance_id, **kwargs)
-
- @classmethod
- def as_relationship_instance(cls, instance_id, **kwargs):
- return cls(relationship_instance_id=instance_id, **kwargs)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/structure.py
----------------------------------------------------------------------
diff --git a/aria/storage/structure.py b/aria/storage/structure.py
new file mode 100644
index 0000000..d222c94
--- /dev/null
+++ b/aria/storage/structure.py
@@ -0,0 +1,180 @@
+# 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):
+ """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)
+ """
+ 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'),
+ )
+
+ @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, 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)
+ """
+ if suppress_error:
+ res = dict()
+ for field in self.fields():
+ try:
+ field_value = getattr(self, field)
+ except AttributeError:
+ field_value = None
+ res[field] = field_value
+ else:
+ # Can't simply call here `self.to_response()` because inheriting
+ # class might override it, but we always need the same code here
+ res = dict((f, getattr(self, f)) for f in self.fields())
+ 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'
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/structures.py
----------------------------------------------------------------------
diff --git a/aria/storage/structures.py b/aria/storage/structures.py
deleted file mode 100644
index 8afa40c..0000000
--- a/aria/storage/structures.py
+++ /dev/null
@@ -1,244 +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.
-"""
-import json
-
-from sqlalchemy.ext.mutable import Mutable
-from sqlalchemy.orm import relationship, backref
-from sqlalchemy.ext.declarative import declarative_base
-# pylint: disable=unused-import
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy import (
- schema,
- VARCHAR,
- ARRAY,
- Column,
- Integer,
- Text,
- DateTime,
- Boolean,
- Enum,
- String,
- PickleType,
- Float,
- TypeDecorator,
- ForeignKey,
- orm,
-)
-
-from aria.storage import exceptions
-
-Model = declarative_base()
-
-
-def foreign_key(foreign_key_column, nullable=False):
- """Return a ForeignKey object with the relevant
-
- :param foreign_key_column: Unique id column in the parent table
- :param nullable: Should the column be allowed to remain empty
- """
- return Column(
- ForeignKey(foreign_key_column, ondelete='CASCADE'),
- nullable=nullable
- )
-
-
-def one_to_many_relationship(child_class,
- parent_class,
- foreign_key_column,
- backreference=None):
- """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 child_class: Class of the child table
- :param foreign_key_column: The column of the foreign key
- :param backreference: The name to give to the reference to the child
- """
- backreference = backreference or child_class.__tablename__
- return relationship(
- parent_class,
- primaryjoin=lambda: parent_class.id == 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, cascade='all')
- )
-
-
-def relationship_to_self(self_cls, parent_key, self_key):
- return relationship(
- self_cls,
- foreign_keys=parent_key,
- remote_side=self_key
- )
-
-
-class _MutableType(TypeDecorator):
- """
- Dict representation of type.
- """
- @property
- def python_type(self):
- raise NotImplementedError
-
- impl = VARCHAR
-
- def process_literal_param(self, value, dialect):
- pass
-
- 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 _DictType(_MutableType):
- @property
- def python_type(self):
- return dict
-
-
-class _ListType(_MutableType):
- @property
- def python_type(self):
- return list
-
-
-class _MutableDict(Mutable, dict):
- """
- Enables tracking for dict values.
- """
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
-
- if not isinstance(value, _MutableDict):
- if isinstance(value, dict):
- return _MutableDict(value)
-
- # this call will raise ValueError
- try:
- return Mutable.coerce(key, value)
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
- else:
- return value
-
- def __setitem__(self, key, value):
- "Detect dictionary set events and emit change events."
-
- dict.__setitem__(self, key, value)
- self.changed()
-
- def __delitem__(self, key):
- "Detect dictionary del events and emit change events."
-
- dict.__delitem__(self, key)
- self.changed()
-
-
-class _MutableList(Mutable, list):
-
- @classmethod
- def coerce(cls, key, value):
- "Convert plain dictionaries to MutableDict."
-
- if not isinstance(value, _MutableList):
- if isinstance(value, list):
- return _MutableList(value)
-
- # this call will raise ValueError
- try:
- return Mutable.coerce(key, value)
- except ValueError as e:
- raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
- else:
- return value
-
- def __setitem__(self, key, value):
- list.__setitem__(self, key, value)
- self.changed()
-
- def __delitem__(self, key):
- list.__delitem__(self, key)
-
-
-Dict = _MutableDict.as_mutable(_DictType)
-List = _MutableList.as_mutable(_ListType)
-
-
-class SQLModelBase(Model):
- """
- Abstract base class for all SQL models that allows [de]serialization
- """
- # SQLAlchemy syntax
- __abstract__ = True
-
- # This would be overridden once the models are created. Created for pylint.
- __table__ = None
-
- _private_fields = []
-
- id = Column(Integer, primary_key=True, autoincrement=True)
-
- def to_dict(self, 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)
- """
- if suppress_error:
- res = dict()
- for field in self.fields():
- try:
- field_value = getattr(self, field)
- except AttributeError:
- field_value = None
- res[field] = field_value
- else:
- # Can't simply call here `self.to_response()` because inheriting
- # class might override it, but we always need the same code here
- res = dict((f, getattr(self, f)) for f in self.fields())
- return res
-
- @classmethod
- def fields(cls):
- """Return the list of field names for this table
-
- Mostly for backwards compatibility in the code (that uses `fields`)
- """
- return set(cls.__table__.columns.keys()) - set(cls._private_fields)
-
- def __repr__(self):
- return '<{0} id=`{1}`>'.format(self.__class__.__name__, self.id)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/aria/storage/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/type.py b/aria/storage/type.py
new file mode 100644
index 0000000..84fd8dc
--- /dev/null
+++ b/aria/storage/type.py
@@ -0,0 +1,122 @@
+# 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 sqlalchemy import (
+ TypeDecorator,
+ VARCHAR
+)
+
+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 _MutableDict(mutable.Mutable, dict):
+ """
+ Enables tracking for dict values.
+ """
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+
+ if not isinstance(value, cls):
+ if isinstance(value, dict):
+ return cls(value)
+
+ # this call will raise ValueError
+ try:
+ return mutable.Mutable.coerce(key, value)
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+ else:
+ return value
+
+ def __setitem__(self, key, value):
+ "Detect dictionary set events and emit change events."
+
+ dict.__setitem__(self, key, value)
+ self.changed()
+
+ def __delitem__(self, key):
+ "Detect dictionary del events and emit change events."
+
+ dict.__delitem__(self, key)
+ self.changed()
+
+
+class _MutableList(mutable.Mutable, list):
+
+ @classmethod
+ def coerce(cls, key, value):
+ "Convert plain dictionaries to MutableDict."
+
+ if not isinstance(value, cls):
+ if isinstance(value, list):
+ return cls(value)
+
+ # this call will raise ValueError
+ try:
+ return mutable.Mutable.coerce(key, value)
+ except ValueError as e:
+ raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+ else:
+ return value
+
+ def __setitem__(self, key, value):
+ list.__setitem__(self, key, value)
+ self.changed()
+
+ def __delitem__(self, key):
+ list.__delitem__(self, key)
+
+_MutableList.associate_with(List)
+_MutableDict.associate_with(Dict)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/tests/mock/models.py
----------------------------------------------------------------------
diff --git a/tests/mock/models.py b/tests/mock/models.py
index 26088e0..8229038 100644
--- a/tests/mock/models.py
+++ b/tests/mock/models.py
@@ -15,7 +15,7 @@
from datetime import datetime
-from aria.storage import models
+from aria.storage import model
from . import operations
@@ -35,7 +35,7 @@ RELATIONSHIP_INSTANCE_NAME = 'relationship_instance'
def get_dependency_node(deployment):
- return models.Node(
+ return model.Node(
name=DEPENDENCY_NODE_NAME,
type='test_node_type',
type_hierarchy=[],
@@ -46,26 +46,25 @@ def get_dependency_node(deployment):
operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
min_number_of_instances=1,
max_number_of_instances=1,
- deployment_id=deployment.id
+ deployment_fk=deployment.id
)
def get_dependency_node_instance(dependency_node):
- return models.NodeInstance(
+ return model.NodeInstance(
name=DEPENDENCY_NODE_INSTANCE_NAME,
runtime_properties={'ip': '1.1.1.1'},
version=None,
- node_id=dependency_node.id,
- deployment_id=dependency_node.deployment.id,
+ node_fk=dependency_node.id,
state='',
- scaling_groups={}
+ scaling_groups=[]
)
def get_relationship(source=None, target=None):
- return models.Relationship(
- source_node_id=source.id,
- target_node_id=target.id,
+ 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={},
@@ -77,17 +76,17 @@ def get_relationship(source=None, target=None):
def get_relationship_instance(source_instance, target_instance, relationship):
- return models.RelationshipInstance(
- relationship_id=relationship.id,
- target_node_instance_id=target_instance.id,
- source_node_instance_id=source_instance.id,
+ return model.RelationshipInstance(
+ relationship_fk=relationship.id,
+ target_node_instance_fk=target_instance.id,
+ source_node_instance_fk=source_instance.id,
)
def get_dependent_node(deployment):
- return models.Node(
+ return model.Node(
name=DEPENDENT_NODE_NAME,
- deployment_id=deployment.id,
+ deployment_fk=deployment.id,
type='test_node_type',
type_hierarchy=[],
number_of_instances=1,
@@ -101,20 +100,19 @@ def get_dependent_node(deployment):
def get_dependent_node_instance(dependent_node):
- return models.NodeInstance(
+ return model.NodeInstance(
name=DEPENDENT_NODE_INSTANCE_NAME,
runtime_properties={},
version=None,
- node_id=dependent_node.id,
- deployment_id=dependent_node.deployment.id,
+ node_fk=dependent_node.id,
state='',
- scaling_groups={}
+ scaling_groups=[]
)
def get_blueprint():
now = datetime.now()
- return models.Blueprint(
+ return model.Blueprint(
plan={},
name=BLUEPRINT_NAME,
description=None,
@@ -125,10 +123,9 @@ def get_blueprint():
def get_execution(deployment):
- return models.Execution(
- deployment_id=deployment.id,
- blueprint_id=deployment.blueprint.id,
- status=models.Execution.STARTED,
+ return model.Execution(
+ deployment_fk=deployment.id,
+ status=model.Execution.STARTED,
workflow_name=WORKFLOW_NAME,
started_at=datetime.utcnow(),
parameters=None
@@ -137,9 +134,9 @@ def get_execution(deployment):
def get_deployment(blueprint):
now = datetime.utcnow()
- return models.Deployment(
+ return model.Deployment(
name=DEPLOYMENT_NAME,
- blueprint_id=blueprint.id,
+ blueprint_fk=blueprint.id,
description='',
created_at=now,
updated_at=now,
@@ -155,7 +152,7 @@ def get_deployment(blueprint):
def get_plugin(package_name='package', package_version='0.1'):
- return models.Plugin(
+ return model.Plugin(
archive_name='archive_name',
distribution='distribution',
distribution_release='dist_release',
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/tests/orchestrator/context/test_toolbelt.py
----------------------------------------------------------------------
diff --git a/tests/orchestrator/context/test_toolbelt.py b/tests/orchestrator/context/test_toolbelt.py
index da46696..b63811b 100644
--- a/tests/orchestrator/context/test_toolbelt.py
+++ b/tests/orchestrator/context/test_toolbelt.py
@@ -49,21 +49,21 @@ def executor():
def _get_elements(workflow_context):
dependency_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
- dependency_node.host_id = dependency_node.id
+ dependency_node.host = dependency_node
workflow_context.model.node.update(dependency_node)
dependency_node_instance = workflow_context.model.node_instance.get_by_name(
mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- dependency_node_instance.host_id = dependency_node_instance.id
+ dependency_node_instance.host_fk = dependency_node_instance.id
workflow_context.model.node_instance.update(dependency_node_instance)
dependent_node = workflow_context.model.node.get_by_name(mock.models.DEPENDENT_NODE_NAME)
- dependent_node.host_id = dependency_node.id
+ dependent_node.host_fk = dependency_node.id
workflow_context.model.node.update(dependent_node)
dependent_node_instance = workflow_context.model.node_instance.get_by_name(
mock.models.DEPENDENT_NODE_INSTANCE_NAME)
- dependent_node_instance.host_id = dependent_node_instance.id
+ dependent_node_instance.host_fk = dependent_node_instance.id
workflow_context.model.node_instance.update(dependent_node_instance)
relationship = workflow_context.model.relationship.list()[0]
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c9ecc54b/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 97121b9..ad281fd 100644
--- a/tests/orchestrator/workflows/builtin/test_heal.py
+++ b/tests/orchestrator/workflows/builtin/test_heal.py
@@ -34,7 +34,7 @@ def ctx(tmpdir):
def test_heal_dependent_node(ctx):
dependent_node_instance = \
ctx.model.node_instance.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME)
- dependent_node_instance.host_id = dependent_node_instance.id
+ dependent_node_instance.host_fk = dependent_node_instance.id
ctx.model.node_instance.update(dependent_node_instance)
heal_graph = task.WorkflowTask(heal, ctx=ctx, node_instance_id=dependent_node_instance.id)
@@ -63,7 +63,7 @@ def test_heal_dependent_node(ctx):
def test_heal_dependency_node(ctx):
dependency_node_instance = \
ctx.model.node_instance.get_by_name(mock.models.DEPENDENCY_NODE_INSTANCE_NAME)
- dependency_node_instance.host_id = dependency_node_instance.id
+ dependency_node_instance.host_fk = dependency_node_instance.id
ctx.model.node_instance.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