http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/aria/storage/base_model.py ---------------------------------------------------------------------- diff --git a/aria/storage/base_model.py b/aria/storage/base_model.py index 65344a6..d041a38 100644 --- a/aria/storage/base_model.py +++ b/aria/storage/base_model.py @@ -1,743 +1,743 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Aria's storage.models module -Path: aria.storage.models - -models module holds aria's models. - -classes: - * Field - represents a single field. - * IterField - represents an iterable field. - * Model - abstract model implementation. - * Snapshot - snapshots implementation model. - * Deployment - deployment implementation model. - * DeploymentUpdateStep - deployment update step implementation model. - * DeploymentUpdate - deployment update implementation model. - * DeploymentModification - deployment modification implementation model. - * Execution - execution implementation model. - * Node - node implementation model. - * Relationship - relationship implementation model. - * NodeInstance - node instance implementation model. - * RelationshipInstance - relationship instance implementation model. - * Plugin - plugin implementation model. -""" -from collections import namedtuple -from datetime import datetime - -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy import ( - Column, - Integer, - Text, - DateTime, - Boolean, - Enum, - String, - Float, - orm, -) -from sqlalchemy.ext.orderinglist import ordering_list - -from ..orchestrator.exceptions import TaskAbortException, TaskRetryException -from .structure import ModelMixin -from .type import ( - List, - Dict -) - -__all__ = ( - 'BlueprintBase', - 'DeploymentBase', - 'DeploymentUpdateStepBase', - 'DeploymentUpdateBase', - 'DeploymentModificationBase', - 'ExecutionBase', - 'NodeBase', - 'RelationshipBase', - 'NodeInstanceBase', - 'RelationshipInstanceBase', - 'PluginBase', - 'TaskBase' -) - -#pylint: disable=no-self-argument, abstract-method - - -class BlueprintBase(ModelMixin): - """ - Blueprint model representation. - """ - __tablename__ = 'blueprints' - - created_at = Column(DateTime, nullable=False, index=True) - main_file_name = Column(Text, nullable=False) - plan = Column(Dict, nullable=False) - updated_at = Column(DateTime) - description = Column(Text) - - -class DeploymentBase(ModelMixin): - """ - Deployment model representation. - """ - __tablename__ = 'deployments' - - _private_fields = ['blueprint_fk'] - - created_at = Column(DateTime, nullable=False, index=True) - description = Column(Text) - inputs = Column(Dict) - groups = Column(Dict) - permalink = Column(Text) - policy_triggers = Column(Dict) - policy_types = Column(Dict) - outputs = Column(Dict) - scaling_groups = Column(Dict) - updated_at = Column(DateTime) - workflows = Column(Dict) - - @declared_attr - def blueprint_fk(cls): - return cls.foreign_key(BlueprintBase, nullable=False) - - @declared_attr - def blueprint(cls): - return cls.many_to_one_relationship('blueprint_fk') - - @declared_attr - def blueprint_name(cls): - return association_proxy('blueprint', cls.name_column_name()) - - -class ExecutionBase(ModelMixin): - """ - Execution model representation. - """ - # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column. - __tablename__ = 'executions' - _private_fields = ['deployment_fk'] - - TERMINATED = 'terminated' - FAILED = 'failed' - CANCELLED = 'cancelled' - PENDING = 'pending' - STARTED = 'started' - CANCELLING = 'cancelling' - FORCE_CANCELLING = 'force_cancelling' - - STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING] - END_STATES = [TERMINATED, FAILED, CANCELLED] - ACTIVE_STATES = [state for state in STATES if state not in END_STATES] - - VALID_TRANSITIONS = { - PENDING: [STARTED, CANCELLED], - STARTED: END_STATES + [CANCELLING], - CANCELLING: END_STATES + [FORCE_CANCELLING] - } - - @orm.validates('status') - def validate_status(self, key, value): - """Validation function that verifies execution status transitions are OK""" - try: - current_status = getattr(self, key) - except AttributeError: - return - valid_transitions = self.VALID_TRANSITIONS.get(current_status, []) - if all([current_status is not None, - current_status != value, - value not in valid_transitions]): - raise ValueError('Cannot change execution status from {current} to {new}'.format( - current=current_status, - new=value)) - return value - - created_at = Column(DateTime, index=True) - started_at = Column(DateTime, nullable=True, index=True) - ended_at = Column(DateTime, nullable=True, index=True) - error = Column(Text, nullable=True) - is_system_workflow = Column(Boolean, nullable=False, default=False) - parameters = Column(Dict) - status = Column(Enum(*STATES, name='execution_status'), default=PENDING) - workflow_name = Column(Text) - - @declared_attr - def blueprint(cls): - return association_proxy('deployment', 'blueprint') - - @declared_attr - def deployment_fk(cls): - return cls.foreign_key(DeploymentBase, nullable=True) - - @declared_attr - def deployment(cls): - return cls.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_relationship('deployment_fk') - - @declared_attr - def deployment_name(cls): - return association_proxy('deployment', cls.name_column_name()) - - @declared_attr - def blueprint_name(cls): - return association_proxy('deployment', 'blueprint_{0}'.format(cls.name_column_name())) - - deploy_number_of_instances = Column(Integer, nullable=False) - max_number_of_instances = Column(Integer, nullable=False) - min_number_of_instances = Column(Integer, nullable=False) - number_of_instances = Column(Integer, nullable=False) - planned_number_of_instances = Column(Integer, nullable=False) - plugins = Column(List) - properties = Column(Dict) - operations = Column(Dict) - type = Column(Text, nullable=False, index=True) - type_hierarchy = Column(List) - - -class RelationshipBase(ModelMixin): - """ - Relationship model representation. - """ - __tablename__ = 'relationships' - - _private_fields = ['source_node_fk', 'target_node_fk', 'source_position', 'target_position'] - - source_position = Column(Integer) - target_position = Column(Integer) - - @declared_attr - def deployment_id(self): - return association_proxy('source_node', 'deployment_id') - - @declared_attr - def source_node_fk(cls): - return cls.foreign_key(NodeBase) - - @declared_attr - def source_node(cls): - return cls.many_to_one_relationship( - 'source_node_fk', - backreference='outbound_relationships', - backref_kwargs=dict( - order_by=cls.source_position, - collection_class=ordering_list('source_position', count_from=0) - ) - ) - - @declared_attr - def source_name(cls): - return association_proxy('source_node', cls.name_column_name()) - - @declared_attr - def target_node_fk(cls): - return cls.foreign_key(NodeBase, nullable=True) - - @declared_attr - def target_node(cls): - return cls.many_to_one_relationship( - 'target_node_fk', - backreference='inbound_relationships', - backref_kwargs=dict( - order_by=cls.target_position, - collection_class=ordering_list('target_position', count_from=0) - ) - ) - - @declared_attr - def target_name(cls): - return association_proxy('target_node', cls.name_column_name()) - - source_interfaces = Column(Dict) - source_operations = Column(Dict, nullable=False) - target_interfaces = Column(Dict) - target_operations = Column(Dict, nullable=False) - type = Column(String, nullable=False) - type_hierarchy = Column(List) - properties = Column(Dict) - - -class NodeInstanceBase(ModelMixin): - """ - Node instance model representation. - """ - __tablename__ = 'node_instances' - _private_fields = ['node_fk', 'host_fk'] - - runtime_properties = Column(Dict) - scaling_groups = Column(List) - state = Column(Text, nullable=False) - version = Column(Integer, default=1) - - @declared_attr - def host_fk(cls): - return cls.foreign_key(NodeInstanceBase, nullable=True) - - @declared_attr - def host(cls): - return cls.relationship_to_self('host_fk') - - @declared_attr - def host_name(cls): - return association_proxy('host', cls.name_column_name()) - - @declared_attr - def deployment(cls): - return association_proxy('node', 'deployment') - - @declared_attr - def deployment_name(cls): - return association_proxy('node', 'deployment_name') - - @declared_attr - def node_fk(cls): - return cls.foreign_key(NodeBase, nullable=True) - - @declared_attr - def node(cls): - return cls.many_to_one_relationship('node_fk') - - @declared_attr - def node_name(cls): - return association_proxy('node', cls.name_column_name()) - - @property - def ip(self): - if not self.host_fk: - return None - host_node_instance = self.host - if 'ip' in host_node_instance.runtime_properties: # pylint: disable=no-member - return host_node_instance.runtime_properties['ip'] # pylint: disable=no-member - host_node = host_node_instance.node # pylint: disable=no-member - if 'ip' in host_node.properties: - return host_node.properties['ip'] - return None - - -class RelationshipInstanceBase(ModelMixin): - """ - Relationship instance model representation. - """ - __tablename__ = 'relationship_instances' - _private_fields = ['relationship_storage_fk', - 'source_node_instance_fk', - 'target_node_instance_fk', - 'source_position', - 'target_position'] - - source_position = Column(Integer) - target_position = Column(Integer) - - @declared_attr - def source_node_instance_fk(cls): - return cls.foreign_key(NodeInstanceBase, nullable=True) - - @declared_attr - def source_node_instance(cls): - return cls.many_to_one_relationship( - 'source_node_instance_fk', - backreference='outbound_relationship_instances', - backref_kwargs=dict( - order_by=cls.source_position, - collection_class=ordering_list('source_position', count_from=0) - ) - ) - - @declared_attr - def source_node_instance_name(cls): - return association_proxy('source_node_instance', 'node_{0}'.format(cls.name_column_name())) - - @declared_attr - def source_node_name(cls): - return association_proxy('source_node_instance', cls.name_column_name()) - - @declared_attr - def target_node_instance_fk(cls): - return cls.foreign_key(NodeInstanceBase, nullable=True) - - @declared_attr - def target_node_instance(cls): - return cls.many_to_one_relationship( - 'target_node_instance_fk', - backreference='inbound_relationship_instances', - backref_kwargs=dict( - order_by=cls.target_position, - collection_class=ordering_list('target_position', count_from=0) - ) - ) - - @declared_attr - def target_node_instance_name(cls): - return association_proxy('target_node_instance', cls.name_column_name()) - - @declared_attr - def target_node_name(cls): - return association_proxy('target_node_instance', 'node_{0}'.format(cls.name_column_name())) - - @declared_attr - def relationship_fk(cls): - return cls.foreign_key(RelationshipBase) - - @declared_attr - def relationship(cls): - return cls.many_to_one_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.many_to_one_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.many_to_one_relationship('relationship_instance_fk') - - @declared_attr - def plugin_fk(cls): - return cls.foreign_key(PluginBase, nullable=True) - - @declared_attr - def plugin(cls): - return cls.many_to_one_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.many_to_one_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) - - @staticmethod - def abort(message=None): - raise TaskAbortException(message) - - @staticmethod - def retry(message=None, retry_interval=None): - raise TaskRetryException(message, retry_interval=retry_interval) +# # Licensed to the Apache Software Foundation (ASF) under one or more +# # contributor license agreements. See the NOTICE file distributed with +# # this work for additional information regarding copyright ownership. +# # The ASF licenses this file to You under the Apache License, Version 2.0 +# # (the "License"); you may not use this file except in compliance with +# # the License. You may obtain a copy of the License at +# # +# # http://www.apache.org/licenses/LICENSE-2.0 +# # +# # Unless required by applicable law or agreed to in writing, software +# # distributed under the License is distributed on an "AS IS" BASIS, +# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# # See the License for the specific language governing permissions and +# # limitations under the License. +# +# """ +# Aria's storage.models module +# Path: aria.storage.models +# +# models module holds aria's models. +# +# classes: +# * Field - represents a single field. +# * IterField - represents an iterable field. +# * Model - abstract model implementation. +# * Snapshot - snapshots implementation model. +# * Deployment - deployment implementation model. +# * DeploymentUpdateStep - deployment update step implementation model. +# * DeploymentUpdate - deployment update implementation model. +# * DeploymentModification - deployment modification implementation model. +# * Execution - execution implementation model. +# * Node - node implementation model. +# * Relationship - relationship implementation model. +# * NodeInstance - node instance implementation model. +# * RelationshipInstance - relationship instance implementation model. +# * Plugin - plugin implementation model. +# """ +# from collections import namedtuple +# from datetime import datetime +# +# from sqlalchemy.ext.associationproxy import association_proxy +# from sqlalchemy.ext.declarative import declared_attr +# from sqlalchemy import ( +# Column, +# Integer, +# Text, +# DateTime, +# Boolean, +# Enum, +# String, +# Float, +# orm, +# ) +# from sqlalchemy.ext.orderinglist import ordering_list +# +# from ..orchestrator.exceptions import TaskAbortException, TaskRetryException +# from .structure import ModelMixin +# from .type import ( +# List, +# Dict +# ) +# +# __all__ = ( +# 'BlueprintBase', +# 'DeploymentBase', +# 'DeploymentUpdateStepBase', +# 'DeploymentUpdateBase', +# 'DeploymentModificationBase', +# 'ExecutionBase', +# 'NodeBase', +# 'RelationshipBase', +# 'NodeInstanceBase', +# 'RelationshipInstanceBase', +# 'PluginBase', +# 'TaskBase' +# ) +# +# #pylint: disable=no-self-argument, abstract-method +# +# +# class BlueprintBase(ModelMixin): +# """ +# Blueprint model representation. +# """ +# __tablename__ = 'blueprints' +# +# created_at = Column(DateTime, nullable=False, index=True) +# main_file_name = Column(Text, nullable=False) +# plan = Column(Dict, nullable=False) +# updated_at = Column(DateTime) +# description = Column(Text) +# +# +# class DeploymentBase(ModelMixin): +# """ +# Deployment model representation. +# """ +# __tablename__ = 'deployments' +# +# _private_fields = ['blueprint_fk'] +# +# created_at = Column(DateTime, nullable=False, index=True) +# description = Column(Text) +# inputs = Column(Dict) +# groups = Column(Dict) +# permalink = Column(Text) +# policy_triggers = Column(Dict) +# policy_types = Column(Dict) +# outputs = Column(Dict) +# scaling_groups = Column(Dict) +# updated_at = Column(DateTime) +# workflows = Column(Dict) +# +# @declared_attr +# def blueprint_fk(cls): +# return cls.foreign_key(BlueprintBase, nullable=False) +# +# @declared_attr +# def blueprint(cls): +# return cls.many_to_one_relationship('blueprint_fk') +# +# @declared_attr +# def blueprint_name(cls): +# return association_proxy('blueprint', cls.name_column_name()) +# +# +# class ExecutionBase(ModelMixin): +# """ +# Execution model representation. +# """ +# # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column. +# __tablename__ = 'executions' +# _private_fields = ['deployment_fk'] +# +# TERMINATED = 'terminated' +# FAILED = 'failed' +# CANCELLED = 'cancelled' +# PENDING = 'pending' +# STARTED = 'started' +# CANCELLING = 'cancelling' +# FORCE_CANCELLING = 'force_cancelling' +# +# STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING] +# END_STATES = [TERMINATED, FAILED, CANCELLED] +# ACTIVE_STATES = [state for state in STATES if state not in END_STATES] +# +# VALID_TRANSITIONS = { +# PENDING: [STARTED, CANCELLED], +# STARTED: END_STATES + [CANCELLING], +# CANCELLING: END_STATES + [FORCE_CANCELLING] +# } +# +# @orm.validates('status') +# def validate_status(self, key, value): +# """Validation function that verifies execution status transitions are OK""" +# try: +# current_status = getattr(self, key) +# except AttributeError: +# return +# valid_transitions = self.VALID_TRANSITIONS.get(current_status, []) +# if all([current_status is not None, +# current_status != value, +# value not in valid_transitions]): +# raise ValueError('Cannot change execution status from {current} to {new}'.format( +# current=current_status, +# new=value)) +# return value +# +# created_at = Column(DateTime, index=True) +# started_at = Column(DateTime, nullable=True, index=True) +# ended_at = Column(DateTime, nullable=True, index=True) +# error = Column(Text, nullable=True) +# is_system_workflow = Column(Boolean, nullable=False, default=False) +# parameters = Column(Dict) +# status = Column(Enum(*STATES, name='execution_status'), default=PENDING) +# workflow_name = Column(Text) +# +# @declared_attr +# def blueprint(cls): +# return association_proxy('deployment', 'blueprint') +# +# @declared_attr +# def deployment_fk(cls): +# return cls.foreign_key(DeploymentBase, nullable=True) +# +# @declared_attr +# def deployment(cls): +# return cls.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_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.many_to_one_relationship('deployment_fk') +# +# @declared_attr +# def deployment_name(cls): +# return association_proxy('deployment', cls.name_column_name()) +# +# @declared_attr +# def blueprint_name(cls): +# return association_proxy('deployment', 'blueprint_{0}'.format(cls.name_column_name())) +# +# deploy_number_of_instances = Column(Integer, nullable=False) +# max_number_of_instances = Column(Integer, nullable=False) +# min_number_of_instances = Column(Integer, nullable=False) +# number_of_instances = Column(Integer, nullable=False) +# planned_number_of_instances = Column(Integer, nullable=False) +# plugins = Column(List) +# properties = Column(Dict) +# operations = Column(Dict) +# type = Column(Text, nullable=False, index=True) +# type_hierarchy = Column(List) +# +# +# class RelationshipBase(ModelMixin): +# """ +# Relationship model representation. +# """ +# __tablename__ = 'relationships' +# +# _private_fields = ['source_node_fk', 'target_node_fk', 'source_position', 'target_position'] +# +# source_position = Column(Integer) +# target_position = Column(Integer) +# +# @declared_attr +# def deployment_id(self): +# return association_proxy('source_node', 'deployment_id') +# +# @declared_attr +# def source_node_fk(cls): +# return cls.foreign_key(NodeBase) +# +# @declared_attr +# def source_node(cls): +# return cls.many_to_one_relationship( +# 'source_node_fk', +# backreference='outbound_relationships', +# backref_kwargs=dict( +# order_by=cls.source_position, +# collection_class=ordering_list('source_position', count_from=0) +# ) +# ) +# +# @declared_attr +# def source_name(cls): +# return association_proxy('source_node', cls.name_column_name()) +# +# @declared_attr +# def target_node_fk(cls): +# return cls.foreign_key(NodeBase, nullable=True) +# +# @declared_attr +# def target_node(cls): +# return cls.many_to_one_relationship( +# 'target_node_fk', +# backreference='inbound_relationships', +# backref_kwargs=dict( +# order_by=cls.target_position, +# collection_class=ordering_list('target_position', count_from=0) +# ) +# ) +# +# @declared_attr +# def target_name(cls): +# return association_proxy('target_node', cls.name_column_name()) +# +# source_interfaces = Column(Dict) +# source_operations = Column(Dict, nullable=False) +# target_interfaces = Column(Dict) +# target_operations = Column(Dict, nullable=False) +# type = Column(String, nullable=False) +# type_hierarchy = Column(List) +# properties = Column(Dict) +# +# +# class NodeInstanceBase(ModelMixin): +# """ +# Node instance model representation. +# """ +# __tablename__ = 'node_instances' +# _private_fields = ['node_fk', 'host_fk'] +# +# runtime_properties = Column(Dict) +# scaling_groups = Column(List) +# state = Column(Text, nullable=False) +# version = Column(Integer, default=1) +# +# @declared_attr +# def host_fk(cls): +# return cls.foreign_key(NodeInstanceBase, nullable=True) +# +# @declared_attr +# def host(cls): +# return cls.relationship_to_self('host_fk') +# +# @declared_attr +# def host_name(cls): +# return association_proxy('host', cls.name_column_name()) +# +# @declared_attr +# def deployment(cls): +# return association_proxy('node', 'deployment') +# +# @declared_attr +# def deployment_name(cls): +# return association_proxy('node', 'deployment_name') +# +# @declared_attr +# def node_fk(cls): +# return cls.foreign_key(NodeBase, nullable=True) +# +# @declared_attr +# def node(cls): +# return cls.many_to_one_relationship('node_fk') +# +# @declared_attr +# def node_name(cls): +# return association_proxy('node', cls.name_column_name()) +# +# @property +# def ip(self): +# if not self.host_fk: +# return None +# host_node_instance = self.host +# if 'ip' in host_node_instance.runtime_properties: # pylint: disable=no-member +# return host_node_instance.runtime_properties['ip'] # pylint: disable=no-member +# host_node = host_node_instance.node # pylint: disable=no-member +# if 'ip' in host_node.properties: +# return host_node.properties['ip'] +# return None +# +# +# class RelationshipInstanceBase(ModelMixin): +# """ +# Relationship instance model representation. +# """ +# __tablename__ = 'relationship_instances' +# _private_fields = ['relationship_storage_fk', +# 'source_node_instance_fk', +# 'target_node_instance_fk', +# 'source_position', +# 'target_position'] +# +# source_position = Column(Integer) +# target_position = Column(Integer) +# +# @declared_attr +# def source_node_instance_fk(cls): +# return cls.foreign_key(NodeInstanceBase, nullable=True) +# +# @declared_attr +# def source_node_instance(cls): +# return cls.many_to_one_relationship( +# 'source_node_instance_fk', +# backreference='outbound_relationship_instances', +# backref_kwargs=dict( +# order_by=cls.source_position, +# collection_class=ordering_list('source_position', count_from=0) +# ) +# ) +# +# @declared_attr +# def source_node_instance_name(cls): +# return association_proxy('source_node_instance', 'node_{0}'.format(cls.name_column_name())) +# +# @declared_attr +# def source_node_name(cls): +# return association_proxy('source_node_instance', cls.name_column_name()) +# +# @declared_attr +# def target_node_instance_fk(cls): +# return cls.foreign_key(NodeInstanceBase, nullable=True) +# +# @declared_attr +# def target_node_instance(cls): +# return cls.many_to_one_relationship( +# 'target_node_instance_fk', +# backreference='inbound_relationship_instances', +# backref_kwargs=dict( +# order_by=cls.target_position, +# collection_class=ordering_list('target_position', count_from=0) +# ) +# ) +# +# @declared_attr +# def target_node_instance_name(cls): +# return association_proxy('target_node_instance', cls.name_column_name()) +# +# @declared_attr +# def target_node_name(cls): +# return association_proxy('target_node_instance', 'node_{0}'.format(cls.name_column_name())) +# +# @declared_attr +# def relationship_fk(cls): +# return cls.foreign_key(RelationshipBase) +# +# @declared_attr +# def relationship(cls): +# return cls.many_to_one_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.many_to_one_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.many_to_one_relationship('relationship_instance_fk') +# +# @declared_attr +# def plugin_fk(cls): +# return cls.foreign_key(PluginBase, nullable=True) +# +# @declared_attr +# def plugin(cls): +# return cls.many_to_one_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.many_to_one_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) +# +# @staticmethod +# def abort(message=None): +# raise TaskAbortException(message) +# +# @staticmethod +# def retry(message=None, retry_interval=None): +# raise TaskRetryException(message, retry_interval=retry_interval)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/aria/storage/model.py ---------------------------------------------------------------------- diff --git a/aria/storage/model.py b/aria/storage/model.py index afca3e4..6533b7f 100644 --- a/aria/storage/model.py +++ b/aria/storage/model.py @@ -1,110 +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 +# # 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/9c6c378c/aria/storage/structure.py ---------------------------------------------------------------------- diff --git a/aria/storage/structure.py b/aria/storage/structure.py index 472b5ba..f9cd58b 100644 --- a/aria/storage/structure.py +++ b/aria/storage/structure.py @@ -37,6 +37,7 @@ from sqlalchemy import ( Table ) +from aria.storage.api import generate_lower_name from ..parser.modeling.elements import Element @@ -62,33 +63,29 @@ class ModelMixin(Element): return cls for table_cls in cls._decl_class_registry.values(): - if tablename in (getattr(table_cls, '__name__', None), - getattr(table_cls, '__tablename__', None)): + if tablename == getattr(table_cls, '__tablename__', None): return table_cls @classmethod - def foreign_key(cls, table, nullable=False): + def foreign_key(cls, table_name, nullable=False): """Return a ForeignKey object with the relevant :param table: Unique id column in the parent table :param nullable: Should the column be allowed to remain empty """ - 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 + return Column(Integer, + ForeignKey('{tablename}.id'.format(tablename=table_name), ondelete='CASCADE'), + nullable=nullable) @classmethod - def one_to_one_relationship(cls, table, backreference=None): - return relationship(table.__name__, + def one_to_one_relationship(cls, table_name, backreference=None): + return relationship(lambda: cls._get_cls_by_tablename(table_name), backref=backref(backreference or cls.__tablename__, uselist=False)) @classmethod def many_to_one_relationship(cls, - foreign_key_column, + parent_table_name, + foreign_key_column=None, backreference=None, backref_kwargs=None, **kwargs): @@ -100,31 +97,27 @@ class ModelMixin(Element): :param foreign_key_column: The column of the foreign key (from the child table) :param backreference: The name to give to the reference to the child (on the parent table) """ - backref_kwargs = backref_kwargs or {} - parent_table = cls._get_cls_by_tablename( - getattr(cls, foreign_key_column).__remote_table_name) - primaryjoin_str = '{parent_class_name}.{parent_unique_id} == ' \ - '{child_class.__name__}.{foreign_key_column}'\ - .format( - parent_class_name=parent_table.__name__, - parent_unique_id=parent_table.id_column_name(), - child_class=cls, - foreign_key_column=foreign_key_column - ) - return relationship( - parent_table.__name__, - primaryjoin=primaryjoin_str, - foreign_keys=[getattr(cls, foreign_key_column)], - # The following line make sure that when the *parent* is - # deleted, all its connected children are deleted as well - backref=backref(backreference or cls.__tablename__, cascade='all', **backref_kwargs), - **kwargs - ) + relationship_kwargs_ = kwargs + if foreign_key_column: + relationship_kwargs_['foreign_keys'] = [getattr(cls, foreign_key_column)] + + return relationship(lambda: cls._get_cls_by_tablename(parent_table_name), + backref=backref( + backreference or cls.__tablename__, + lazy='dynamic', + # The following line make sure that when the *parent* is + # deleted, all its connected children are deleted as well + cascade='all', + + **backref_kwargs or {}), + + **relationship_kwargs_) @classmethod - def one_to_many_relationship(cls, table, backrefernce=None): - return relationship(table.__name__, - backref=backrefernce or cls.__tablename__) + def one_to_many_relationship(cls, child_table_name, backrefernce=None): + return relationship(lambda: cls._get_cls_by_tablename(child_table_name), + backref=backrefernce or '{0}s'.format(cls.__tablename__), + lazy='dynamic') @classmethod def relationship_to_self(cls, local_column): @@ -137,13 +130,13 @@ class ModelMixin(Element): remote_side_str=remote_side_str, cls=cls, local_column=local_column) - return relationship(cls.__name__, + return relationship(cls._get_cls_by_tablename(cls.__tablename__).__name__, primaryjoin=primaryjoin_str, remote_side=remote_side_str, post_update=True) @classmethod - def many_to_many_relationship(cls, other_cls, table_prefix): + def many_to_many_relationship(cls, other_table_name, table_prefix): """Return a many-to-many SQL relationship object Notes: @@ -151,29 +144,18 @@ class ModelMixin(Element): 2. This method creates a new helper table in the DB :param cls: The class of the table we're connecting from - :param other_cls: The class of the table we're connecting to + :param other_table_name: The class of the table we're connecting to :param table_prefix: Custom prefix for the helper table name and the backreference name """ current_table_name = cls.__tablename__ current_column_name = '{0}_id'.format(current_table_name) - current_foreign_key = '{0}.{1}'.format( - current_table_name, - cls.id_column_name() - ) + current_foreign_key = '{0}.id'.format(current_table_name) - other_cls = cls._get_cls_by_tablename(other_cls.__tablename__) - other_table_name = other_cls.__tablename__ other_column_name = '{0}_id'.format(other_table_name) - other_foreign_key = '{0}.{1}'.format( - other_table_name, - other_cls.id_column_name() - ) + other_foreign_key = '{0}.id'.format(other_table_name) - helper_table_name = '{0}_{1}'.format( - current_table_name, - other_table_name - ) + helper_table_name = '{0}_{1}'.format(current_table_name, other_table_name) backref_name = current_table_name if table_prefix: @@ -188,8 +170,9 @@ class ModelMixin(Element): current_foreign_key, other_foreign_key ) + return relationship( - other_cls, + lambda: cls._get_cls_by_tablename(other_table_name), secondary=secondary_table, backref=backref(backref_name) ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/tests/mock/models.py ---------------------------------------------------------------------- diff --git a/tests/mock/models.py b/tests/mock/models.py index 87fa352..e5f29ff 100644 --- a/tests/mock/models.py +++ b/tests/mock/models.py @@ -35,11 +35,6 @@ RELATIONSHIP_INSTANCE_NAME = 'relationship_instance' def get_dependency_node(deployment): - operation_templates = [models.OperationTemplate(implementation=op, - service_template=deployment.service_template) - for op in operations.NODE_OPERATIONS] - interface_template = models.InterfaceTemplate(operation_templates=operation_templates) - return models.NodeTemplate( name=DEPENDENCY_NODE_NAME, type_name='test_node_type', @@ -48,7 +43,7 @@ def get_dependency_node(deployment): min_instances=1, max_instances=1, service_template=deployment.service_template, - interface_templates=[interface_template], + # interface_templates=[get_interface_template(op) for op in operations.NODE_OPERATIONS], ) @@ -162,3 +157,29 @@ def get_plugin(package_name='package', package_version='0.1'): uploaded_at=datetime.now(), wheels=[], ) + + +def get_interface_template(operation_name, operation_kwargs=None, interface_kwargs=None): + operation_template = models.OperationTemplate( + name=operation_name, + **(operation_kwargs or {}) + + ) + return models.InterfaceTemplate( + operation_templates=[operation_template], + name=operation_name.rsplit('.', 1)[0], + **(interface_kwargs or {}) + ) + + +def get_interface(operation_name, operation_kwargs=None, interface_kwargs=None): + operation = models.Operation( + name=operation_name, + **(operation_kwargs or {}) + ) + + return models.Interface( + operations=[operation], + name=operation_name.rsplit('.', 1)[0], + **(interface_kwargs or {}) + ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/tests/orchestrator/workflows/api/test_task.py ---------------------------------------------------------------------- diff --git a/tests/orchestrator/workflows/api/test_task.py b/tests/orchestrator/workflows/api/test_task.py index 198e461..2393af4 100644 --- a/tests/orchestrator/workflows/api/test_task.py +++ b/tests/orchestrator/workflows/api/test_task.py @@ -18,6 +18,7 @@ import pytest from aria.orchestrator import context from aria.orchestrator.workflows import api +from aria.modeling import models from tests import mock, storage @@ -39,16 +40,19 @@ class TestOperationTask(object): def test_node_operation_task_creation(self, ctx): operation_name = 'aria.interfaces.lifecycle.create' - op_details = {'operation': True, 'plugin': 'plugin'} + interface_template = mock.models.get_interface_template( + operation_name, + operation_kwargs=dict(plugin='plugin', operation=True)) + node = ctx.model.node_template.get_by_name(mock.models.DEPENDENT_NODE_NAME) - node.operations[operation_name] = op_details + node.interface_templates = [interface_template] node.plugins = [{'name': 'plugin', 'package_name': 'package', 'package_version': '0.1'}] ctx.model.node_template.update(node) node_instance = \ ctx.model.node.get_by_name(mock.models.DEPENDENT_NODE_INSTANCE_NAME) - inputs = {'inputs': True} + inputs = [models.Parameter(type_name='Boolean', value=True)] max_attempts = 10 retry_interval = 10 ignore_failure = True @@ -75,14 +79,18 @@ class TestOperationTask(object): def test_source_relationship_operation_task_creation(self, ctx): operation_name = 'aria.interfaces.relationship_lifecycle.preconfigure' - op_details = {'operation': True, 'plugin': 'plugin'} + + interface = mock.models.get_interface( + operation_name, + operation_kwargs=dict(operation=True, plugin='plugin')) + relationship = ctx.model.relationship.list()[0] - relationship.source_operations[operation_name] = op_details + relationship.source_interfaces = [interface] relationship.source_node.plugins = [{'name': 'plugin', 'package_name': 'package', 'package_version': '0.1'}] relationship_instance = ctx.model.relationship_instance.list()[0] - inputs = {'inputs': True} + input = models.Parameter(type_name='Boolean', value=True) max_attempts = 10 retry_interval = 10 @@ -91,7 +99,7 @@ class TestOperationTask(object): name=operation_name, instance=relationship_instance, operation_end=api.task.OperationTask.SOURCE_OPERATION, - inputs=inputs, + inputs=[input], max_attempts=max_attempts, retry_interval=retry_interval) @@ -138,7 +146,7 @@ class TestOperationTask(object): 'package_version': '0.1'} def test_operation_task_default_values(self, ctx): - dependency_node_instance = ctx.model.node_instance.get_by_name( + dependency_node_instance = ctx.model.node.get_by_name( mock.models.DEPENDENCY_NODE_INSTANCE_NAME) with context.workflow.current.push(ctx): task = api.task.OperationTask( http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/tests/storage/__init__.py ---------------------------------------------------------------------- diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py index 1c8b41b..acdfc8a 100644 --- a/tests/storage/__init__.py +++ b/tests/storage/__init__.py @@ -17,7 +17,7 @@ import platform from tempfile import mkdtemp from shutil import rmtree -from aria.storage import model +from aria.modeling import models from sqlalchemy import ( create_engine, orm) @@ -34,9 +34,9 @@ class TestFileSystem(object): rmtree(self.path, ignore_errors=True) -def get_sqlite_api_kwargs(base_dir=None, - filename='db.sqlite', - declarative_base=model.DeclarativeBase): +def get_sqlite_api_kwargs(declarative_base=models.DB, + base_dir=None, + filename='db.sqlite'): """ Create sql params. works in in-memory and in filesystem mode. If base_dir is passed, the mode will be filesystem mode. while the default mode is in-memory. @@ -61,7 +61,8 @@ def get_sqlite_api_kwargs(base_dir=None, session_factory = orm.sessionmaker(bind=engine) session = scoped_session(session_factory=session_factory) if base_dir else session_factory() - declarative_base.metadata.create_all(bind=engine) + if declarative_base: + declarative_base.metadata.create_all(bind=engine) return dict(engine=engine, session=session) @@ -78,4 +79,4 @@ def release_sqlite_storage(storage): session.rollback() session.close() for engine in set(mapi._engine for mapi in mapis): - model.DeclarativeBase.metadata.drop_all(engine) + models.DB.metadata.drop_all(engine) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/tests/storage/test_model_storage.py ---------------------------------------------------------------------- diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py index d9b7329..b96c2b2 100644 --- a/tests/storage/test_model_storage.py +++ b/tests/storage/test_model_storage.py @@ -25,6 +25,7 @@ from aria.storage import ( structure, type as aria_type, ) +from aria import modeling from aria import application_model_storage from ..storage import get_sqlite_api_kwargs, release_sqlite_storage from ..mock import ( @@ -34,7 +35,7 @@ from ..mock import ( ) -class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method +class MockModel(modeling.models.DB, structure.ModelMixin): #pylint: disable=abstract-method __tablename__ = 'mock_models' model_dict = Column(aria_type.Dict) model_list = Column(aria_type.List) @@ -57,7 +58,7 @@ def context(): @pytest.fixture(scope='module', autouse=True) def module_cleanup(): - model.DeclarativeBase.metadata.remove(MockModel.__table__) #pylint: disable=no-member + modeling.models.DB.metadata.remove(MockModel.__table__) #pylint: disable=no-member def test_storage_base(storage): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9c6c378c/tests/storage/test_new_modelling.py ---------------------------------------------------------------------- diff --git a/tests/storage/test_new_modelling.py b/tests/storage/test_new_modelling.py index ff96d3d..477144b 100644 --- a/tests/storage/test_new_modelling.py +++ b/tests/storage/test_new_modelling.py @@ -14,8 +14,6 @@ from ..storage import ( release_sqlite_storage ) -DB = declarative_base(cls=structure.ModelIDMixin) - tosca_classes = [ models.Parameter, @@ -59,7 +57,7 @@ tosca_classes = [ @pytest.fixture def storage(): base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, - api_kwargs=get_sqlite_api_kwargs(declarative_base=DB), + api_kwargs=get_sqlite_api_kwargs(), items=tosca_classes) yield base_storage release_sqlite_storage(base_storage) @@ -71,5 +69,5 @@ def test_models_creation(storage): :param storage: :return: """ - for cls in tosca_classes: - assert len(getattr(storage, api.generate_lower_name(cls)).list()) == 0 + for cls in storage.registered.values(): + assert len(cls.list()) == 0
