You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by mx...@apache.org on 2017/03/12 09:45:56 UTC
[6/8] incubator-ariatosca git commit: Separate plugin specification
form plugin; move dry support to CLI; various renames and refactorings
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service.py b/aria/modeling/service.py
deleted file mode 100644
index bf189f7..0000000
--- a/aria/modeling/service.py
+++ /dev/null
@@ -1,1529 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
-
-from sqlalchemy import (
- Column,
- Text,
- Integer
-)
-from sqlalchemy import DateTime
-from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import declared_attr
-
-from .bases import InstanceModelMixin
-from ..parser import validation
-from ..utils import collections, formatting, console
-
-from . import (
- utils,
- types as modeling_types
-)
-
-
-class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
- """
- A service is usually an instance of a :class:`ServiceTemplate`.
-
- You will usually not create it programmatically, but instead instantiate it from a service
- template.
-
- :ivar name: Name (unique for this ARIA installation)
- :vartype name: basestring
- :ivar service_template: Template from which this service was instantiated (optional)
- :vartype service_template: :class:`ServiceTemplate`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar meta_data: Custom annotations
- :vartype meta_data: {basestring: :class:`Metadata`}
- :ivar node: Nodes
- :vartype node: [:class:`Node`]
- :ivar groups: Groups of nodes
- :vartype groups: [:class:`Group`]
- :ivar policies: Policies
- :vartype policies: [:class:`Policy`]
- :ivar substitution: The entire service can appear as a node
- :vartype substitution: :class:`Substitution`
- :ivar inputs: Externally provided parameters
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar outputs: These parameters are filled in after service installation
- :vartype outputs: {basestring: :class:`Parameter`}
- :ivar operations: Custom operations that can be performed on the service
- :vartype operations: {basestring: :class:`Operation`}
- :ivar plugins: Plugins required to be installed
- :vartype plugins: {basestring: :class:`Plugin`}
- :ivar created_at: Creation timestamp
- :vartype created_at: :class:`datetime.datetime`
- :ivar updated_at: Update timestamp
- :vartype updated_at: :class:`datetime.datetime`
-
- :ivar permalink: ??
- :vartype permalink: basestring
- :ivar scaling_groups: ??
- :vartype scaling_groups: {}
-
- :ivar modifications: Modifications of this service
- :vartype modifications: [:class:`ServiceModification`]
- :ivar updates: Updates of this service
- :vartype updates: [:class:`ServiceUpdate`]
- :ivar executions: Executions on this service
- :vartype executions: [:class:`Execution`]
- """
-
- __tablename__ = 'service'
-
- @declared_attr
- def service_template(cls):
- return cls.many_to_one_relationship('service_template')
-
- description = Column(Text)
-
- @declared_attr
- def meta_data(cls):
- # Warning! We cannot use the attr name "metadata" because it's used by SqlAlchemy!
- return cls.many_to_many_relationship('metadata', dict_key='name')
-
- @declared_attr
- def nodes(cls):
- return cls.one_to_many_relationship('node')
-
- @declared_attr
- def groups(cls):
- return cls.one_to_many_relationship('group')
-
- @declared_attr
- def policies(cls):
- return cls.one_to_many_relationship('policy')
-
- @declared_attr
- def substitution(cls):
- return cls.one_to_one_relationship('substitution')
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- @declared_attr
- def outputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='outputs',
- dict_key='name')
-
- @declared_attr
- def operations(cls):
- return cls.one_to_many_relationship('operation', dict_key='name')
-
- @declared_attr
- def plugins(cls):
- return cls.many_to_many_relationship('plugin')
-
- created_at = Column(DateTime, nullable=False, index=True)
- updated_at = Column(DateTime)
-
- # region orchestration
-
- permalink = Column(Text)
- scaling_groups = Column(modeling_types.Dict)
-
- # endregion
-
- # region foreign keys
-
- __private_fields__ = ['substituion_fk',
- 'service_template_fk']
-
- # Service one-to-one to Substitution
- @declared_attr
- def substitution_fk(cls):
- return cls.foreign_key('substitution', nullable=True)
-
- # Service many-to-one to ServiceTemplate
- @declared_attr
- def service_template_fk(cls):
- return cls.foreign_key('service_template', nullable=True)
-
- # endregion
-
- def satisfy_requirements(self, context):
- satisfied = True
- for node in self.nodes:
- if not node.satisfy_requirements(context):
- satisfied = False
- return satisfied
-
- def validate_capabilities(self, context):
- satisfied = True
- for node in self.nodes:
- if not node.validate_capabilities(context):
- satisfied = False
- return satisfied
-
- def find_nodes(self, node_template_name):
- nodes = []
- for node in self.nodes:
- if node.node_template.name == node_template_name:
- nodes.append(node)
- return collections.FrozenList(nodes)
-
- def get_node_ids(self, node_template_name):
- return collections.FrozenList((node.name for node in self.find_nodes(node_template_name)))
-
- def find_groups(self, group_template_name):
- groups = []
- for group in self.groups:
- if group.template_name == group_template_name:
- groups.append(group)
- return collections.FrozenList(groups)
-
- def get_group_ids(self, group_template_name):
- return collections.FrozenList((group.name
- for group in self.find_groups(group_template_name)))
-
- def is_node_a_target(self, context, target_node):
- for node in self.nodes:
- if self._is_node_a_target(context, node, target_node):
- return True
- return False
-
- def _is_node_a_target(self, context, source_node, target_node):
- if source_node.relationships:
- for relationship in source_node.relationships:
- if relationship.target_node_id == target_node.name:
- return True
- else:
- node = context.modeling.instance.nodes.get(relationship.target_node_id)
- if node is not None:
- if self._is_node_a_target(context, node, target_node):
- return True
- return False
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('description', self.description),
- ('metadata', formatting.as_raw_dict(self.meta_data)),
- ('nodes', formatting.as_raw_list(self.nodes)),
- ('groups', formatting.as_raw_list(self.groups)),
- ('policies', formatting.as_raw_list(self.policies)),
- ('substitution', formatting.as_raw(self.substitution)),
- ('inputs', formatting.as_raw_dict(self.inputs)),
- ('outputs', formatting.as_raw_dict(self.outputs)),
- ('operations', formatting.as_raw_list(self.operations))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.meta_data)
- utils.validate_list_values(context, self.nodes)
- utils.validate_list_values(context, self.groups)
- utils.validate_list_values(context, self.policies)
- if self.substitution is not None:
- self.substitution.validate(context)
- utils.validate_dict_values(context, self.inputs)
- utils.validate_dict_values(context, self.outputs)
- utils.validate_dict_values(context, self.operations)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.meta_data, report_issues)
- utils.coerce_list_values(context, container, self.nodes, report_issues)
- utils.coerce_list_values(context, container, self.groups, report_issues)
- utils.coerce_list_values(context, container, self.policies, report_issues)
- if self.substitution is not None:
- self.substitution.coerce_values(context, container, report_issues)
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.outputs, report_issues)
- utils.coerce_dict_values(context, container, self.operations, report_issues)
-
- def dump(self, context):
- if self.description is not None:
- console.puts(context.style.meta(self.description))
- utils.dump_dict_values(context, self.meta_data, 'Metadata')
- for node in self.nodes:
- node.dump(context)
- for group in self.groups:
- group.dump(context)
- for policy in self.policies:
- policy.dump(context)
- if self.substitution is not None:
- self.substitution.dump(context)
- utils.dump_dict_values(context, self.inputs, 'Inputs')
- utils.dump_dict_values(context, self.outputs, 'Outputs')
- utils.dump_dict_values(context, self.operations, 'Operations')
-
- def dump_graph(self, context):
- for node in self.nodes.itervalues():
- if not self.is_node_a_target(context, node):
- self._dump_graph_node(context, node)
-
- def _dump_graph_node(self, context, node):
- console.puts(context.style.node(node.name))
- if node.relationships:
- with context.style.indent:
- for relationship in node.relationships:
- relationship_name = (context.style.node(relationship.template_name)
- if relationship.template_name is not None
- else context.style.type(relationship.type_name))
- capability_name = (context.style.node(relationship.target_capability_name)
- if relationship.target_capability_name is not None
- else None)
- if capability_name is not None:
- console.puts('-> {0} {1}'.format(relationship_name, capability_name))
- else:
- console.puts('-> {0}'.format(relationship_name))
- target_node = self.nodes.get(relationship.target_node_id)
- with console.indent(3):
- self._dump_graph_node(context, target_node)
-
-
-class NodeBase(InstanceModelMixin): # pylint: disable=too-many-public-methods
- """
- Usually an instance of a :class:`NodeTemplate`.
-
- Nodes may have zero or more :class:`Relationship` instances to other nodes.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar node_template: Template from which this node was instantiated (optional)
- :vartype node_template: :class:`NodeTemplate`
- :ivar type: Node type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interface`}
- :ivar artifacts: Associated files
- :vartype artifacts: {basestring: :class:`Artifact`}
- :ivar capabilities: Exposed capabilities
- :vartype capabilities: {basestring: :class:`Capability`}
- :ivar outbound_relationships: Relationships to other nodes
- :vartype outbound_relationships: [:class:`Relationship`]
- :ivar inbound_relationships: Relationships from other nodes
- :vartype inbound_relationships: [:class:`Relationship`]
- :ivar plugins: Plugins required to be installed on the node's host
- :vartype plugins: {basestring: :class:`Plugin`}
- :ivar host: Host node (can be self)
- :vartype host: :class:`Node`
-
- :ivar runtime_properties: TODO: should be replaced with attributes
- :vartype runtime_properties: {}
- :ivar scaling_groups: ??
- :vartype scaling_groups: []
- :ivar state: ??
- :vartype state: basestring
- :ivar version: ??
- :vartype version: int
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- :ivar groups: We are a member of these groups
- :vartype groups: [:class:`Group`]
- :ivar policies: Policies enacted on this node
- :vartype policies: [:class:`Policy`]
- :ivar substitution_mapping: Our contribution to service substitution
- :vartype substitution_mapping: :class:`SubstitutionMapping`
- :ivar tasks: Tasks on this node
- :vartype tasks: [:class:`Task`]
- """
-
- __tablename__ = 'node'
-
- @declared_attr
- def node_template(cls):
- return cls.many_to_one_relationship('node_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- @declared_attr
- def artifacts(cls):
- return cls.one_to_many_relationship('artifact', dict_key='name')
-
- @declared_attr
- def capabilities(cls):
- return cls.one_to_many_relationship('capability', dict_key='name')
-
- @declared_attr
- def outbound_relationships(cls):
- return cls.one_to_many_relationship('relationship',
- foreign_key='source_node_fk',
- backreference='source_node')
-
- @declared_attr
- def inbound_relationships(cls):
- return cls.one_to_many_relationship('relationship',
- foreign_key='target_node_fk',
- backreference='target_node')
-
- @declared_attr
- def plugins(cls):
- return cls.many_to_many_relationship('plugin')
-
- @declared_attr
- def host(cls):
- return cls.relationship_to_self('host_fk')
-
- # region orchestration
-
- runtime_properties = Column(modeling_types.Dict)
- scaling_groups = Column(modeling_types.List)
- state = Column(Text, nullable=False)
- version = Column(Integer, default=1)
-
- @declared_attr
- def service_name(cls):
- return association_proxy('service', 'name')
-
- @property
- def ip(self):
- # TODO: totally broken
- if not self.host_fk:
- return None
- host_node = self.host
- if 'ip' in host_node.runtime_properties: # pylint: disable=no-member
- return host_node.runtime_properties['ip'] # pylint: disable=no-member
- host_node = host_node.node_template # pylint: disable=no-member
- host_ip_property = host_node.properties.get('ip')
- if host_ip_property:
- return host_ip_property.value
- return None
-
- # endregion
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'host_fk',
- 'service_fk',
- 'node_template_fk']
-
- # Node many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-one to Node
- @declared_attr
- def host_fk(cls):
- return cls.foreign_key('node', nullable=True)
-
- # Service one-to-many to Node
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Node many-to-one to NodeTemplate
- @declared_attr
- def node_template_fk(cls):
- return cls.foreign_key('node_template', nullable=True)
-
- # endregion
-
- def satisfy_requirements(self, context):
- node_template = self.node_template
- satisfied = True
- for requirement_template in node_template.requirement_templates:
- # Find target template
- target_node_template, target_node_capability = \
- requirement_template.find_target(context, node_template)
- if target_node_template is not None:
- satisfied = self._satisfy_capability(context,
- target_node_capability,
- target_node_template,
- requirement_template)
- else:
- context.validation.report('requirement "{0}" of node "{1}" has no target node '
- 'template'.format(requirement_template.name, self.name),
- level=validation.Issue.BETWEEN_INSTANCES)
- satisfied = False
- return satisfied
-
- def _satisfy_capability(self, context, target_node_capability, target_node_template,
- requirement_template):
- from . import models
- # Find target nodes
- target_nodes = context.modeling.instance.find_nodes(target_node_template.name)
- if target_nodes:
- target_node = None
- target_capability = None
-
- if target_node_capability is not None:
- # Relate to the first target node that has capacity
- for node in target_nodes:
- target_capability = node.capabilities.get(target_node_capability.name)
- if target_capability.relate():
- target_node = node
- break
- else:
- # Use first target node
- target_node = target_nodes[0]
-
- if target_node is not None:
- if requirement_template.relationship_template is not None:
- relationship = \
- requirement_template.relationship_template.instantiate(context, self)
- else:
- relationship = models.Relationship(target_capability=target_capability)
- relationship.name = requirement_template.name
- relationship.requirement_template = requirement_template
- relationship.target_node = target_node
- self.outbound_relationships.append(relationship)
- return True
- else:
- context.validation.report('requirement "{0}" of node "{1}" targets node '
- 'template "{2}" but its instantiated nodes do not '
- 'have enough capacity'.format(
- requirement_template.name,
- self.name,
- target_node_template.name),
- level=validation.Issue.BETWEEN_INSTANCES)
- return False
- else:
- context.validation.report('requirement "{0}" of node "{1}" targets node template '
- '"{2}" but it has no instantiated nodes'.format(
- requirement_template.name,
- self.name,
- target_node_template.name),
- level=validation.Issue.BETWEEN_INSTANCES)
- return False
-
- def validate_capabilities(self, context):
- satisfied = False
- for capability in self.capabilities.itervalues():
- if not capability.has_enough_relationships:
- context.validation.report('capability "{0}" of node "{1}" requires at least {2:d} '
- 'relationships but has {3:d}'.format(
- capability.name,
- self.name,
- capability.min_occurrences,
- capability.occurrences),
- level=validation.Issue.BETWEEN_INSTANCES)
- satisfied = False
- return satisfied
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type_name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces)),
- ('artifacts', formatting.as_raw_list(self.artifacts)),
- ('capabilities', formatting.as_raw_list(self.capabilities)),
- ('relationships', formatting.as_raw_list(self.outbound_relationships))))
-
- def validate(self, context):
- if len(self.name) > context.modeling.id_max_length:
- context.validation.report('"{0}" has an ID longer than the limit of {1:d} characters: '
- '{2:d}'.format(
- self.name,
- context.modeling.id_max_length,
- len(self.name)),
- level=validation.Issue.BETWEEN_INSTANCES)
-
- # TODO: validate that node template is of type?
-
- utils.validate_dict_values(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
- utils.validate_dict_values(context, self.artifacts)
- utils.validate_dict_values(context, self.capabilities)
- utils.validate_list_values(context, self.outbound_relationships)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, self, self.properties, report_issues)
- utils.coerce_dict_values(context, self, self.interfaces, report_issues)
- utils.coerce_dict_values(context, self, self.artifacts, report_issues)
- utils.coerce_dict_values(context, self, self.capabilities, report_issues)
- utils.coerce_list_values(context, self, self.outbound_relationships, report_issues)
-
- def dump(self, context):
- console.puts('Node: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type: {0}'.format(context.style.type(self.type.name)))
- console.puts('Template: {0}'.format(context.style.node(self.node_template.name)))
- utils.dump_dict_values(context, self.properties, 'Properties')
- utils.dump_interfaces(context, self.interfaces)
- utils.dump_dict_values(context, self.artifacts, 'Artifacts')
- utils.dump_dict_values(context, self.capabilities, 'Capabilities')
- utils.dump_list_values(context, self.outbound_relationships, 'Relationships')
-
-
-class GroupBase(InstanceModelMixin):
- """
- Usually an instance of a :class:`GroupTemplate`.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar group_template: Template from which this group was instantiated (optional)
- :vartype group_template: :class:`GroupTemplate`
- :ivar type: Group type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar nodes: Members of this group
- :vartype nodes: [:class:`Node`]
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interface`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- :ivar policies: Policies enacted on this group
- :vartype policies: [:class:`Policy`]
- """
-
- __tablename__ = 'group'
-
- @declared_attr
- def group_template(cls):
- return cls.many_to_one_relationship('group_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def nodes(cls):
- return cls.many_to_many_relationship('node')
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'service_fk',
- 'group_template_fk']
-
- # Group many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Service one-to-many to Group
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Group many-to-one to GroupTemplate
- @declared_attr
- def group_template_fk(cls):
- return cls.foreign_key('group_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
- def dump(self, context):
- console.puts('Group: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type: {0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(context, self.properties, 'Properties')
- utils.dump_interfaces(context, self.interfaces)
- if self.nodes:
- console.puts('Member nodes:')
- with context.style.indent:
- for node in self.nodes:
- console.puts(context.style.node(node.name))
-
-
-class PolicyBase(InstanceModelMixin):
- """
- Usually an instance of a :class:`PolicyTemplate`.
-
- :ivar name: Name (unique for this service)
- :vartype name: basestring
- :ivar policy_template: Template from which this policy was instantiated (optional)
- :vartype policy_template: :class:`PolicyTemplate`
- :ivar type: Policy type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar nodes: Policy will be enacted on all these nodes
- :vartype nodes: [:class:`Node`]
- :ivar groups: Policy will be enacted on all nodes in these groups
- :vartype groups: [:class:`Group`]
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'policy'
-
- @declared_attr
- def policy_template(cls):
- return cls.many_to_one_relationship('policy_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def nodes(cls):
- return cls.many_to_many_relationship('node')
-
- @declared_attr
- def groups(cls):
- return cls.many_to_many_relationship('group')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'service_fk',
- 'policy_template_fk']
-
- # Policy many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Service one-to-many to Policy
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service')
-
- # Policy many-to-one to PolicyTemplate
- @declared_attr
- def policy_template_fk(cls):
- return cls.foreign_key('policy_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type_name),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- console.puts('Policy: {0}'.format(context.style.node(self.name)))
- with context.style.indent:
- console.puts('Type: {0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(context, self.properties, 'Properties')
- if self.nodes:
- console.puts('Target nodes:')
- with context.style.indent:
- for node in self.nodes:
- console.puts(context.style.node(node.name))
- if self.groups:
- console.puts('Target groups:')
- with context.style.indent:
- for group in self.groups:
- console.puts(context.style.node(group.name))
-
-
-class SubstitutionBase(InstanceModelMixin):
- """
- Used to substitute a single node for the entire deployment.
-
- Usually an instance of a :class:`SubstitutionTemplate`.
-
- :ivar substitution_template: Template from which this substitution was instantiated (optional)
- :vartype substitution_template: :class:`SubstitutionTemplate`
- :ivar node_type: Exposed node type
- :vartype node_type: :class:`Type`
- :ivar mappings: Requirement and capability mappings
- :vartype mappings: {basestring: :class:`SubstitutionTemplate`}
-
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'substitution'
-
- @declared_attr
- def substitution_template(cls):
- return cls.many_to_one_relationship('substitution_template')
-
- @declared_attr
- def node_type(cls):
- return cls.many_to_one_relationship('type')
-
- @declared_attr
- def mappings(cls):
- return cls.one_to_many_relationship('substitution_mapping', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['node_type_fk',
- 'substitution_template_fk']
-
- # Substitution many-to-one to Type
- @declared_attr
- def node_type_fk(cls):
- return cls.foreign_key('type')
-
- # Substitution many-to-one to SubstitutionTemplate
- @declared_attr
- def substitution_template_fk(cls):
- return cls.foreign_key('substitution_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('node_type_name', self.node_type_name),
- ('mappings', formatting.as_raw_dict(self.mappings))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.mappings)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.mappings, report_issues)
-
- def dump(self, context):
- console.puts('Substitution:')
- with context.style.indent:
- console.puts('Node type: {0}'.format(context.style.type(self.node_type.name)))
- utils.dump_dict_values(context, self.mappings, 'Mappings')
-
-
-class SubstitutionMappingBase(InstanceModelMixin):
- """
- Used by :class:`Substitution` to map a capability or a requirement to a node.
-
- Only one of `capability_template` and `requirement_template` can be set.
-
- Usually an instance of a :class:`SubstitutionTemplate`.
-
- :ivar name: Exposed capability or requirement name
- :vartype name: basestring
- :ivar node: Node
- :vartype node: :class:`Node`
- :ivar capability: Capability in the node
- :vartype capability: :class:`Capability`
- :ivar requirement_template: Requirement template in the node template
- :vartype requirement_template: :class:`RequirementTemplate`
-
- :ivar substitution: Containing substitution
- :vartype substitution: :class:`Substitution`
- """
-
- __tablename__ = 'substitution_mapping'
-
- @declared_attr
- def node(cls):
- return cls.one_to_one_relationship('node')
-
- @declared_attr
- def capability(cls):
- return cls.one_to_one_relationship('capability')
-
- @declared_attr
- def requirement_template(cls):
- return cls.one_to_one_relationship('requirement_template')
-
- # region foreign keys
-
- __private_fields__ = ['substitution_fk',
- 'node_fk',
- 'capability_fk',
- 'requirement_template_fk']
-
- # Substitution one-to-many to SubstitutionMapping
- @declared_attr
- def substitution_fk(cls):
- return cls.foreign_key('substitution')
-
- # Substitution one-to-one to NodeTemplate
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Substitution one-to-one to Capability
- @declared_attr
- def capability_fk(cls):
- return cls.foreign_key('capability', nullable=True)
-
- # Substitution one-to-one to RequirementTemplate
- @declared_attr
- def requirement_template_fk(cls):
- return cls.foreign_key('requirement_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name)))
-
- def validate(self, context):
- if (self.capability is None) and (self.requirement_template is None):
- context.validation.report('mapping "{0}" refers to neither capability nor a requirement'
- ' in node: {1}'.format(
- self.name,
- formatting.safe_repr(self.node.name)),
- level=validation.Issue.BETWEEN_TYPES)
-
- def dump(self, context):
- console.puts('{0} -> {1}.{2}'.format(
- context.style.node(self.name),
- context.style.node(self.node.name),
- context.style.node(self.capability.name
- if self.capability
- else self.requirement_template.name)))
-
-
-class RelationshipBase(InstanceModelMixin):
- """
- Connects :class:`Node` to a capability in another node.
-
- Might be an instance of a :class:`RelationshipTemplate`.
-
- :ivar name: Name (usually the name of the requirement at the source node template)
- :vartype name: basestring
- :ivar relationship_template: Template from which this relationship was instantiated (optional)
- :vartype relationship_template: :class:`RelationshipTemplate`
- :ivar requirement_template: Template from which this relationship was instantiated (optional)
- :vartype requirement_template: :class:`RequirementTemplate`
- :ivar type: Relationship type
- :vartype type: :class:`Type`
- :ivar target_capability: Capability at the target node (optional)
- :vartype target_capability: :class:`Capability`
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
- :ivar interfaces: Bundles of operations
- :vartype interfaces: {basestring: :class:`Interfaces`}
-
- :ivar source_position: ??
- :vartype source_position: int
- :ivar target_position: ??
- :vartype target_position: int
-
- :ivar source_node: Source node
- :vartype source_node: :class:`Node`
- :ivar target_node: Target node
- :vartype target_node: :class:`Node`
- :ivar tasks: Tasks on this node
- :vartype tasks: [:class:`Task`]
- """
-
- __tablename__ = 'relationship'
-
- @declared_attr
- def relationship_template(cls):
- return cls.many_to_one_relationship('relationship_template')
-
- @declared_attr
- def requirement_template(cls):
- return cls.many_to_one_relationship('requirement_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- @declared_attr
- def target_capability(cls):
- return cls.one_to_one_relationship('capability')
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- @declared_attr
- def interfaces(cls):
- return cls.one_to_many_relationship('interface', dict_key='name')
-
- # region orchestration
-
- source_position = Column(Integer) # ???
- target_position = Column(Integer) # ???
-
- # endregion
-
- # region foreign keys
-
- __private_fields__ = ['type_fk',
- 'source_node_fk',
- 'target_node_fk',
- 'target_capability_fk',
- 'requirement_template_fk',
- 'relationship_template_fk']
-
- # Relationship many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type', nullable=True)
-
- # Node one-to-many to Relationship
- @declared_attr
- def source_node_fk(cls):
- return cls.foreign_key('node')
-
- # Node one-to-many to Relationship
- @declared_attr
- def target_node_fk(cls):
- return cls.foreign_key('node')
-
- # Relationship one-to-one to Capability
- @declared_attr
- def target_capability_fk(cls):
- return cls.foreign_key('capability', nullable=True)
-
- # Relationship many-to-one to RequirementTemplate
- @declared_attr
- def requirement_template_fk(cls):
- return cls.foreign_key('requirement_template', nullable=True)
-
- # Relationship many-to-one to RelationshipTemplate
- @declared_attr
- def relationship_template_fk(cls):
- return cls.foreign_key('relationship_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('target_node_id', self.target_node.name),
- ('type_name', self.type_name),
- ('template_name', self.template_name),
- ('properties', formatting.as_raw_dict(self.properties)),
- ('interfaces', formatting.as_raw_list(self.interfaces))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
- utils.validate_dict_values(context, self.interfaces)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
- utils.coerce_dict_values(context, container, self.interfaces, report_issues)
-
- def dump(self, context):
- if self.name:
- console.puts('{0} ->'.format(context.style.node(self.name)))
- else:
- console.puts('->')
- with context.style.indent:
- console.puts('Node: {0}'.format(context.style.node(self.target_node.name)))
- if self.target_capability:
- console.puts('Capability: {0}'.format(context.style.node(
- self.target_capability.name)))
- if self.type is not None:
- console.puts('Relationship type: {0}'.format(context.style.type(self.type.name)))
- if (self.relationship_template is not None) and self.relationship_template.name:
- console.puts('Relationship template: {0}'.format(
- context.style.node(self.relationship_template.name)))
- utils.dump_dict_values(context, self.properties, 'Properties')
- utils.dump_interfaces(context, self.interfaces, 'Interfaces')
-
-
-class CapabilityBase(InstanceModelMixin):
- """
- A capability of a :class:`Node`.
-
- Usually an instance of a :class:`CapabilityTemplate`.
-
- :ivar name: Name (unique for the node)
- :vartype name: basestring
- :ivar capability_template: Template from which this capability was instantiated (optional)
- :vartype capability_template: :class:`capabilityTemplate`
- :ivar type: Capability type
- :vartype type: :class:`Type`
- :ivar min_occurrences: Minimum number of requirement matches required
- :vartype min_occurrences: int
- :ivar max_occurrences: Maximum number of requirement matches allowed
- :vartype min_occurrences: int
- :ivar occurrences: Actual number of requirement matches
- :vartype occurrences: int
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- :ivar relationship: Available when we are the target of a relationship
- :vartype relationship: :class:`Relationship`
- :ivar substitution_mapping: Our contribution to service substitution
- :vartype substitution_mapping: :class:`SubstitutionMapping`
- """
-
- __tablename__ = 'capability'
-
- @declared_attr
- def capability_template(cls):
- return cls.many_to_one_relationship('capability_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- min_occurrences = Column(Integer, default=None)
- max_occurrences = Column(Integer, default=None)
- occurrences = Column(Integer, default=0)
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['capability_fk',
- 'node_fk',
- 'capability_template_fk']
-
- # Capability many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Capability
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Capability many-to-one to CapabilityTemplate
- @declared_attr
- def capability_template_fk(cls):
- return cls.foreign_key('capability_template', nullable=True)
-
- # endregion
-
- @property
- def has_enough_relationships(self):
- if self.min_occurrences is not None:
- return self.occurrences >= self.min_occurrences
- return True
-
- def relate(self):
- if self.max_occurrences is not None:
- if self.occurrences == self.max_occurrences:
- return False
- self.occurrences += 1
- return True
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('type_name', self.type_name),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- console.puts(context.style.node(self.name))
- with context.style.indent:
- console.puts('Type: {0}'.format(context.style.type(self.type.name)))
- console.puts('Occurrences: {0:d} ({1:d}{2})'.format(
- self.occurrences,
- self.min_occurrences or 0,
- ' to {0:d}'.format(self.max_occurrences)
- if self.max_occurrences is not None
- else ' or more'))
- utils.dump_dict_values(context, self.properties, 'Properties')
-
-
-class InterfaceBase(InstanceModelMixin):
- """
- A typed set of :class:`Operation`.
-
- Usually an instance of :class:`InterfaceTemplate`.
-
- :ivar name: Name (unique for the node, group, or relationship)
- :vartype name: basestring
- :ivar interface_template: Template from which this interface was instantiated (optional)
- :vartype interface_template: :class:`InterfaceTemplate`
- :ivar type: Interface type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar inputs: Parameters that can be used by all operations in the interface
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar operations: Operations
- :vartype operations: {basestring: :class:`Operation`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- :ivar group: Containing group
- :vartype group: :class:`Group`
- :ivar relationship: Containing relationship
- :vartype relationship: :class:`Relationship`
- """
-
- __tablename__ = 'interface'
-
- @declared_attr
- def interface_template(cls):
- return cls.many_to_one_relationship('interface_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- @declared_attr
- def operations(cls):
- return cls.one_to_many_relationship('operation', dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'node_fk',
- 'group_fk',
- 'relationship_fk',
- 'interface_template_fk']
-
- # Interface many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Interface
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node', nullable=True)
-
- # Group one-to-many to Interface
- @declared_attr
- def group_fk(cls):
- return cls.foreign_key('group', nullable=True)
-
- # Relationship one-to-many to Interface
- @declared_attr
- def relationship_fk(cls):
- return cls.foreign_key('relationship', nullable=True)
-
- # Interface many-to-one to InterfaceTemplate
- @declared_attr
- def interface_template_fk(cls):
- return cls.foreign_key('interface_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('type_name', self.type_name),
- ('inputs', formatting.as_raw_dict(self.inputs)),
- ('operations', formatting.as_raw_list(self.operations))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.inputs)
- utils.validate_dict_values(context, self.operations)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
- utils.coerce_dict_values(context, container, self.operations, report_issues)
-
- def dump(self, context):
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- console.puts('Interface type: {0}'.format(context.style.type(self.type.name)))
- utils.dump_dict_values(context, self.inputs, 'Inputs')
- utils.dump_dict_values(context, self.operations, 'Operations')
-
-
-class OperationBase(InstanceModelMixin):
- """
- An operation in a :class:`Interface`.
-
- Might be an instance of :class:`OperationTemplate`.
-
- :ivar name: Name (unique for the interface or service)
- :vartype name: basestring
- :ivar operation_template: Template from which this operation was instantiated (optional)
- :vartype operation_template: :class:`OperationTemplate`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar plugin: Associated plugin
- :vartype plugin: :class:`Plugin`
- :ivar implementation: Implementation string (interpreted by the plugin)
- :vartype implementation: basestring
- :ivar dependencies: Dependency strings (interpreted by the plugin)
- :vartype dependencies: [basestring]
- :ivar inputs: Parameters that can be used by this operation
- :vartype inputs: {basestring: :class:`Parameter`}
- :ivar executor: Executor name
- :vartype executor: basestring
- :ivar max_retries: Maximum number of retries allowed in case of failure
- :vartype max_retries: int
- :ivar retry_interval: Interval between retries (in seconds)
- :vartype retry_interval: int
-
- :ivar interface: Containing interface
- :vartype interface: :class:`Interface`
- :ivar service: Containing service
- :vartype service: :class:`Service`
- """
-
- __tablename__ = 'operation'
-
- @declared_attr
- def operation_template(cls):
- return cls.many_to_one_relationship('operation_template')
-
- description = Column(Text)
-
- @declared_attr
- def plugin(cls):
- return cls.one_to_one_relationship('plugin')
-
- implementation = Column(Text)
- dependencies = Column(modeling_types.StrictList(item_cls=basestring))
-
- @declared_attr
- def inputs(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='inputs',
- dict_key='name')
-
- executor = Column(Text)
- max_retries = Column(Integer)
- retry_interval = Column(Integer)
-
- # region foreign_keys
-
- __private_fields__ = ['service_fk',
- 'interface_fk',
- 'plugin_fk',
- 'operation_template_fk']
-
- # Service one-to-many to Operation
- @declared_attr
- def service_fk(cls):
- return cls.foreign_key('service', nullable=True)
-
- # Interface one-to-many to Operation
- @declared_attr
- def interface_fk(cls):
- return cls.foreign_key('interface', nullable=True)
-
- # Operation one-to-one to Plugin
- @declared_attr
- def plugin_fk(cls):
- return cls.foreign_key('plugin', nullable=True)
-
- # Operation many-to-one to OperationTemplate
- @declared_attr
- def operation_template_fk(cls):
- return cls.foreign_key('operation_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('implementation', self.implementation),
- ('dependencies', self.dependencies),
- ('executor', self.executor),
- ('max_retries', self.max_retries),
- ('retry_interval', self.retry_interval),
- ('inputs', formatting.as_raw_dict(self.inputs))))
-
- def validate(self, context):
- # TODO must be associated with interface or service
- utils.validate_dict_values(context, self.inputs)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.inputs, report_issues)
-
- def dump(self, context):
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- if self.implementation is not None:
- console.puts('Implementation: {0}'.format(
- context.style.literal(self.implementation)))
- if self.dependencies:
- console.puts(
- 'Dependencies: {0}'.format(
- ', '.join((str(context.style.literal(v)) for v in self.dependencies))))
- if self.executor is not None:
- console.puts('Executor: {0}'.format(context.style.literal(self.executor)))
- if self.max_retries is not None:
- console.puts('Max retries: {0}'.format(context.style.literal(self.max_retries)))
- if self.retry_interval is not None:
- console.puts('Retry interval: {0}'.format(
- context.style.literal(self.retry_interval)))
- utils.dump_dict_values(context, self.inputs, 'Inputs')
-
-
-class ArtifactBase(InstanceModelMixin):
- """
- A file associated with a :class:`Node`.
-
- Usually an instance of :class:`ArtifactTemplate`.
-
- :ivar name: Name (unique for the node)
- :vartype name: basestring
- :ivar artifact_template: Template from which this artifact was instantiated (optional)
- :vartype artifact_template: :class:`ArtifactTemplate`
- :ivar type: Artifact type
- :vartype type: :class:`Type`
- :ivar description: Human-readable description
- :vartype description: string
- :ivar source_path: Source path (CSAR or repository)
- :vartype source_path: basestring
- :ivar target_path: Path at destination machine
- :vartype target_path: basestring
- :ivar repository_url: Repository URL
- :vartype repository_path: basestring
- :ivar repository_credential: Credentials for accessing the repository
- :vartype repository_credential: {basestring: basestring}
- :ivar properties: Associated parameters
- :vartype properties: {basestring: :class:`Parameter`}
-
- :ivar node: Containing node
- :vartype node: :class:`Node`
- """
-
- __tablename__ = 'artifact'
-
- @declared_attr
- def artifact_template(cls):
- return cls.many_to_one_relationship('artifact_template')
-
- @declared_attr
- def type(cls):
- return cls.many_to_one_relationship('type')
-
- description = Column(Text)
- source_path = Column(Text)
- target_path = Column(Text)
- repository_url = Column(Text)
- repository_credential = Column(modeling_types.StrictDict(basestring, basestring))
-
- @declared_attr
- def properties(cls):
- return cls.many_to_many_relationship('parameter', table_prefix='properties',
- dict_key='name')
-
- # region foreign_keys
-
- __private_fields__ = ['type_fk',
- 'node_fk',
- 'artifact_template_fk']
-
- # Artifact many-to-one to Type
- @declared_attr
- def type_fk(cls):
- return cls.foreign_key('type')
-
- # Node one-to-many to Artifact
- @declared_attr
- def node_fk(cls):
- return cls.foreign_key('node')
-
- # Artifact many-to-one to ArtifactTemplate
- @declared_attr
- def artifact_template_fk(cls):
- return cls.foreign_key('artifact_template', nullable=True)
-
- # endregion
-
- @property
- def as_raw(self):
- return collections.OrderedDict((
- ('name', self.name),
- ('description', self.description),
- ('type_name', self.type_name),
- ('source_path', self.source_path),
- ('target_path', self.target_path),
- ('repository_url', self.repository_url),
- ('repository_credential', formatting.as_agnostic(self.repository_credential)),
- ('properties', formatting.as_raw_dict(self.properties))))
-
- def validate(self, context):
- utils.validate_dict_values(context, self.properties)
-
- def coerce_values(self, context, container, report_issues):
- utils.coerce_dict_values(context, container, self.properties, report_issues)
-
- def dump(self, context):
- console.puts(context.style.node(self.name))
- if self.description:
- console.puts(context.style.meta(self.description))
- with context.style.indent:
- console.puts('Artifact type: {0}'.format(context.style.type(self.type.name)))
- console.puts('Source path: {0}'.format(context.style.literal(self.source_path)))
- if self.target_path is not None:
- console.puts('Target path: {0}'.format(context.style.literal(self.target_path)))
- if self.repository_url is not None:
- console.puts('Repository URL: {0}'.format(
- context.style.literal(self.repository_url)))
- if self.repository_credential:
- console.puts('Repository credential: {0}'.format(
- context.style.literal(self.repository_credential)))
- utils.dump_dict_values(context, self.properties, 'Properties')
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_changes.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_changes.py b/aria/modeling/service_changes.py
new file mode 100644
index 0000000..b83a376
--- /dev/null
+++ b/aria/modeling/service_changes.py
@@ -0,0 +1,219 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+classes:
+ * ServiceUpdate - service update implementation model.
+ * ServiceUpdateStep - service update step implementation model.
+ * ServiceModification - service modification implementation model.
+"""
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+from collections import namedtuple
+
+from sqlalchemy import (
+ Column,
+ Text,
+ DateTime,
+ Enum,
+)
+from sqlalchemy.ext.associationproxy import association_proxy
+from sqlalchemy.ext.declarative import declared_attr
+
+from .types import (List, Dict)
+from .mixins import ModelMixin
+
+__all__ = (
+ 'ServiceUpdateBase',
+ 'ServiceUpdateStepBase',
+ 'ServiceModificationBase'
+)
+
+
+class ServiceUpdateBase(ModelMixin):
+ """
+ Deployment update model representation.
+ """
+
+ steps = None
+
+ __tablename__ = 'service_update'
+
+ _private_fields = ['execution_fk',
+ 'service_fk']
+
+ created_at = Column(DateTime, nullable=False, index=True)
+ service_plan = Column(Dict, nullable=False)
+ service_update_nodes = Column(Dict)
+ service_update_service = Column(Dict)
+ service_update_node_templates = Column(List)
+ modified_entity_ids = Column(Dict)
+ state = Column(Text)
+
+ @declared_attr
+ def execution(cls):
+ return cls._create_many_to_one_relationship('execution')
+
+ @declared_attr
+ def execution_name(cls):
+ return association_proxy('execution', cls.name_column_name())
+
+ @declared_attr
+ def service(cls):
+ return cls._create_many_to_one_relationship('service',
+ backreference='updates')
+
+ @declared_attr
+ def service_name(cls):
+ return association_proxy('service', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_fk',
+ 'execution_fk']
+
+ @declared_attr
+ def execution_fk(cls):
+ return cls._create_foreign_key('execution', nullable=True)
+
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # endregion
+
+ def to_dict(self, suppress_error=False, **kwargs):
+ dep_update_dict = super(ServiceUpdateBase, self).to_dict(suppress_error) #pylint: disable=no-member
+ # Taking care of the fact the DeploymentSteps are _BaseModels
+ dep_update_dict['steps'] = [step.to_dict() for step in self.steps]
+ return dep_update_dict
+
+
+class ServiceUpdateStepBase(ModelMixin):
+ """
+ Deployment update step model representation.
+ """
+
+ __tablename__ = 'service_update_step'
+
+ _action_types = namedtuple('ACTION_TYPES', 'ADD, REMOVE, MODIFY')
+ ACTION_TYPES = _action_types(ADD='add', REMOVE='remove', MODIFY='modify')
+
+ _entity_types = namedtuple(
+ 'ENTITY_TYPES',
+ 'NODE, RELATIONSHIP, PROPERTY, OPERATION, WORKFLOW, OUTPUT, DESCRIPTION, GROUP, PLUGIN')
+ ENTITY_TYPES = _entity_types(
+ NODE='node',
+ RELATIONSHIP='relationship',
+ PROPERTY='property',
+ OPERATION='operation',
+ WORKFLOW='workflow',
+ OUTPUT='output',
+ DESCRIPTION='description',
+ GROUP='group',
+ PLUGIN='plugin'
+ )
+
+ action = Column(Enum(*ACTION_TYPES, name='action_type'), nullable=False)
+ entity_id = Column(Text, nullable=False)
+ entity_type = Column(Enum(*ENTITY_TYPES, name='entity_type'), nullable=False)
+
+ @declared_attr
+ def service_update(cls):
+ return cls._create_many_to_one_relationship('service_update',
+ backreference='steps')
+
+ @declared_attr
+ def service_update_name(cls):
+ return association_proxy('service_update', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_update_fk']
+
+ @declared_attr
+ def service_update_fk(cls):
+ return cls._create_foreign_key('service_update')
+
+ # endregion
+
+ def __hash__(self):
+ return hash((getattr(self, self.id_column_name()), self.entity_id))
+
+ def __lt__(self, other):
+ """
+ the order is 'remove' < 'modify' < 'add'
+ :param other:
+ :return:
+ """
+ if not isinstance(other, self.__class__):
+ return not self >= other
+
+ if self.action != other.action:
+ if self.action == 'remove':
+ return_value = True
+ elif self.action == 'add':
+ return_value = False
+ else:
+ return_value = other.action == 'add'
+ return return_value
+
+ if self.action == 'add':
+ return self.entity_type == 'node' and other.entity_type == 'relationship'
+ if self.action == 'remove':
+ return self.entity_type == 'relationship' and other.entity_type == 'node'
+ return False
+
+
+class ServiceModificationBase(ModelMixin):
+ """
+ Deployment modification model representation.
+ """
+
+ __tablename__ = 'service_modification'
+
+ STARTED = 'started'
+ FINISHED = 'finished'
+ ROLLEDBACK = 'rolledback'
+
+ STATES = [STARTED, FINISHED, ROLLEDBACK]
+ END_STATES = [FINISHED, ROLLEDBACK]
+
+ context = Column(Dict)
+ created_at = Column(DateTime, nullable=False, index=True)
+ ended_at = Column(DateTime, index=True)
+ modified_node_templates = Column(Dict)
+ nodes = Column(Dict)
+ status = Column(Enum(*STATES, name='service_modification_status'))
+
+ @declared_attr
+ def service(cls):
+ return cls._create_many_to_one_relationship('service',
+ backreference='modifications')
+
+ @declared_attr
+ def service_name(cls):
+ return association_proxy('service', cls.name_column_name())
+
+ # region foreign keys
+
+ __private_fields__ = ['service_fk']
+
+ @declared_attr
+ def service_fk(cls):
+ return cls._create_foreign_key('service')
+
+ # endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_common.py
----------------------------------------------------------------------
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
new file mode 100644
index 0000000..b3535a6
--- /dev/null
+++ b/aria/modeling/service_common.py
@@ -0,0 +1,270 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# pylint: disable=no-self-argument, no-member, abstract-method
+
+import cPickle as pickle
+import logging
+
+from sqlalchemy import (
+ Column,
+ Text,
+ Binary,
+)
+from sqlalchemy.ext.declarative import declared_attr
+
+from ..storage import exceptions
+from ..utils import collections, formatting, console
+from .mixins import InstanceModelMixin, TemplateModelMixin
+from .types import List
+from . import utils
+
+
+class ParameterBase(TemplateModelMixin):
+ """
+ Represents a typed value.
+
+ This model is used by both service template and service instance elements.
+
+ :ivar name: Name
+ :ivar type_name: Type name
+ :ivar value: Value
+ :ivar description: Description
+ """
+
+ __tablename__ = 'parameter'
+
+ name = Column(Text)
+ type_name = Column(Text)
+
+ # Check: value type
+ _value = Column(Binary, name='value')
+ description = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('type_name', self.type_name),
+ ('value', self.value),
+ ('description', self.description)))
+
+ @property
+ def value(self):
+ if self._value is None:
+ return None
+ try:
+ return pickle.loads(self._value)
+ except BaseException:
+ raise exceptions.StorageError('bad format for parameter of type "{0}": {1}'.format(
+ self.type_name, self._value))
+
+ @value.setter
+ def value(self, value):
+ if value is None:
+ self._value = None
+ else:
+ try:
+ self._value = pickle.dumps(value)
+ except (pickle.PicklingError, TypeError):
+ logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}'
+ .format(self.type_name, value))
+ self._value = pickle.dumps(str(value))
+
+ def instantiate(self, context, container):
+ from . import models
+ return models.Parameter(name=self.name,
+ type_name=self.type_name,
+ _value=self._value,
+ description=self.description)
+
+ def coerce_values(self, context, container, report_issues):
+ if self.value is not None:
+ self.value = utils.coerce_value(context, container, self.value,
+ report_issues)
+
+ def dump(self, context):
+ if self.type_name is not None:
+ console.puts('{0}: {1} ({2})'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value),
+ context.style.type(self.type_name)))
+ else:
+ console.puts('{0}: {1}'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value)))
+ if self.description:
+ console.puts(context.style.meta(self.description))
+
+
+class TypeBase(InstanceModelMixin):
+ """
+ Represents a type and its children.
+ """
+
+ __tablename__ = 'type'
+
+ variant = Column(Text, nullable=False)
+ description = Column(Text)
+ _role = Column(Text, name='role')
+
+ @declared_attr
+ def parent(cls):
+ return cls._create_relationship_to_self('parent_type_fk')
+
+ @declared_attr
+ def children(cls):
+ return cls._create_one_to_many_relationship_to_self('parent_type_fk')
+
+ # region foreign keys
+
+ __private_fields__ = ['parent_type_fk']
+
+ # Type one-to-many to Type
+ @declared_attr
+ def parent_type_fk(cls):
+ return cls._create_foreign_key('type', nullable=True)
+
+ # endregion
+
+ @property
+ def role(self):
+ def get_role(the_type):
+ if the_type is None:
+ return None
+ elif the_type._role is None:
+ return get_role(the_type.parent)
+ return the_type._role
+
+ return get_role(self)
+
+ @role.setter
+ def role(self, value):
+ self._role = value
+
+ def is_descendant(self, base_name, name):
+ base = self.get_descendant(base_name)
+ if base is not None:
+ if base.get_descendant(name) is not None:
+ return True
+ return False
+
+ def get_descendant(self, name):
+ if self.name == name:
+ return self
+ for child in self.children:
+ found = child.get_descendant(name)
+ if found is not None:
+ return found
+ return None
+
+ def iter_descendants(self):
+ for child in self.children:
+ yield child
+ for descendant in child.iter_descendants():
+ yield descendant
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('description', self.description),
+ ('role', self.role)))
+
+ @property
+ def as_raw_all(self):
+ types = []
+ self._append_raw_children(types)
+ return types
+
+ def dump(self, context):
+ if self.name:
+ console.puts(context.style.type(self.name))
+ with context.style.indent:
+ for child in self.children:
+ child.dump(context)
+
+ def _append_raw_children(self, types):
+ for child in self.children:
+ raw_child = formatting.as_raw(child)
+ raw_child['parent'] = self.name
+ types.append(raw_child)
+ child._append_raw_children(types)
+
+
+class MetadataBase(TemplateModelMixin):
+ """
+ Custom values associated with the service.
+
+ This model is used by both service template and service instance elements.
+
+ :ivar name: Name
+ :ivar value: Value
+ """
+
+ __tablename__ = 'metadata'
+
+ value = Column(Text)
+
+ @property
+ def as_raw(self):
+ return collections.OrderedDict((
+ ('name', self.name),
+ ('value', self.value)))
+
+ def instantiate(self, context, container):
+ from . import models
+ return models.Metadata(name=self.name,
+ value=self.value)
+
+ def dump(self, context):
+ console.puts('{0}: {1}'.format(
+ context.style.property(self.name),
+ context.style.literal(self.value)))
+
+
+class PluginSpecificationBase(InstanceModelMixin):
+ """
+ Plugin specification model representation.
+ """
+
+ __tablename__ = 'plugin_specification'
+
+ archive_name = Column(Text, nullable=False, index=True)
+ distribution = Column(Text)
+ distribution_release = Column(Text)
+ distribution_version = Column(Text)
+ package_name = Column(Text, nullable=False, index=True)
+ package_source = Column(Text)
+ package_version = Column(Text)
+ supported_platform = Column(Text)
+ supported_py_versions = Column(List)
+
+ # region foreign keys
+
+ __private_fields__ = ['service_template_fk']
+
+ @declared_attr
+ def service_template_fk(cls):
+ return cls._create_foreign_key('service_template', nullable=True)
+
+ # endregion
+
+ def find_plugin(self, plugins):
+ # TODO: this should check versions/distribution and other specification
+ for plugin in plugins:
+ if plugin.name == self.name:
+ return plugin
+ return None