http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/elements.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/elements.py b/aria/storage/modeling/elements.py deleted file mode 100644 index 8c720b9..0000000 --- a/aria/storage/modeling/elements.py +++ /dev/null @@ -1,106 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sqlalchemy import ( - Column, - Text -) - -from ...parser.modeling import utils -from ...utils.collections import OrderedDict -from ...utils.console import puts -from .. import exceptions - -from . import structure -from . import type - -# pylint: disable=no-self-argument, no-member, abstract-method - - -class ParameterBase(structure.ModelMixin): - """ - Represents a typed value. - - This class is used by both service model and service instance elements. - """ - __tablename__ = 'parameter' - name = Column(Text, nullable=False) - type = Column(Text, nullable=False) - - # Check: value type - str_value = Column(Text) - description = Column(Text) - - @property - def as_raw(self): - return OrderedDict(( - ('name', self.name), - ('type_name', self.type), - ('value', self.value), - ('description', self.description))) - - @property - def value(self): - if self.type is None: - return - try: - if self.type.lower() in ['str', 'unicode']: - return self.str_value.decode('utf-8') - elif self.type.lower() == 'int': - return int(self.str_value) - elif self.type.lower() == 'bool': - return bool(self.str_value) - elif self.type.lower() == 'float': - return float(self.str_value) - else: - raise exceptions.StorageError('No supported type_name was provided') - except ValueError: - raise exceptions.StorageError('Trying to cast {0} to {1} failed'.format(self.str_value, - self.type)) - - def instantiate(self, context, container): - return ParameterBase(self.type, self.str_value, self.description) - - def coerce_values(self, context, container, report_issues): - if self.str_value is not None: - self.str_value = utils.coerce_value(context, container, self.str_value, report_issues) - - -class MetadataBase(structure.ModelMixin): - """ - Custom values associated with the deployment template and its plans. - - This class is used by both service model and service instance elements. - - Properties: - - * :code:`values`: Dict of custom values - """ - values = Column(type.StrictDict(key_cls=basestring)) - - @property - def as_raw(self): - return self.values - - def instantiate(self, context, container): - metadata = MetadataBase() - metadata.values.update(self.values) - return metadata - - def dump(self, context): - puts('Metadata:') - with context.style.indent: - for name, value in self.values.iteritems(): - puts('%s: %s' % (name, context.style.meta(value)))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/instance_elements.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/instance_elements.py b/aria/storage/modeling/instance_elements.py deleted file mode 100644 index 86e2ea6..0000000 --- a/aria/storage/modeling/instance_elements.py +++ /dev/null @@ -1,1288 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sqlalchemy import ( - Column, - Text, - Integer, - Boolean, -) -from sqlalchemy import DateTime -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.ext.declarative import declared_attr -from sqlalchemy.ext.orderinglist import ordering_list - -from aria.parser import validation -from aria.utils import collections, formatting, console - -from . import ( - utils, - structure, - type as aria_types -) - -# pylint: disable=no-self-argument, no-member, abstract-method - -# region Element instances - - -class ServiceInstanceBase(structure.ModelMixin): - __tablename__ = 'service_instance' - - __private_fields__ = ['substituion_fk', - 'service_template_fk'] - - description = Column(Text) - _metadata = Column(Text) - - # region orchestrator required columns - - created_at = Column(DateTime, nullable=False, index=True) - permalink = Column(Text) - policy_triggers = Column(aria_types.Dict) - policy_types = Column(aria_types.Dict) - scaling_groups = Column(aria_types.Dict) - updated_at = Column(DateTime) - workflows = Column(aria_types.Dict) - - @declared_attr - def service_template_name(cls): - return association_proxy('service_template', 'name') - - # endregion - - # region foreign keys - @declared_attr - def substitution_fk(cls): - return cls.foreign_key('substitution', nullable=True) - - @declared_attr - def service_template_fk(cls): - return cls.foreign_key('service_template') - - # endregion - - # region one-to-one relationships - @declared_attr - def substitution(cls): - return cls.one_to_one_relationship('substitution') - # endregion - - # region many-to-one relationships - @declared_attr - def service_template(cls): - return cls.many_to_one_relationship('service_template') - - # endregion - - # region many-to-many relationships - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - @declared_attr - def outputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='outputs') - - # endregion - - # association proxies - - def satisfy_requirements(self, context): - satisfied = True - for node in self.nodes.all(): - if not node.satisfy_requirements(context): - satisfied = False - return satisfied - - def validate_capabilities(self, context): - satisfied = True - for node in self.nodes.all(): - if not node.validate_capabilities(context): - satisfied = False - return satisfied - - def find_nodes(self, node_template_name): - nodes = [] - for node in self.nodes.all(): - if node.template_name == node_template_name: - nodes.append(node) - return collections.FrozenList(nodes) - - def get_node_ids(self, node_template_name): - return collections.FrozenList((node.id for node in self.find_nodes(node_template_name))) - - def find_groups(self, group_template_name): - groups = [] - for group in self.groups.all(): - if group.template_name == group_template_name: - groups.append(group) - return collections.FrozenList(groups) - - def get_group_ids(self, group_template_name): - return collections.FrozenList((group.id for group in self.find_groups(group_template_name))) - - def is_node_a_target(self, context, target_node): - for node in self.nodes.all(): - if self._is_node_a_target(context, node, target_node): - return True - return False - - def _is_node_a_target(self, context, source_node, target_node): - if source_node.relationships: - for relationship in source_node.relationships: - if relationship.target_node_id == target_node.id: - return True - else: - node = context.modeling.instance.nodes.get(relationship.target_node_id) - if node is not None: - if self._is_node_a_target(context, node, target_node): - return True - return False - - -class OperationBase(structure.ModelMixin): - """ - An operation in a :class:`Interface`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`implementation`: Implementation string (interpreted by the orchestrator) - * :code:`dependencies`: List of strings (interpreted by the orchestrator) - * :code:`executor`: Executor string (interpreted by the orchestrator) - * :code:`max_retries`: Maximum number of retries allowed in case of failure - * :code:`retry_interval`: Interval between retries - * :code:`inputs`: Dict of :class:`Parameter` - """ - __tablename__ = 'operation' - - __private_fields__ = ['service_template_fk', - 'interface_instance_fk'] - - # region foreign_keys - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance', nullable=True) - - @declared_attr - def interface_instance_fk(cls): - return cls.foreign_key('interface', nullable=True) - - # endregion - description = Column(Text) - implementation = Column(Text) - dependencies = Column(aria_types.StrictList(item_cls=basestring)) - - executor = Column(Text) - max_retries = Column(Integer, default=None) - retry_interval = Column(Integer, default=None) - plugin = Column(Text) - operation = Column(Boolean) - - # region many-to-one relationships - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - @declared_attr - def interface(cls): - return cls.many_to_one_relationship('interface') - # region many-to-many relationships - - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('implementation', self.implementation), - ('dependencies', self.dependencies), - ('executor', self.executor), - ('max_retries', self.max_retries), - ('retry_interval', self.retry_interval), - ('inputs', formatting.as_raw_dict(self.inputs)))) - - def validate(self, context): - utils.validate_dict_values(context, self.inputs) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.inputs, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - if self.implementation is not None: - console.puts('Implementation: %s' % context.style.literal(self.implementation)) - if self.dependencies: - console.puts( - 'Dependencies: %s' - % ', '.join((str(context.style.literal(v)) for v in self.dependencies))) - if self.executor is not None: - console.puts('Executor: %s' % context.style.literal(self.executor)) - if self.max_retries is not None: - console.puts('Max retries: %s' % context.style.literal(self.max_retries)) - if self.retry_interval is not None: - console.puts('Retry interval: %s' % context.style.literal(self.retry_interval)) - utils.dump_parameters(context, self.inputs, 'Inputs') - - -class InterfaceBase(structure.ModelMixin): - """ - A typed set of :class:`Operation`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`inputs`: Dict of :class:`Parameter` - * :code:`operations`: Dict of :class:`Operation` - """ - __tablename__ = 'interface' - - __private_fields__ = ['group_fk', - 'node_fk', - 'relationship_fk'] - - - # region foreign_keys - @declared_attr - def group_fk(cls): - return cls.foreign_key('group', nullable=True) - - @declared_attr - def node_fk(cls): - return cls.foreign_key('node', nullable=True) - - @declared_attr - def relationship_fk(cls): - return cls.foreign_key('relationship', nullable=True) - - # endregion - - description = Column(Text) - type_name = Column(Text) - edge = Column(Text) - - # region many-to-one relationships - - @declared_attr - def node(cls): - return cls.many_to_one_relationship('node') - - @declared_attr - def relationship(cls): - return cls.many_to_one_relationship('relationship') - - @declared_attr - def group(cls): - return cls.many_to_one_relationship('group') - - # endregion - - # region many-to-many relationships - - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('inputs', formatting.as_raw_dict(self.inputs)), - ('operations', formatting.as_raw_list(self.operations)))) - - def validate(self, context): - if self.type_name: - if context.modeling.interface_types.get_descendant(self.type_name) is None: - context.validation.report('interface "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.inputs) - utils.validate_dict_values(context, self.operations) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.inputs, report_issues) - utils.coerce_dict_values(context, container, self.operations, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Interface type: %s' % context.style.type(self.type_name)) - utils.dump_parameters(context, self.inputs, 'Inputs') - utils.dump_dict_values(context, self.operations, 'Operations') - - -class CapabilityBase(structure.ModelMixin): - """ - A capability of a :class:`Node`. - - An instance of a :class:`CapabilityTemplate`. - - Properties: - - * :code:`name`: Name - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`min_occurrences`: Minimum number of requirement matches required - * :code:`max_occurrences`: Maximum number of requirement matches allowed - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'capability' - - __private_fields__ = ['node_fk'] - - # region foreign_keys - @declared_attr - def node_fk(cls): - return cls.foreign_key('node') - - # endregion - type_name = Column(Text) - - min_occurrences = Column(Integer, default=None) # optional - max_occurrences = Column(Integer, default=None) # optional - occurrences = Column(Integer, default=0) - - # region many-to-one relationships - @declared_attr - def node(cls): - return cls.many_to_one_relationship('node') - - # endregion - - - # region many-to-many relationships - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def has_enough_relationships(self): - if self.min_occurrences is not None: - return self.occurrences >= self.min_occurrences - return True - - def relate(self): - if self.max_occurrences is not None: - if self.occurrences == self.max_occurrences: - return False - self.occurrences += 1 - return True - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)))) - - def validate(self, context): - if context.modeling.capability_types.get_descendant(self.type_name) is None: - context.validation.report('capability "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - console.puts('Occurrences: %s (%s%s)' - % (self.occurrences, - self.min_occurrences or 0, - (' to %d' % self.max_occurrences) - if self.max_occurrences is not None - else ' or more')) - utils.dump_parameters(context, self.properties) - - -class ArtifactBase(structure.ModelMixin): - """ - A file associated with a :class:`Node`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`source_path`: Source path (CSAR or repository) - * :code:`target_path`: Path at destination machine - * :code:`repository_url`: Repository URL - * :code:`repository_credential`: Dict of string - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'artifact' - - __private_fields__ = ['node_fk'] - - # region foreign_keys - - @declared_attr - def node_fk(cls): - return cls.foreign_key('node') - - # endregion - - description = Column(Text) - type_name = Column(Text) - source_path = Column(Text) - target_path = Column(Text) - repository_url = Column(Text) - repository_credential = Column(aria_types.StrictDict(basestring, basestring)) - - # region many-to-one relationships - @declared_attr - def node(cls): - return cls.many_to_one_relationship('node') - - # endregion - - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('source_path', self.source_path), - ('target_path', self.target_path), - ('repository_url', self.repository_url), - ('repository_credential', formatting.as_agnostic(self.repository_credential)), - ('properties', formatting.as_raw_dict(self.properties)))) - - def validate(self, context): - if context.modeling.artifact_types.get_descendant(self.type_name) is None: - context.validation.report('artifact "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Artifact type: %s' % context.style.type(self.type_name)) - console.puts('Source path: %s' % context.style.literal(self.source_path)) - if self.target_path is not None: - console.puts('Target path: %s' % context.style.literal(self.target_path)) - if self.repository_url is not None: - console.puts('Repository URL: %s' % context.style.literal(self.repository_url)) - if self.repository_credential: - console.puts('Repository credential: %s' - % context.style.literal(self.repository_credential)) - utils.dump_parameters(context, self.properties) - - -class PolicyBase(structure.ModelMixin): - """ - An instance of a :class:`PolicyTemplate`. - - Properties: - - * :code:`name`: Name - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`target_node_ids`: Must be represented in the :class:`ServiceInstance` - * :code:`target_group_ids`: Must be represented in the :class:`ServiceInstance` - """ - __tablename__ = 'policy' - - __private_fields__ = ['service_instance_fk'] - - # region foreign_keys - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - # endregion - type_name = Column(Text) - target_node_ids = Column(aria_types.StrictList(basestring)) - target_group_ids = Column(aria_types.StrictList(basestring)) - - # region many-to-one relationships - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('target_node_ids', self.target_node_ids), - ('target_group_ids', self.target_group_ids))) - - def validate(self, context): - if context.modeling.policy_types.get_descendant(self.type_name) is None: - context.validation.report('policy "%s" has an unknown type: %s' - % (self.name, utils.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts('Policy: %s' % context.style.node(self.name)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - utils.dump_parameters(context, self.properties) - if self.target_node_ids: - console.puts('Target nodes:') - with context.style.indent: - for node_id in self.target_node_ids: - console.puts(context.style.node(node_id)) - if self.target_group_ids: - console.puts('Target groups:') - with context.style.indent: - for group_id in self.target_group_ids: - console.puts(context.style.node(group_id)) - - -class GroupPolicyBase(structure.ModelMixin): - """ - Policies applied to groups. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`triggers`: Dict of :class:`GroupPolicyTrigger` - """ - __tablename__ = 'group_policy' - - __private_fields__ = ['group_fk'] - - # region foreign_keys - - @declared_attr - def group_fk(cls): - return cls.foreign_key('group') - - # endregion - - description = Column(Text) - type_name = Column(Text) - - # region many-to-one relationships - @declared_attr - def group(cls): - return cls.many_to_one_relationship('group') - - # end region - - # region many-to-many relationships - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('triggers', formatting.as_raw_list(self.triggers)))) - - def validate(self, context): - if context.modeling.policy_types.get_descendant(self.type_name) is None: - context.validation.report( - 'group policy "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.triggers) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - utils.coerce_dict_values(context, container, self.triggers, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Group policy type: %s' % context.style.type(self.type_name)) - utils.dump_parameters(context, self.properties) - utils.dump_dict_values(context, self.triggers, 'Triggers') - - -class GroupPolicyTriggerBase(structure.ModelMixin): - """ - Triggers for :class:`GroupPolicy`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`implementation`: Implementation string (interpreted by the orchestrator) - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'group_policy_trigger' - - __private_fields__ = ['group_policy_fk'] - - # region foreign keys - - @declared_attr - def group_policy_fk(cls): - return cls.foreign_key('group_policy') - - # endregion - - description = Column(Text) - implementation = Column(Text) - - # region many-to-one relationships - - @declared_attr - def group_policy(cls): - return cls.many_to_one_relationship('group_policy') - - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('implementation', self.implementation), - ('properties', formatting.as_raw_dict(self.properties)))) - - def validate(self, context): - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Implementation: %s' % context.style.literal(self.implementation)) - utils.dump_parameters(context, self.properties) - - -class MappingBase(structure.ModelMixin): - """ - An instance of a :class:`MappingTemplate`. - - Properties: - - * :code:`mapped_name`: Exposed capability or requirement name - * :code:`node_id`: Must be represented in the :class:`ServiceInstance` - * :code:`name`: Name of capability or requirement at the node - """ - __tablename__ = 'mapping' - - mapped_name = Column(Text) - node_id = Column(Text) - - @property - def as_raw(self): - return collections.OrderedDict(( - ('mapped_name', self.mapped_name), - ('node_id', self.node_id), - ('name', self.name))) - - def dump(self, context): - console.puts('%s -> %s.%s' - % (context.style.node(self.mapped_name), - context.style.node(self.node_id), - context.style.node(self.name))) - - -class SubstitutionBase(structure.ModelMixin): - """ - An instance of a :class:`SubstitutionTemplate`. - - Properties: - - * :code:`node_type_name`: Must be represented in the :class:`ModelingContext` - * :code:`capabilities`: Dict of :class:`Mapping` - * :code:`requirements`: Dict of :class:`Mapping` - """ - __tablename__ = 'substitution' - - node_type_name = Column(Text) - - # region many-to-many relationships - - @declared_attr - def capabilities(cls): - return cls.many_to_many_relationship('mapping', table_prefix='capabilities') - - @declared_attr - def requirements(cls): - return cls.many_to_many_relationship('mapping', - table_prefix='requirements', - relationship_kwargs=dict(lazy='dynamic')) - - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('node_type_name', self.node_type_name), - ('capabilities', formatting.as_raw_list(self.capabilities)), - ('requirements', formatting.as_raw_list(self.requirements)))) - - def validate(self, context): - if context.modeling.node_types.get_descendant(self.node_type_name) is None: - context.validation.report('substitution "%s" has an unknown type: %s' - % (self.name, # pylint: disable=no-member - # TODO fix self.name reference - formatting.safe_repr(self.node_type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.capabilities) - utils.validate_dict_values(context, self.requirements) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.capabilities, report_issues) - utils.coerce_dict_values(context, container, self.requirements, report_issues) - - def dump(self, context): - console.puts('Substitution:') - with context.style.indent: - console.puts('Node type: %s' % context.style.type(self.node_type_name)) - utils.dump_dict_values(context, self.capabilities, 'Capability mappings') - utils.dump_dict_values(context, self.requirements, 'Requirement mappings') - - -# endregion - -# region Node instances - -class NodeBase(structure.ModelMixin): - """ - An instance of a :class:`NodeTemplate`. - - Nodes may have zero or more :class:`Relationship` instances to other nodes. - - Properties: - - * :code:`id`: Unique ID (prefixed with the template name) - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`template_name`: Must be represented in the :class:`ServiceModel` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`interfaces`: Dict of :class:`Interface` - * :code:`artifacts`: Dict of :class:`Artifact` - * :code:`capabilities`: Dict of :class:`CapabilityTemplate` - * :code:`relationships`: List of :class:`Relationship` - """ - __tablename__ = 'node' - version = Column(Integer, nullable=False) - __mapper_args__ = {'version_id_col': version} - - __private_fields__ = ['service_instance_fk', - 'host_fk', - 'node_template_fk'] - - # region foreign_keys - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - @declared_attr - def host_fk(cls): - return cls.foreign_key('node', nullable=True) - - @declared_attr - def node_template_fk(cls): - return cls.foreign_key('node_template') - - # endregion - - type_name = Column(Text) - template_name = Column(Text) - - # region orchestrator required columns - runtime_properties = Column(aria_types.Dict) - scaling_groups = Column(aria_types.List) - state = Column(Text, nullable=False) - - @declared_attr - def plugins(cls): - return association_proxy('node_template', 'plugins') - - @declared_attr - def host(cls): - return cls.relationship_to_self('host_fk') - - @declared_attr - def service_instance_name(cls): - return association_proxy('service_instance', 'name') - - @property - def ip(self): - if not self.host_fk: - return None - host_node = self.host - if 'ip' in host_node.runtime_properties: # pylint: disable=no-member - return host_node.runtime_properties['ip'] # pylint: disable=no-member - host_node = host_node.node_template # pylint: disable=no-member - host_ip_property = [prop for prop in host_node.properties if prop.name == 'ip'] - if host_ip_property: - return host_ip_property[0].value - return None - - @declared_attr - def node_template(cls): - return cls.many_to_one_relationship('node_template') - - @declared_attr - def service_template(cls): - return association_proxy('service_instance', 'service_template') - # endregion - - # region many-to-one relationships - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - def satisfy_requirements(self, context): - node_template = context.modeling.model.node_templates.get(self.template_name) - satisfied = True - for i in range(len(node_template.requirement_templates)): - requirement_template = node_template.requirement_templates[i] - - # Find target template - target_node_template, target_node_capability = \ - requirement_template.find_target(context, node_template) - if target_node_template is not None: - satisfied = self._satisfy_capability(context, - target_node_capability, - target_node_template, - requirement_template, - requirement_template_index=i) - else: - context.validation.report('requirement "%s" of node "%s" has no target node ' - 'template' % (requirement_template.name, - self.id), - level=validation.Issue.BETWEEN_INSTANCES) - satisfied = False - return satisfied - - def _satisfy_capability(self, context, target_node_capability, target_node_template, - requirement_template, requirement_template_index): - # Find target nodes - target_nodes = context.modeling.instance.find_nodes(target_node_template.name) - if target_nodes: - target_node = None - target_capability = None - - if target_node_capability is not None: - # Relate to the first target node that has capacity - for node in target_nodes: - target_capability = node.capabilities.get(target_node_capability.name) - if target_capability.relate(): - target_node = node - break - else: - # Use first target node - target_node = target_nodes[0] - - if target_node is not None: - relationship = RelationshipBase( - name=requirement_template.name, - source_requirement_index=requirement_template_index, - target_node_id=target_node.id, - target_capability_name=target_capability.name - ) - self.relationships.append(relationship) - else: - context.validation.report('requirement "%s" of node "%s" targets node ' - 'template "%s" but its instantiated nodes do not ' - 'have enough capacity' - % (requirement_template.name, - self.id, - target_node_template.name), - level=validation.Issue.BETWEEN_INSTANCES) - return False - else: - context.validation.report('requirement "%s" of node "%s" targets node template ' - '"%s" but it has no instantiated nodes' - % (requirement_template.name, - self.id, - target_node_template.name), - level=validation.Issue.BETWEEN_INSTANCES) - return False - - def validate_capabilities(self, context): - satisfied = False - for capability in self.capabilities.itervalues(): - if not capability.has_enough_relationships: - context.validation.report('capability "%s" of node "%s" requires at least %d ' - 'relationships but has %d' - % (capability.name, - self.id, - capability.min_occurrences, - capability.occurrences), - level=validation.Issue.BETWEEN_INSTANCES) - satisfied = False - return satisfied - - @property - def as_raw(self): - return collections.OrderedDict(( - ('id', self.id), - ('type_name', self.type_name), - ('template_name', self.template_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('interfaces', formatting.as_raw_list(self.interfaces)), - ('artifacts', formatting.as_raw_list(self.artifacts)), - ('capabilities', formatting.as_raw_list(self.capabilities)), - ('relationships', formatting.as_raw_list(self.relationships)))) - - def validate(self, context): - if len(self.id) > context.modeling.id_max_length: - context.validation.report('"%s" has an ID longer than the limit of %d characters: %d' - % (self.id, - context.modeling.id_max_length, - len(self.id)), - level=validation.Issue.BETWEEN_INSTANCES) - - # TODO: validate that node template is of type? - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.interfaces) - utils.validate_dict_values(context, self.artifacts) - utils.validate_dict_values(context, self.capabilities) - utils.validate_list_values(context, self.relationships) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.properties, report_issues) - utils.coerce_dict_values(context, self, self.interfaces, report_issues) - utils.coerce_dict_values(context, self, self.artifacts, report_issues) - utils.coerce_dict_values(context, self, self.capabilities, report_issues) - utils.coerce_list_values(context, self, self.relationships, report_issues) - - def dump(self, context): - console.puts('Node: %s' % context.style.node(self.id)) - with context.style.indent: - console.puts('Template: %s' % context.style.node(self.template_name)) - console.puts('Type: %s' % context.style.type(self.type_name)) - utils.dump_parameters(context, self.properties) - utils.dump_interfaces(context, self.interfaces) - utils.dump_dict_values(context, self.artifacts, 'Artifacts') - utils.dump_dict_values(context, self.capabilities, 'Capabilities') - utils.dump_list_values(context, self.relationships, 'Relationships') - - -class GroupBase(structure.ModelMixin): - """ - An instance of a :class:`GroupTemplate`. - - Properties: - - * :code:`id`: Unique ID (prefixed with the template name) - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`template_name`: Must be represented in the :class:`ServiceModel` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`interfaces`: Dict of :class:`Interface` - * :code:`policies`: Dict of :class:`GroupPolicy` - * :code:`member_node_ids`: Must be represented in the :class:`ServiceInstance` - * :code:`member_group_ids`: Must be represented in the :class:`ServiceInstance` - """ - __tablename__ = 'group' - - __private_fields__ = ['service_instance_fk'] - - # region foreign_keys - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - # endregion - - type_name = Column(Text) - template_name = Column(Text) - member_node_ids = Column(aria_types.StrictList(basestring)) - member_group_ids = Column(aria_types.StrictList(basestring)) - - # region many-to-one relationships - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - # region many-to-many relationships - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('id', self.id), - ('type_name', self.type_name), - ('template_name', self.template_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('interfaces', formatting.as_raw_list(self.interfaces)), - ('policies', formatting.as_raw_list(self.policies)), - ('member_node_ids', self.member_node_ids), - ('member_group_ids', self.member_group_ids))) - - def validate(self, context): - if context.modeling.group_types.get_descendant(self.type_name) is None: - context.validation.report('group "%s" has an unknown type: %s' - % (self.name, # pylint: disable=no-member - # TODO fix self.name reference - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.interfaces) - utils.validate_dict_values(context, self.policies) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - utils.coerce_dict_values(context, container, self.interfaces, report_issues) - utils.coerce_dict_values(context, container, self.policies, report_issues) - - def dump(self, context): - console.puts('Group: %s' % context.style.node(self.id)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - console.puts('Template: %s' % context.style.type(self.template_name)) - utils.dump_parameters(context, self.properties) - utils.dump_interfaces(context, self.interfaces) - utils.dump_dict_values(context, self.policies, 'Policies') - if self.member_node_ids: - console.puts('Member nodes:') - with context.style.indent: - for node_id in self.member_node_ids: - console.puts(context.style.node(node_id)) - -# endregion - -# region Relationship instances - - -class RelationshipBase(structure.ModelMixin): - """ - Connects :class:`Node` to another node. - - An instance of a :class:`RelationshipTemplate`. - - Properties: - - * :code:`name`: Name (usually the name of the requirement at the source node template) - * :code:`source_requirement_index`: Must be represented in the source node template - * :code:`target_node_id`: Must be represented in the :class:`ServiceInstance` - * :code:`target_capability_name`: Matches the capability at the target node - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`template_name`: Must be represented in the :class:`ServiceModel` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`source_interfaces`: Dict of :class:`Interface` - * :code:`target_interfaces`: Dict of :class:`Interface` - """ - __tablename__ = 'relationship' - - __private_fields__ = ['source_node_fk', - 'target_node_fk'] - - source_requirement_index = Column(Integer) - target_node_id = Column(Text) - target_capability_name = Column(Text) - type_name = Column(Text) - template_name = Column(Text) - type_hierarchy = Column(aria_types.List) - - # region orchestrator required columns - source_position = Column(Integer) - target_position = Column(Integer) - - @declared_attr - def source_node_fk(cls): - return cls.foreign_key('node', nullable=True) - - @declared_attr - def source_node(cls): - return cls.many_to_one_relationship( - 'node', - 'source_node_fk', - backreference='outbound_relationships', - backref_kwargs=dict( - order_by=cls.source_position, - collection_class=ordering_list('source_position', count_from=0), - ) - ) - - @declared_attr - def source_node_name(cls): - return association_proxy('source_node', cls.name_column_name()) - - @declared_attr - def target_node_fk(cls): - return cls.foreign_key('node', nullable=True) - - @declared_attr - def target_node(cls): - return cls.many_to_one_relationship( - 'node', - 'target_node_fk', - backreference='inbound_relationships', - backref_kwargs=dict( - order_by=cls.target_position, - collection_class=ordering_list('target_position', count_from=0), - ) - ) - - @declared_attr - def target_node_name(cls): - return association_proxy('target_node', cls.name_column_name()) - # endregion - - # region many-to-many relationship - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('source_requirement_index', self.source_requirement_index), - ('target_node_id', self.target_node_id), - ('target_capability_name', self.target_capability_name), - ('type_name', self.type_name), - ('template_name', self.template_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('source_interfaces', formatting.as_raw_list(self.source_interfaces)), - ('target_interfaces', formatting.as_raw_list(self.target_interfaces)))) - - def validate(self, context): - if self.type_name: - if context.modeling.relationship_types.get_descendant(self.type_name) is None: - context.validation.report('relationship "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.source_interfaces) - utils.validate_dict_values(context, self.target_interfaces) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - utils.coerce_dict_values(context, container, self.source_interfaces, report_issues) - utils.coerce_dict_values(context, container, self.target_interfaces, report_issues) - - def dump(self, context): - if self.name: - if self.source_requirement_index is not None: - console.puts('%s (%d) ->' % ( - context.style.node(self.name), - self.source_requirement_index)) - else: - console.puts('%s ->' % context.style.node(self.name)) - else: - console.puts('->') - with context.style.indent: - console.puts('Node: %s' % context.style.node(self.target_node_id)) - if self.target_capability_name is not None: - console.puts('Capability: %s' % context.style.node(self.target_capability_name)) - if self.type_name is not None: - console.puts('Relationship type: %s' % context.style.type(self.type_name)) - if self.template_name is not None: - console.puts('Relationship template: %s' % context.style.node(self.template_name)) - utils.dump_parameters(context, self.properties) - utils.dump_interfaces(context, self.source_interfaces, 'Source interfaces') - utils.dump_interfaces(context, self.target_interfaces, 'Target interfaces') - -# endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/model.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/model.py b/aria/storage/modeling/model.py deleted file mode 100644 index cf7d933..0000000 --- a/aria/storage/modeling/model.py +++ /dev/null @@ -1,223 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from sqlalchemy.ext.declarative import declarative_base - -from . import ( - template_elements, - instance_elements, - orchestrator_elements, - elements, - structure, -) - -__all__ = ( - 'aria_declarative_base', - - 'Parameter', - - 'MappingTemplate', - 'InterfaceTemplate', - 'OperationTemplate', - 'ServiceTemplate', - 'NodeTemplate', - 'GroupTemplate', - 'ArtifactTemplate', - 'PolicyTemplate', - 'GroupPolicyTemplate', - 'GroupPolicyTriggerTemplate', - 'RequirementTemplate', - 'CapabilityTemplate', - - 'Mapping', - 'Substitution', - 'ServiceInstance', - 'Node', - 'Relationship', - 'Artifact', - 'Group', - 'Interface', - 'Operation', - 'Capability', - 'Policy', - 'GroupPolicy', - 'GroupPolicyTrigger', - - 'Execution', - 'ServiceInstanceUpdate', - 'ServiceInstanceUpdateStep', - 'ServiceInstanceModification', - 'Plugin', - 'Task' -) - -aria_declarative_base = declarative_base(cls=structure.ModelIDMixin) - -# pylint: disable=abstract-method - -# region elements - - -class Parameter(aria_declarative_base, elements.ParameterBase): - pass - -# endregion - -# region template models - - -class MappingTemplate(aria_declarative_base, template_elements.MappingTemplateBase): - pass - - -class SubstitutionTemplate(aria_declarative_base, template_elements.SubstitutionTemplateBase): - pass - - -class InterfaceTemplate(aria_declarative_base, template_elements.InterfaceTemplateBase): - pass - - -class OperationTemplate(aria_declarative_base, template_elements.OperationTemplateBase): - pass - - -class ServiceTemplate(aria_declarative_base, template_elements.ServiceTemplateBase): - pass - - -class NodeTemplate(aria_declarative_base, template_elements.NodeTemplateBase): - pass - - -class GroupTemplate(aria_declarative_base, template_elements.GroupTemplateBase): - pass - - -class ArtifactTemplate(aria_declarative_base, template_elements.ArtifactTemplateBase): - pass - - -class PolicyTemplate(aria_declarative_base, template_elements.PolicyTemplateBase): - pass - - -class GroupPolicyTemplate(aria_declarative_base, template_elements.GroupPolicyTemplateBase): - pass - - -class GroupPolicyTriggerTemplate(aria_declarative_base, - template_elements.GroupPolicyTriggerTemplateBase): - pass - - -class RequirementTemplate(aria_declarative_base, template_elements.RequirementTemplateBase): - pass - - -class CapabilityTemplate(aria_declarative_base, template_elements.CapabilityTemplateBase): - pass - - -# endregion - -# region instance models - -class Mapping(aria_declarative_base, instance_elements.MappingBase): - pass - - -class Substitution(aria_declarative_base, instance_elements.SubstitutionBase): - pass - - -class ServiceInstance(aria_declarative_base, instance_elements.ServiceInstanceBase): - pass - - -class Node(aria_declarative_base, instance_elements.NodeBase): - pass - - -class Relationship(aria_declarative_base, instance_elements.RelationshipBase): - pass - - -class Artifact(aria_declarative_base, instance_elements.ArtifactBase): - pass - - -class Group(aria_declarative_base, instance_elements.GroupBase): - pass - - -class Interface(aria_declarative_base, instance_elements.InterfaceBase): - pass - - -class Operation(aria_declarative_base, instance_elements.OperationBase): - pass - - -class Capability(aria_declarative_base, instance_elements.CapabilityBase): - pass - - -class Policy(aria_declarative_base, instance_elements.PolicyBase): - pass - - -class GroupPolicy(aria_declarative_base, instance_elements.GroupPolicyBase): - pass - - -class GroupPolicyTrigger(aria_declarative_base, instance_elements.GroupPolicyTriggerBase): - pass - - -# endregion - -# region orchestrator models - -class Execution(aria_declarative_base, orchestrator_elements.Execution): - pass - - -class ServiceInstanceUpdate(aria_declarative_base, - orchestrator_elements.ServiceInstanceUpdateBase): - pass - - -class ServiceInstanceUpdateStep(aria_declarative_base, - orchestrator_elements.ServiceInstanceUpdateStepBase): - pass - - -class ServiceInstanceModification(aria_declarative_base, - orchestrator_elements.ServiceInstanceModificationBase): - pass - - -class Plugin(aria_declarative_base, orchestrator_elements.PluginBase): - pass - - -class Task(aria_declarative_base, orchestrator_elements.TaskBase): - pass - - -class Log(aria_declarative_base, orchestrator_elements.LogBase): - pass -# endregion http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/orchestrator_elements.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/orchestrator_elements.py b/aria/storage/modeling/orchestrator_elements.py deleted file mode 100644 index ef773ed..0000000 --- a/aria/storage/modeling/orchestrator_elements.py +++ /dev/null @@ -1,497 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Aria's storage.models module -Path: aria.storage.models - -models module holds aria's models. - -classes: - * Field - represents a single field. - * IterField - represents an iterable field. - * Model - abstract model implementation. - * Snapshot - snapshots implementation model. - * Deployment - deployment implementation model. - * DeploymentUpdateStep - deployment update step implementation model. - * DeploymentUpdate - deployment update implementation model. - * DeploymentModification - deployment modification implementation model. - * Execution - execution implementation model. - * Node - node implementation model. - * Relationship - relationship implementation model. - * NodeInstance - node instance implementation model. - * RelationshipInstance - relationship instance implementation model. - * Plugin - plugin implementation model. -""" -from collections import namedtuple -from datetime import datetime - -from sqlalchemy import ( - Column, - Integer, - Text, - DateTime, - Boolean, - Enum, - String, - Float, - orm, -) -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.ext.declarative import declared_attr - -from aria.orchestrator.exceptions import TaskAbortException, TaskRetryException - -from .type import List, Dict -from .structure import ModelMixin - -__all__ = ( - 'ServiceInstanceUpdateStepBase', - 'ServiceInstanceUpdateBase', - 'ServiceInstanceModificationBase', - 'Execution', - 'PluginBase', - 'TaskBase' -) - -# pylint: disable=no-self-argument, no-member, abstract-method - - -class Execution(ModelMixin): - """ - Execution model representation. - """ - # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column. - __tablename__ = 'execution' - - __private_fields__ = ['service_instance_fk'] - - TERMINATED = 'terminated' - FAILED = 'failed' - CANCELLED = 'cancelled' - PENDING = 'pending' - STARTED = 'started' - CANCELLING = 'cancelling' - FORCE_CANCELLING = 'force_cancelling' - - STATES = [TERMINATED, FAILED, CANCELLED, PENDING, STARTED, CANCELLING, FORCE_CANCELLING] - END_STATES = [TERMINATED, FAILED, CANCELLED] - ACTIVE_STATES = [state for state in STATES if state not in END_STATES] - - VALID_TRANSITIONS = { - PENDING: [STARTED, CANCELLED], - STARTED: END_STATES + [CANCELLING], - CANCELLING: END_STATES + [FORCE_CANCELLING] - } - - @orm.validates('status') - def validate_status(self, key, value): - """Validation function that verifies execution status transitions are OK""" - try: - current_status = getattr(self, key) - except AttributeError: - return - valid_transitions = self.VALID_TRANSITIONS.get(current_status, []) - if all([current_status is not None, - current_status != value, - value not in valid_transitions]): - raise ValueError('Cannot change execution status from {current} to {new}'.format( - current=current_status, - new=value)) - return value - - created_at = Column(DateTime, index=True) - started_at = Column(DateTime, nullable=True, index=True) - ended_at = Column(DateTime, nullable=True, index=True) - error = Column(Text, nullable=True) - is_system_workflow = Column(Boolean, nullable=False, default=False) - parameters = Column(Dict) - status = Column(Enum(*STATES, name='execution_status'), default=PENDING) - workflow_name = Column(Text) - - @declared_attr - def service_template(cls): - return association_proxy('service_instance', 'service_template') - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - @declared_attr - def service_instance_name(cls): - return association_proxy('service_instance', cls.name_column_name()) - - @declared_attr - def service_template_name(cls): - return association_proxy('service_instance', 'service_template_name') - - def __str__(self): - return '<{0} id=`{1}` (status={2})>'.format( - self.__class__.__name__, - getattr(self, self.name_column_name()), - self.status - ) - - -class ServiceInstanceUpdateBase(ModelMixin): - """ - Deployment update model representation. - """ - # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column. - steps = None - - __tablename__ = 'service_instance_update' - __private_fields__ = ['service_instance_fk', - 'execution_fk'] - - _private_fields = ['execution_fk', 'deployment_fk'] - - created_at = Column(DateTime, nullable=False, index=True) - service_instance_plan = Column(Dict, nullable=False) - service_instance_update_node_instances = Column(Dict) - service_instance_update_service_instance = Column(Dict) - service_instance_update_nodes = Column(List) - modified_entity_ids = Column(Dict) - state = Column(Text) - - @declared_attr - def execution_fk(cls): - return cls.foreign_key('execution', nullable=True) - - @declared_attr - def execution(cls): - return cls.many_to_one_relationship('execution') - - @declared_attr - def execution_name(cls): - return association_proxy('execution', cls.name_column_name()) - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance') - - @declared_attr - def service_instance_name(cls): - return association_proxy('service_instance', cls.name_column_name()) - - def to_dict(self, suppress_error=False, **kwargs): - dep_update_dict = super(ServiceInstanceUpdateBase, self).to_dict(suppress_error) #pylint: disable=no-member - # Taking care of the fact the DeploymentSteps are _BaseModels - dep_update_dict['steps'] = [step.to_dict() for step in self.steps] - return dep_update_dict - - -class ServiceInstanceUpdateStepBase(ModelMixin): - """ - Deployment update step model representation. - """ - # Needed only for pylint. the id will be populated by sqlalcehmy and the proper column. - __tablename__ = 'service_instance_update_step' - __private_fields__ = ['service_instance_update_fk'] - - _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY') - ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify') - _entity_types = namedtuple( - 'ENTITY_TYPES', - 'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, ' - 'POLICY_TYPE, POLICY_TRIGGER, PLUGIN') - ENTITY_TYPES = _entity_types( - NODE='node', - RELATIONSHIP='relationship', - PROPERTY='property', - OPERATION='operation', - WORKFLOW='workflow', - OUTPUT='output', - DESCRIPTION='description', - GROUP='group', - POLICY_TYPE='policy_type', - POLICY_TRIGGER='policy_trigger', - PLUGIN='plugin' - ) - - action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False) - entity_id = Column(Text, nullable=False) - entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False) - - @declared_attr - def service_instance_update_fk(cls): - return cls.foreign_key('service_instance_update') - - @declared_attr - def service_instance_update(cls): - return cls.many_to_one_relationship('service_instance_update', - backreference='steps') - - @declared_attr - def deployment_update_name(cls): - return association_proxy('deployment_update', cls.name_column_name()) - - def __hash__(self): - return hash((getattr(self, self.id_column_name()), self.entity_id)) - - def __lt__(self, other): - """ - the order is 'remove' < 'modify' < 'add' - :param other: - :return: - """ - if not isinstance(other, self.__class__): - return not self >= other - - if self.action != other.action: - if self.action == 'remove': - return_value = True - elif self.action == 'add': - return_value = False - else: - return_value = other.action == 'add' - return return_value - - if self.action == 'add': - return self.entity_type == 'node' and other.entity_type == 'relationship' - if self.action == 'remove': - return self.entity_type == 'relationship' and other.entity_type == 'node' - return False - - -class ServiceInstanceModificationBase(ModelMixin): - """ - Deployment modification model representation. - """ - __tablename__ = 'service_instance_modification' - __private_fields__ = ['service_instance_fk'] - - STARTED = 'started' - FINISHED = 'finished' - ROLLEDBACK = 'rolledback' - - STATES = [STARTED, FINISHED, ROLLEDBACK] - END_STATES = [FINISHED, ROLLEDBACK] - - context = Column(Dict) - created_at = Column(DateTime, nullable=False, index=True) - ended_at = Column(DateTime, index=True) - modified_nodes = Column(Dict) - node_instances = Column(Dict) - status = Column(Enum(*STATES, name='deployment_modification_status')) - - @declared_attr - def service_instance_fk(cls): - return cls.foreign_key('service_instance') - - @declared_attr - def service_instance(cls): - return cls.many_to_one_relationship('service_instance', - backreference='modifications') - - @declared_attr - def service_instance_name(cls): - return association_proxy('service_instance', cls.name_column_name()) - - -class PluginBase(ModelMixin): - """ - Plugin model representation. - """ - __tablename__ = 'plugin' - - archive_name = Column(Text, nullable=False, index=True) - distribution = Column(Text) - distribution_release = Column(Text) - distribution_version = Column(Text) - package_name = Column(Text, nullable=False, index=True) - package_source = Column(Text) - package_version = Column(Text) - supported_platform = Column(Text) - supported_py_versions = Column(List) - uploaded_at = Column(DateTime, nullable=False, index=True) - wheels = Column(List, nullable=False) - - -class TaskBase(ModelMixin): - """ - A Model which represents an task - """ - __tablename__ = 'task' - __private_fields__ = ['node_fk', - 'relationship_fk', - 'execution_fk', - 'plugin_fk'] - - @declared_attr - def node_fk(cls): - return cls.foreign_key('node', nullable=True) - - @declared_attr - def node_name(cls): - return association_proxy('node', cls.name_column_name()) - - @declared_attr - def node(cls): - return cls.many_to_one_relationship('node') - - @declared_attr - def relationship_fk(cls): - return cls.foreign_key('relationship', nullable=True) - - @declared_attr - def relationship_name(cls): - return association_proxy('relationships', cls.name_column_name()) - - @declared_attr - def relationship(cls): - return cls.many_to_one_relationship('relationship') - - @declared_attr - def plugin_fk(cls): - return cls.foreign_key('plugin', nullable=True) - - @declared_attr - def plugin(cls): - return cls.many_to_one_relationship('plugin') - - @declared_attr - def execution_fk(cls): - return cls.foreign_key('execution', nullable=True) - - @declared_attr - def execution(cls): - return cls.many_to_one_relationship('execution') - - @declared_attr - def execution_name(cls): - return association_proxy('execution', cls.name_column_name()) - - PENDING = 'pending' - RETRYING = 'retrying' - SENT = 'sent' - STARTED = 'started' - SUCCESS = 'success' - FAILED = 'failed' - STATES = ( - PENDING, - RETRYING, - SENT, - STARTED, - SUCCESS, - FAILED, - ) - - WAIT_STATES = [PENDING, RETRYING] - END_STATES = [SUCCESS, FAILED] - - RUNS_ON_SOURCE = 'source' - RUNS_ON_TARGET = 'target' - RUNS_ON_NODE_INSTANCE = 'node_instance' - RUNS_ON = (RUNS_ON_NODE_INSTANCE, RUNS_ON_SOURCE, RUNS_ON_TARGET) - - @orm.validates('max_attempts') - def validate_max_attempts(self, _, value): # pylint: disable=no-self-use - """Validates that max attempts is either -1 or a positive number""" - if value < 1 and value != TaskBase.INFINITE_RETRIES: - raise ValueError('Max attempts can be either -1 (infinite) or any positive number. ' - 'Got {value}'.format(value=value)) - return value - - INFINITE_RETRIES = -1 - - status = Column(Enum(*STATES, name='status'), default=PENDING) - - due_at = Column(DateTime, default=datetime.utcnow) - started_at = Column(DateTime, default=None) - ended_at = Column(DateTime, default=None) - max_attempts = Column(Integer, default=1) - retry_count = Column(Integer, default=0) - retry_interval = Column(Float, default=0) - ignore_failure = Column(Boolean, default=False) - - # Operation specific fields - implementation = Column(String) - inputs = Column(Dict) - # This is unrelated to the plugin of the task. This field is related to the plugin name - # received from the blueprint. - plugin_name = Column(String) - _runs_on = Column(Enum(*RUNS_ON, name='runs_on'), name='runs_on') - - @property - def runs_on(self): - if self._runs_on == self.RUNS_ON_NODE_INSTANCE: - return self.node - elif self._runs_on == self.RUNS_ON_SOURCE: - return self.relationship.source_node # pylint: disable=no-member - elif self._runs_on == self.RUNS_ON_TARGET: - return self.relationship.target_node # pylint: disable=no-member - return None - - @property - def actor(self): - """ - Return the actor of the task - :return: - """ - return self.node or self.relationship - - @classmethod - def as_node_instance(cls, instance, runs_on, **kwargs): - return cls(node=instance, _runs_on=runs_on, **kwargs) - - @classmethod - def as_relationship_instance(cls, instance, runs_on, **kwargs): - return cls(relationship=instance, _runs_on=runs_on, **kwargs) - - @staticmethod - def abort(message=None): - raise TaskAbortException(message) - - @staticmethod - def retry(message=None, retry_interval=None): - raise TaskRetryException(message, retry_interval=retry_interval) - - -class LogBase(ModelMixin): - __tablename__ = 'log' - - @declared_attr - def execution_fk(cls): - return cls.foreign_key('execution') - - @declared_attr - def execution(cls): - return cls.many_to_one_relationship('execution') - - @declared_attr - def task_fk(cls): - return cls.foreign_key('task', nullable=True) - - @declared_attr - def task(cls): - return cls.many_to_one_relationship('task') - - level = Column(String) - msg = Column(String) - created_at = Column(DateTime, index=True) - actor = Column(String) - - def __repr__(self): - return "<{self.created_at}: [{self.level}] @{self.actor}> {msg}".format( - self=self, msg=self.msg[:50]) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/structure.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/structure.py b/aria/storage/modeling/structure.py deleted file mode 100644 index eacdb44..0000000 --- a/aria/storage/modeling/structure.py +++ /dev/null @@ -1,320 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -""" -Aria's storage.structures module -Path: aria.storage.structures - -models module holds aria's models. - -classes: - * Field - represents a single field. - * IterField - represents an iterable field. - * PointerField - represents a single pointer field. - * IterPointerField - represents an iterable pointers field. - * Model - abstract model implementation. -""" - -from sqlalchemy.orm import relationship, backref -from sqlalchemy.ext import associationproxy -from sqlalchemy import ( - Column, - ForeignKey, - Integer, - Text, - Table, -) - -from . import utils - - -class Function(object): - """ - An intrinsic function. - - Serves as a placeholder for a value that should eventually be derived - by calling the function. - """ - - @property - def as_raw(self): - raise NotImplementedError - - def _evaluate(self, context, container): - raise NotImplementedError - - def __deepcopy__(self, memo): - # Circumvent cloning in order to maintain our state - return self - - -class ElementBase(object): - """ - Base class for :class:`ServiceInstance` elements. - - All elements support validation, diagnostic dumping, and representation as - raw data (which can be translated into JSON or YAML) via :code:`as_raw`. - """ - - @property - def as_raw(self): - raise NotImplementedError - - def validate(self, context): - pass - - def coerce_values(self, context, container, report_issues): - pass - - def dump(self, context): - pass - - -class ModelElementBase(ElementBase): - """ - Base class for :class:`ServiceModel` elements. - - All model elements can be instantiated into :class:`ServiceInstance` elements. - """ - - def instantiate(self, context, container): - raise NotImplementedError - - -class ModelMixin(ModelElementBase): - - @utils.classproperty - def __modelname__(cls): # pylint: disable=no-self-argument - return getattr(cls, '__mapiname__', cls.__tablename__) - - @classmethod - def id_column_name(cls): - raise NotImplementedError - - @classmethod - def name_column_name(cls): - raise NotImplementedError - - @classmethod - def _get_cls_by_tablename(cls, tablename): - """Return class reference mapped to table. - - :param tablename: String with name of table. - :return: Class reference or None. - """ - if tablename in (cls.__name__, cls.__tablename__): - return cls - - for table_cls in cls._decl_class_registry.values(): - if tablename == getattr(table_cls, '__tablename__', None): - return table_cls - - @classmethod - def foreign_key(cls, table_name, nullable=False): - """Return a ForeignKey object with the relevant - - :param table: Unique id column in the parent table - :param nullable: Should the column be allowed to remain empty - """ - return Column(Integer, - ForeignKey('{tablename}.id'.format(tablename=table_name), ondelete='CASCADE'), - nullable=nullable) - - @classmethod - def one_to_one_relationship(cls, table_name, backreference=None): - return relationship(lambda: cls._get_cls_by_tablename(table_name), - backref=backref(backreference or cls.__tablename__, uselist=False)) - - @classmethod - def many_to_one_relationship(cls, - parent_table_name, - foreign_key_column=None, - backreference=None, - backref_kwargs=None, - **kwargs): - """Return a one-to-many SQL relationship object - Meant to be used from inside the *child* object - - :param parent_class: Class of the parent table - :param cls: Class of the child table - :param foreign_key_column: The column of the foreign key (from the child table) - :param backreference: The name to give to the reference to the child (on the parent table) - """ - relationship_kwargs = kwargs - if foreign_key_column: - relationship_kwargs.setdefault('foreign_keys', getattr(cls, foreign_key_column)) - - backref_kwargs = backref_kwargs or {} - backref_kwargs.setdefault('lazy', 'dynamic') - # The following line make sure that when the *parent* is - # deleted, all its connected children are deleted as well - backref_kwargs.setdefault('cascade', 'all') - - return relationship(lambda: cls._get_cls_by_tablename(parent_table_name), - backref=backref(backreference or utils.pluralize(cls.__tablename__), - **backref_kwargs or {}), - **relationship_kwargs) - - @classmethod - def relationship_to_self(cls, local_column): - - remote_side_str = '{cls.__name__}.{remote_column}'.format( - cls=cls, - remote_column=cls.id_column_name() - ) - primaryjoin_str = '{remote_side_str} == {cls.__name__}.{local_column}'.format( - remote_side_str=remote_side_str, - cls=cls, - local_column=local_column) - return relationship(cls._get_cls_by_tablename(cls.__tablename__).__name__, - primaryjoin=primaryjoin_str, - remote_side=remote_side_str, - post_update=True) - - @classmethod - def many_to_many_relationship(cls, other_table_name, table_prefix, relationship_kwargs=None): - """Return a many-to-many SQL relationship object - - Notes: - 1. The backreference name is the current table's table name - 2. This method creates a new helper table in the DB - - :param cls: The class of the table we're connecting from - :param other_table_name: The class of the table we're connecting to - :param table_prefix: Custom prefix for the helper table name and the - backreference name - """ - current_table_name = cls.__tablename__ - current_column_name = '{0}_id'.format(current_table_name) - current_foreign_key = '{0}.id'.format(current_table_name) - - other_column_name = '{0}_id'.format(other_table_name) - other_foreign_key = '{0}.id'.format(other_table_name) - - helper_table_name = '{0}_{1}'.format(current_table_name, other_table_name) - - backref_name = current_table_name - if table_prefix: - helper_table_name = '{0}_{1}'.format(table_prefix, helper_table_name) - backref_name = '{0}_{1}'.format(table_prefix, backref_name) - - secondary_table = cls.get_secondary_table( - cls.metadata, - helper_table_name, - current_column_name, - other_column_name, - current_foreign_key, - other_foreign_key - ) - - return relationship( - lambda: cls._get_cls_by_tablename(other_table_name), - secondary=secondary_table, - backref=backref(backref_name), - **(relationship_kwargs or {}) - ) - - @staticmethod - def get_secondary_table(metadata, - helper_table_name, - first_column_name, - second_column_name, - first_foreign_key, - second_foreign_key): - """Create a helper table for a many-to-many relationship - - :param helper_table_name: The name of the table - :param first_column_name: The name of the first column in the table - :param second_column_name: The name of the second column in the table - :param first_foreign_key: The string representing the first foreign key, - for example `blueprint.storage_id`, or `tenants.id` - :param second_foreign_key: The string representing the second foreign key - :return: A Table object - """ - return Table( - helper_table_name, - metadata, - Column( - first_column_name, - Integer, - ForeignKey(first_foreign_key) - ), - Column( - second_column_name, - Integer, - ForeignKey(second_foreign_key) - ) - ) - - def to_dict(self, fields=None, suppress_error=False): - """Return a dict representation of the model - - :param suppress_error: If set to True, sets `None` to attributes that - it's unable to retrieve (e.g., if a relationship wasn't established - yet, and so it's impossible to access a property through it) - """ - res = dict() - fields = fields or self.fields() - for field in fields: - try: - field_value = getattr(self, field) - except AttributeError: - if suppress_error: - field_value = None - else: - raise - if isinstance(field_value, list): - field_value = list(field_value) - elif isinstance(field_value, dict): - field_value = dict(field_value) - elif isinstance(field_value, ModelMixin): - field_value = field_value.to_dict() - res[field] = field_value - - return res - - @classmethod - def _association_proxies(cls): - for col, value in vars(cls).items(): - if isinstance(value, associationproxy.AssociationProxy): - yield col - - @classmethod - def fields(cls): - """Return the list of field names for this table - - Mostly for backwards compatibility in the code (that uses `fields`) - """ - fields = set(cls._association_proxies()) - fields.update(cls.__table__.columns.keys()) - return fields - set(getattr(cls, '__private_fields__', [])) - - def __repr__(self): - return '<{__class__.__name__} id=`{id}`>'.format( - __class__=self.__class__, - id=getattr(self, self.name_column_name())) - - -class ModelIDMixin(object): - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(Text, nullable=True, index=True) - - @classmethod - def id_column_name(cls): - return 'id' - - @classmethod - def name_column_name(cls): - return 'name'
