http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/template_elements.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/template_elements.py b/aria/storage/modeling/template_elements.py deleted file mode 100644 index 4212b15..0000000 --- a/aria/storage/modeling/template_elements.py +++ /dev/null @@ -1,1387 +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 copy import deepcopy -from types import FunctionType - -from sqlalchemy import ( - Column, - Text, - Integer, - DateTime, - Boolean, -) -from sqlalchemy.ext.associationproxy import association_proxy -from sqlalchemy.ext.declarative import declared_attr - -from aria.parser import validation -from aria.utils import collections, formatting, console - -from . import ( - utils, - instance_elements, - structure, - type as aria_type -) - -# pylint: disable=no-self-argument, no-member, abstract-method - - -# region Element templates - - -class ServiceTemplateBase(structure.ModelMixin): - - __tablename__ = 'service_template' - - __private_fields__ = ['substitution_template_fk'] - - description = Column(Text) - metadata = Column(Text) - - # region orchestrator required columns - - created_at = Column(DateTime, nullable=False, index=True) - main_file_name = Column(Text) - plan = Column(aria_type.Dict, nullable=False) - updated_at = Column(DateTime) - - # endregion - - # region foreign keys - @declared_attr - def substitution_template_fk(cls): - return cls.foreign_key('substitution_template', nullable=True) - - # endregion - - # region one-to-one relationships - @declared_attr - def substitution_template(cls): - return cls.one_to_one_relationship('substitution_template') - # endregion - - # region many-to-many relationships - - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - @declared_attr - def outputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='outputs') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('description', self.description), - ('metadata', formatting.as_raw(self.metadata)), - ('node_templates', formatting.as_raw_list(self.node_templates)), - ('group_templates', formatting.as_raw_list(self.group_templates)), - ('policy_templates', formatting.as_raw_list(self.policy_templates)), - ('substitution_template', formatting.as_raw(self.substitution_template)), - ('inputs', formatting.as_raw_dict(self.inputs)), - ('outputs', formatting.as_raw_dict(self.outputs)), - ('operation_templates', formatting.as_raw_list(self.operation_templates)))) - - def instantiate(self, context, container): - service_instance = instance_elements.ServiceInstanceBase() - context.modeling.instance = service_instance - - service_instance.description = deepcopy_with_locators(self.description) - - if self.metadata is not None: - service_instance.metadata = self.metadata.instantiate(context, container) - - for node_template in self.node_templates.itervalues(): - for _ in range(node_template.default_instances): - node = node_template.instantiate(context, container) - service_instance.nodes[node.id] = node - - utils.instantiate_dict(context, self, service_instance.groups, self.group_templates) - utils.instantiate_dict(context, self, service_instance.policies, self.policy_templates) - utils.instantiate_dict(context, self, service_instance.operations, self.operation_templates) - - if self.substitution_template is not None: - service_instance.substitution = self.substitution_template.instantiate(context, - container) - - utils.instantiate_dict(context, self, service_instance.inputs, self.inputs) - utils.instantiate_dict(context, self, service_instance.outputs, self.outputs) - - for name, the_input in context.modeling.inputs.iteritems(): - if name not in service_instance.inputs: - context.validation.report('input "%s" is not supported' % name) - else: - service_instance.inputs[name].value = the_input - - return service_instance - - def validate(self, context): - if self.metadata is not None: - self.metadata.validate(context) - utils.validate_dict_values(context, self.node_templates) - utils.validate_dict_values(context, self.group_templates) - utils.validate_dict_values(context, self.policy_templates) - if self.substitution_template is not None: - self.substitution_template.validate(context) - utils.validate_dict_values(context, self.inputs) - utils.validate_dict_values(context, self.outputs) - utils.validate_dict_values(context, self.operation_templates) - - def coerce_values(self, context, container, report_issues): - if self.metadata is not None: - self.metadata.coerce_values(context, container, report_issues) - utils.coerce_dict_values(context, container, self.node_templates, report_issues) - utils.coerce_dict_values(context, container, self.group_templates, report_issues) - utils.coerce_dict_values(context, container, self.policy_templates, report_issues) - if self.substitution_template is not None: - self.substitution_template.coerce_values(context, container, report_issues) - utils.coerce_dict_values(context, container, self.inputs, report_issues) - utils.coerce_dict_values(context, container, self.outputs, report_issues) - utils.coerce_dict_values(context, container, self.operation_templates, report_issues) - - def dump(self, context): - if self.description is not None: - console.puts(context.style.meta(self.description)) - if self.metadata is not None: - self.metadata.dump(context) - for node_template in self.node_templates.itervalues(): - node_template.dump(context) - for group_template in self.group_templates.itervalues(): - group_template.dump(context) - for policy_template in self.policy_templates.itervalues(): - policy_template.dump(context) - if self.substitution_template is not None: - self.substitution_template.dump(context) - dump_parameters(context, self.inputs, 'Inputs') - dump_parameters(context, self.outputs, 'Outputs') - utils.dump_dict_values(context, self.operation_templates, 'Operation templates') - - -class InterfaceTemplateBase(structure.ModelMixin): - __tablename__ = 'interface_template' - - __private_fields__ = ['node_template_fk', - 'group_template_fk'] - - # region foreign keys - - @declared_attr - def node_template_fk(cls): - return cls.foreign_key('node_template', nullable=True) - - @declared_attr - def group_template_fk(cls): - return cls.foreign_key('group_template', nullable=True) - - # endregion - - description = Column(Text) - type_name = Column(Text) - - # region many-to-one relationship - @declared_attr - def node_template(cls): - return cls.many_to_one_relationship('node_template') - - @declared_attr - def group_template(cls): - return cls.many_to_one_relationship('group_template') - - # endregion - - # region many-to-many relationships - - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('inputs', formatting.as_raw_dict(self.properties)), # pylint: disable=no-member - # TODO fix self.properties reference - ('operation_templates', formatting.as_raw_list(self.operation_templates)))) - - def instantiate(self, context, container): - interface = instance_elements.InterfaceBase(self.name, self.type_name) - interface.description = deepcopy_with_locators(self.description) - utils.instantiate_dict(context, container, interface.inputs, self.inputs) - utils.instantiate_dict(context, container, interface.operations, self.operation_templates) - return interface - - def validate(self, context): - if self.type_name: - if context.modeling.interface_types.get_descendant(self.type_name) is None: - context.validation.report('interface "%s" has an unknown type: %s' - % (self.name, formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.inputs) - utils.validate_dict_values(context, self.operation_templates) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.inputs, report_issues) - utils.coerce_dict_values(context, container, self.operation_templates, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Interface type: %s' % context.style.type(self.type_name)) - dump_parameters(context, self.inputs, 'Inputs') - utils.dump_dict_values(context, self.operation_templates, 'Operation templates') - - -class OperationTemplateBase(structure.ModelMixin): - __tablename__ = 'operation_template' - - __private_fields__ = ['service_template_fk', - 'interface_template_fk'] - - # region foreign keys - - @declared_attr - def service_template_fk(cls): - return cls.foreign_key('service_template', nullable=True) - - @declared_attr - def interface_template_fk(cls): - return cls.foreign_key('interface_template', nullable=True) - - # endregion - - description = Column(Text) - implementation = Column(Text) - dependencies = Column(aria_type.StrictList(item_cls=basestring)) - executor = Column(Text) - max_retries = Column(Integer) - retry_interval = Column(Integer) - - # region orchestrator required columns - plugin = Column(Text) - operation = Column(Boolean) - - # region many-to-one relationships - @declared_attr - def service_template(cls): - return cls.many_to_one_relationship('service_template') - - @declared_attr - def interface_template(cls): - return cls.many_to_one_relationship('interface_template') - # endregion - - # region many-to-many relationships - - @declared_attr - def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('implementation', self.implementation), - ('dependencies', self.dependencies), - ('executor', self.executor), - ('max_retries', self.max_retries), - ('retry_interval', self.retry_interval), - ('inputs', formatting.as_raw_dict(self.inputs)))) - - def instantiate(self, context, container): - operation = instance_elements.OperationBase(self.name) - operation.description = deepcopy_with_locators(self.description) - operation.implementation = self.implementation - operation.dependencies = self.dependencies - operation.executor = self.executor - operation.max_retries = self.max_retries - operation.retry_interval = self.retry_interval - utils.instantiate_dict(context, container, operation.inputs, self.inputs) - return operation - - def validate(self, context): - utils.validate_dict_values(context, self.inputs) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.inputs, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - if self.implementation is not None: - console.puts('Implementation: %s' % context.style.literal(self.implementation)) - if self.dependencies: - console.puts('Dependencies: %s' % ', '.join( - (str(context.style.literal(v)) for v in self.dependencies))) - if self.executor is not None: - console.puts('Executor: %s' % context.style.literal(self.executor)) - if self.max_retries is not None: - console.puts('Max retries: %s' % context.style.literal(self.max_retries)) - if self.retry_interval is not None: - console.puts('Retry interval: %s' % context.style.literal(self.retry_interval)) - dump_parameters(context, self.inputs, 'Inputs') - - -class ArtifactTemplateBase(structure.ModelMixin): - """ - A file associated with a :class:`NodeTemplate`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`source_path`: Source path (CSAR or repository) - * :code:`target_path`: Path at destination machine - * :code:`repository_url`: Repository URL - * :code:`repository_credential`: Dict of string - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'artifact_template' - - __private_fields__ = ['node_template_fk'] - - # region foreign keys - - @declared_attr - def node_template_fk(cls): - return cls.foreign_key('node_template') - - # endregion - - description = Column(Text) - type_name = Column(Text) - source_path = Column(Text) - target_path = Column(Text) - repository_url = Column(Text) - repository_credential = Column(aria_type.StrictDict(basestring, basestring)) - - # region many-to-one relationship - @declared_attr - def node_template(cls): - return cls.many_to_one_relationship('node_template') - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('source_path', self.source_path), - ('target_path', self.target_path), - ('repository_url', self.repository_url), - ('repository_credential', formatting.as_agnostic(self.repository_credential)), - ('properties', formatting.as_raw_dict(self.properties.iteritems())))) - - def instantiate(self, context, container): - artifact = instance_elements.ArtifactBase(self.name, self.type_name, self.source_path) - artifact.description = deepcopy_with_locators(self.description) - artifact.target_path = self.target_path - artifact.repository_url = self.repository_url - artifact.repository_credential = self.repository_credential - utils.instantiate_dict(context, container, artifact.properties, self.properties) - return artifact - - def validate(self, context): - if context.modeling.artifact_types.get_descendant(self.type_name) is None: - context.validation.report('artifact "%s" has an unknown type: %s' - % (self.name, formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Artifact type: %s' % context.style.type(self.type_name)) - console.puts('Source path: %s' % context.style.literal(self.source_path)) - if self.target_path is not None: - console.puts('Target path: %s' % context.style.literal(self.target_path)) - if self.repository_url is not None: - console.puts('Repository URL: %s' % context.style.literal(self.repository_url)) - if self.repository_credential: - console.puts('Repository credential: %s' - % context.style.literal(self.repository_credential)) - dump_parameters(context, self.properties) - - -class PolicyTemplateBase(structure.ModelMixin): - """ - Policies can be applied to zero or more :class:`NodeTemplate` or :class:`GroupTemplate` - instances. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`target_node_template_names`: Must be represented in the :class:`ServiceModel` - * :code:`target_group_template_names`: Must be represented in the :class:`ServiceModel` - """ - __tablename__ = 'policy_template' - - __private_fields__ = ['service_templaet_fk', - 'group_template_fk'] - - # region foreign keys - - @declared_attr - def service_template_fk(cls): - return cls.foreign_key('service_template') - - @declared_attr - def group_template_fk(cls): - return cls.foreign_key('group_template') - - # endregion - - description = Column(Text) - type_name = Column(Text) - target_node_template_names = Column(aria_type.StrictList(basestring)) - target_group_template_names = Column(aria_type.StrictList(basestring)) - - # region many-to-one relationship - @declared_attr - def service_template(cls): - return cls.many_to_one_relationship('service_template') - - @declared_attr - def group_template(cls): - return cls.many_to_one_relationship('group_template') - - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('target_node_template_names', self.target_node_template_names), - ('target_group_template_names', self.target_group_template_names))) - - def instantiate(self, context, *args, **kwargs): - policy = instance_elements.PolicyBase(self.name, self.type_name) - utils.instantiate_dict(context, self, policy.properties, self.properties) - for node_template_name in self.target_node_template_names: - policy.target_node_ids.extend( - context.modeling.instance.get_node_ids(node_template_name)) - for group_template_name in self.target_group_template_names: - policy.target_group_ids.extend( - context.modeling.instance.get_group_ids(group_template_name)) - return policy - - def validate(self, context): - if context.modeling.policy_types.get_descendant(self.type_name) is None: - context.validation.report('policy template "%s" has an unknown type: %s' - % (self.name, formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.properties, report_issues) - - def dump(self, context): - console.puts('Policy template: %s' % context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - dump_parameters(context, self.properties) - if self.target_node_template_names: - console.puts('Target node templates: %s' % ', '.join( - (str(context.style.node(v)) for v in self.target_node_template_names))) - if self.target_group_template_names: - console.puts('Target group templates: %s' % ', '.join( - (str(context.style.node(v)) for v in self.target_group_template_names))) - - -class GroupPolicyTemplateBase(structure.ModelMixin): - """ - Policies applied to groups. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`triggers`: Dict of :class:`GroupPolicyTrigger` - """ - - __tablename__ = 'group_policy_template' - - __private_fields__ = ['group_template_fk'] - - # region foreign keys - @declared_attr - def group_template_fk(cls): - return cls.foreign_key('group_template') - - # endregion - - description = Column(Text) - type_name = Column(Text) - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('triggers', formatting.as_raw_list(self.triggers)))) - - def instantiate(self, context, container): - group_policy = instance_elements.GroupPolicyBase(self.name, self.type_name) - group_policy.description = deepcopy_with_locators(self.description) - utils.instantiate_dict(context, container, group_policy.properties, self.properties) - utils.instantiate_dict(context, container, group_policy.triggers, self.triggers) - return group_policy - - def validate(self, context): - if context.modeling.policy_types.get_descendant(self.type_name) is None: - context.validation.report('group policy "%s" has an unknown type: %s' - % (self.name, formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.triggers) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - utils.coerce_dict_values(context, container, self.triggers, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Group policy type: %s' % context.style.type(self.type_name)) - dump_parameters(context, self.properties) - utils.dump_dict_values(context, self.triggers, 'Triggers') - - -class GroupPolicyTriggerTemplateBase(structure.ModelMixin): - """ - Triggers for :class:`GroupPolicyTemplate`. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`implementation`: Implementation string (interpreted by the orchestrator) - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'group_policy_trigger_template' - - __private_fields__ = ['group_policy_template_fk'] - - # region foreign keys - - @declared_attr - def group_policy_template_fk(cls): - return cls.foreign_key('group_policy_template') - - # endregion - - description = Column(Text) - implementation = Column(Text) - - # region many-to-one relationship - @declared_attr - def group_policy_template(cls): - return cls.many_to_one_relationship('group_policy_template') - - # endregion - - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('implementation', self.implementation), - ('properties', formatting.as_raw_dict(self.properties)))) - - def instantiate(self, context, container): - group_policy_trigger = instance_elements.GroupPolicyTriggerBase(self.name, - self.implementation) - group_policy_trigger.description = deepcopy_with_locators(self.description) - utils.instantiate_dict(context, container, group_policy_trigger.properties, - self.properties) - return group_policy_trigger - - def validate(self, context): - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, container, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Implementation: %s' % context.style.literal(self.implementation)) - dump_parameters(context, self.properties) - - -class MappingTemplateBase(structure.ModelMixin): - """ - Used by :class:`SubstitutionTemplate` to map a capability or a requirement to a node. - - Properties: - - * :code:`mapped_name`: Exposed capability or requirement name - * :code:`node_template_name`: Must be represented in the :class:`ServiceModel` - * :code:`name`: Name of capability or requirement at the node template - """ - __tablename__ = 'mapping_template' - - mapped_name = Column(Text) - node_template_name = Column(Text) - - @property - def as_raw(self): - return collections.OrderedDict(( - ('mapped_name', self.mapped_name), - ('node_template_name', self.node_template_name), - ('name', self.name))) - - def instantiate(self, context, *args, **kwargs): - nodes = context.modeling.instance.find_nodes(self.node_template_name) - if len(nodes) == 0: - context.validation.report( - 'mapping "%s" refer to node template "%s" but there are no ' - 'node instances' % (self.mapped_name, - self.node_template_name), - level=validation.Issue.BETWEEN_INSTANCES) - return None - return instance_elements.MappingBase(self.mapped_name, nodes[0].id, self.name) - - def validate(self, context): - if self.node_template_name not in context.modeling.model.node_templates: - context.validation.report('mapping "%s" refers to an unknown node template: %s' - % ( - self.mapped_name, - formatting.safe_repr(self.node_template_name)), - level=validation.Issue.BETWEEN_TYPES) - - def dump(self, context): - console.puts('%s -> %s.%s' % (context.style.node(self.mapped_name), - context.style.node(self.node_template_name), - context.style.node(self.name))) - - -class SubstitutionTemplateBase(structure.ModelMixin): - """ - Used to substitute a single node for the entire deployment. - - Properties: - - * :code:`node_type_name`: Must be represented in the :class:`ModelingContext` - * :code:`capability_templates`: Dict of :class:`MappingTemplate` - * :code:`requirement_templates`: Dict of :class:`MappingTemplate` - """ - __tablename__ = 'substitution_template' - node_type_name = Column(Text) - - # region many-to-many relationships - - @declared_attr - def capability_templates(cls): - return cls.many_to_many_relationship('mapping_template', - table_prefix='capability_templates', - relationship_kwargs=dict(lazy='dynamic')) - - @declared_attr - def requirement_templates(cls): - return cls.many_to_many_relationship('mapping_template', - table_prefix='requirement_templates', - relationship_kwargs=dict(lazy='dynamic')) - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('node_type_name', self.node_type_name), - ('capability_templates', formatting.as_raw_list(self.capability_templates)), - ('requirement_templates', formatting.as_raw_list(self.requirement_templates)))) - - def instantiate(self, context, container): - substitution = instance_elements.SubstitutionBase(self.node_type_name) - utils.instantiate_dict(context, container, substitution.capabilities, - self.capability_templates) - utils.instantiate_dict(context, container, substitution.requirements, - self.requirement_templates) - return substitution - - def validate(self, context): - if context.modeling.node_types.get_descendant(self.node_type_name) is None: - context.validation.report('substitution template has an unknown type: %s' - % formatting.safe_repr(self.node_type_name), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.capability_templates) - utils.validate_dict_values(context, self.requirement_templates) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.capability_templates, report_issues) - utils.coerce_dict_values(context, self, self.requirement_templates, report_issues) - - def dump(self, context): - console.puts('Substitution template:') - with context.style.indent: - console.puts('Node type: %s' % context.style.type(self.node_type_name)) - utils.dump_dict_values(context, self.capability_templates, - 'Capability template mappings') - utils.dump_dict_values(context, self.requirement_templates, - 'Requirement template mappings') - - -# endregion - -# region Node templates - -class NodeTemplateBase(structure.ModelMixin): - __tablename__ = 'node_template' - - __private_fields__ = ['service_template_fk', - 'host_fk'] - - # region foreign_keys - - @declared_attr - def service_template_fk(cls): - return cls.foreign_key('service_template') - - @declared_attr - def host_fk(cls): - return cls.foreign_key('node_template', nullable=True) - - # endregion - - description = Column(Text) - type_name = Column(Text) - default_instances = Column(Integer, default=1) - min_instances = Column(Integer, default=0) - max_instances = Column(Integer, default=None) - target_node_template_constraints = Column(aria_type.StrictList(FunctionType)) - - # region orchestrator required columns - - plugins = Column(aria_type.List) - type_hierarchy = Column(aria_type.List) - - @declared_attr - def host(cls): - return cls.relationship_to_self('host_fk') - - @declared_attr - def service_template_name(cls): - return association_proxy('service_template', cls.name_column_name()) - - # endregion - - # region many-to-one relationship - @declared_attr - def service_template(cls): - return cls.many_to_one_relationship('service_template') - - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - def is_target_node_valid(self, target_node_template): - if self.target_node_template_constraints: - for node_type_constraint in self.target_node_template_constraints: - if not node_type_constraint(target_node_template, self): - return False - return True - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('default_instances', self.default_instances), - ('min_instances', self.min_instances), - ('max_instances', self.max_instances), - ('properties', formatting.as_raw_dict(self.properties)), - ('interface_templates', formatting.as_raw_list(self.interface_templates)), - ('artifact_templates', formatting.as_raw_list(self.artifact_templates)), - ('capability_templates', formatting.as_raw_list(self.capability_templates)), - ('requirement_templates', formatting.as_raw_list(self.requirement_templates)))) - - def instantiate(self, context, *args, **kwargs): - node = instance_elements.NodeBase(context, self.type_name, self.name) - utils.instantiate_dict(context, node, node.properties, self.properties) - utils.instantiate_dict(context, node, node.interfaces, self.interface_templates) - utils.instantiate_dict(context, node, node.artifacts, self.artifact_templates) - utils.instantiate_dict(context, node, node.capabilities, self.capability_templates) - return node - - def validate(self, context): - if context.modeling.node_types.get_descendant(self.type_name) is None: - context.validation.report('node template "%s" has an unknown type: %s' - % (self.name, - formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.interface_templates) - utils.validate_dict_values(context, self.artifact_templates) - utils.validate_dict_values(context, self.capability_templates) - utils.validate_list_values(context, self.requirement_templates) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.properties, report_issues) - utils.coerce_dict_values(context, self, self.interface_templates, report_issues) - utils.coerce_dict_values(context, self, self.artifact_templates, report_issues) - utils.coerce_dict_values(context, self, self.capability_templates, report_issues) - utils.coerce_list_values(context, self, self.requirement_templates, report_issues) - - def dump(self, context): - console.puts('Node template: %s' % context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - console.puts('Instances: %d (%d%s)' - % (self.default_instances, - self.min_instances, - (' to %d' % self.max_instances - if self.max_instances is not None - else ' or more'))) - dump_parameters(context, self.properties) - utils.dump_interfaces(context, self.interface_templates) - utils.dump_dict_values(context, self.artifact_templates, 'Artifact tempaltes') - utils.dump_dict_values(context, self.capability_templates, 'Capability templates') - utils.dump_list_values(context, self.requirement_templates, 'Requirement templates') - - -class GroupTemplateBase(structure.ModelMixin): - """ - A template for creating zero or more :class:`Group` instances. - - Groups are logical containers for zero or more nodes that allow applying zero or more - :class:`GroupPolicy` instances to the nodes together. - - Properties: - - * :code:`name`: Name (will be used as a prefix for group IDs) - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - * :code:`interface_templates`: Dict of :class:`InterfaceTemplate` - * :code:`policy_templates`: Dict of :class:`GroupPolicyTemplate` - * :code:`member_node_template_names`: Must be represented in the :class:`ServiceModel` - * :code:`member_group_template_names`: Must be represented in the :class:`ServiceModel` - """ - __tablename__ = 'group_template' - - __private_fields__ = ['service_template_fk'] - - # region foreign keys - - @declared_attr - def service_template_fk(cls): - return cls.foreign_key('service_template') - - # endregion - - description = Column(Text) - type_name = Column(Text) - member_node_template_names = Column(aria_type.StrictList(basestring)) - member_group_template_names = Column(aria_type.StrictList(basestring)) - - # region many-to-one relationship - @declared_attr - def service_template(cls): - return cls.many_to_one_relationship('service_template') - - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('properties', formatting.as_raw_dict(self.properties)), - ('interface_templates', formatting.as_raw_list(self.interface_templates)), - ('policy_templates', formatting.as_raw_list(self.policy_templates)), - ('member_node_template_names', self.member_node_template_names), - ('member_group_template_names', self.member_group_template_names1))) - - def instantiate(self, context, *args, **kwargs): - group = instance_elements.GroupBase(context, self.type_name, self.name) - utils.instantiate_dict(context, self, group.properties, self.properties) - utils.instantiate_dict(context, self, group.interfaces, self.interface_templates) - utils.instantiate_dict(context, self, group.policies, self.policy_templates) - for member_node_template_name in self.member_node_template_names: - group.member_node_ids += \ - context.modeling.instance.get_node_ids(member_node_template_name) - for member_group_template_name in self.member_group_template_names: - group.member_group_ids += \ - context.modeling.instance.get_group_ids(member_group_template_name) - return group - - def validate(self, context): - if context.modeling.group_types.get_descendant(self.type_name) is None: - context.validation.report('group template "%s" has an unknown type: %s' - % (self.name, formatting.safe_repr(self.type_name)), - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - utils.validate_dict_values(context, self.interface_templates) - utils.validate_dict_values(context, self.policy_templates) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.properties, report_issues) - utils.coerce_dict_values(context, self, self.interface_templates, report_issues) - utils.coerce_dict_values(context, self, self.policy_templates, report_issues) - - def dump(self, context): - console.puts('Group template: %s' % context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - if self.type_name: - console.puts('Type: %s' % context.style.type(self.type_name)) - dump_parameters(context, self.properties) - utils.dump_interfaces(context, self.interface_templates) - utils.dump_dict_values(context, self.policy_templates, 'Policy templates') - if self.member_node_template_names: - console.puts('Member node templates: %s' % ', '.join( - (str(context.style.node(v)) for v in self.member_node_template_names))) - - -# endregion - -# region Relationship templates - -class RequirementTemplateBase(structure.ModelMixin): - """ - A requirement for a :class:`NodeTemplate`. During instantiation will be matched with a - capability of another - node. - - Requirements may optionally contain a :class:`RelationshipTemplate` that will be created between - the nodes. - - Properties: - - * :code:`name`: Name - * :code:`target_node_type_name`: Must be represented in the :class:`ModelingContext` - * :code:`target_node_template_name`: Must be represented in the :class:`ServiceModel` - * :code:`target_node_template_constraints`: List of :class:`FunctionType` - * :code:`target_capability_type_name`: Type of capability in target node - * :code:`target_capability_name`: Name of capability in target node - * :code:`relationship_template`: :class:`RelationshipTemplate` - """ - __tablename__ = 'requirement_template' - - __private_fields__ = ['node_template_fk'] - - # region foreign keys - - @declared_attr - def node_template_fk(cls): - return cls.foreign_key('node_template', nullable=True) - - # endregion - - - target_node_type_name = Column(Text) - target_node_template_name = Column(Text) - target_node_template_constraints = Column(aria_type.StrictList(FunctionType)) - target_capability_type_name = Column(Text) - target_capability_name = Column(Text) - # CHECK: ??? - relationship_template = Column(Text) # optional - - # region many-to-one relationship - @declared_attr - def node_template(cls): - return cls.many_to_one_relationship('node_template') - # endregion - - def instantiate(self, context, container): - raise NotImplementedError - - def find_target(self, context, source_node_template): - # We might already have a specific node template, so we'll just verify it - if self.target_node_template_name is not None: - target_node_template = \ - context.modeling.model.node_templates.get(self.target_node_template_name) - - if not source_node_template.is_target_node_valid(target_node_template): - context.validation.report('requirement "%s" of node template "%s" is for node ' - 'template "%s" but it does not match constraints' - % (self.name, - self.target_node_template_name, - source_node_template.name), - level=validation.Issue.BETWEEN_TYPES) - return None, None - - if self.target_capability_type_name is not None \ - or self.target_capability_name is not None: - target_node_capability = self.find_target_capability(context, - source_node_template, - target_node_template) - if target_node_capability is None: - return None, None - else: - target_node_capability = None - - return target_node_template, target_node_capability - - # Find first node that matches the type - elif self.target_node_type_name is not None: - for target_node_template in context.modeling.model.node_templates.itervalues(): - if not context.modeling.node_types.is_descendant(self.target_node_type_name, - target_node_template.type_name): - continue - - if not source_node_template.is_target_node_valid(target_node_template): - continue - - target_node_capability = self.find_target_capability(context, - source_node_template, - target_node_template) - if target_node_capability is None: - continue - - return target_node_template, target_node_capability - - return None, None - - def find_target_capability(self, context, source_node_template, target_node_template): - for capability_template in target_node_template.capability_templates.itervalues(): - if capability_template.satisfies_requirement(context, - source_node_template, - self, - target_node_template): - return capability_template - return None - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('target_node_type_name', self.target_node_type_name), - ('target_node_template_name', self.target_node_template_name), - ('target_capability_type_name', self.target_capability_type_name), - ('target_capability_name', self.target_capability_name), - ('relationship_template', formatting.as_raw(self.relationship_template)))) - - def validate(self, context): - node_types = context.modeling.node_types - capability_types = context.modeling.capability_types - if self.target_node_type_name \ - and node_types.get_descendant(self.target_node_type_name) is None: - context.validation.report('requirement "%s" refers to an unknown node type: %s' - % (self.name, - formatting.safe_repr(self.target_node_type_name)), - level=validation.Issue.BETWEEN_TYPES) - if self.target_capability_type_name and \ - capability_types.get_descendant(self.target_capability_type_name is None): - context.validation.report('requirement "%s" refers to an unknown capability type: %s' - % (self.name, - formatting.safe_repr(self.target_capability_type_name)), - level=validation.Issue.BETWEEN_TYPES) - if self.relationship_template: - self.relationship_template.validate(context) - - def coerce_values(self, context, container, report_issues): - if self.relationship_template is not None: - self.relationship_template.coerce_values(context, container, report_issues) - - def dump(self, context): - if self.name: - console.puts(context.style.node(self.name)) - else: - console.puts('Requirement:') - with context.style.indent: - if self.target_node_type_name is not None: - console.puts('Target node type: %s' - % context.style.type(self.target_node_type_name)) - elif self.target_node_template_name is not None: - console.puts('Target node template: %s' - % context.style.node(self.target_node_template_name)) - if self.target_capability_type_name is not None: - console.puts('Target capability type: %s' - % context.style.type(self.target_capability_type_name)) - elif self.target_capability_name is not None: - console.puts('Target capability name: %s' - % context.style.node(self.target_capability_name)) - if self.target_node_template_constraints: - console.puts('Target node template constraints:') - with context.style.indent: - for constraint in self.target_node_template_constraints: - console.puts(context.style.literal(constraint)) - if self.relationship_template: - console.puts('Relationship:') - with context.style.indent: - self.relationship_template.dump(context) - - -class CapabilityTemplateBase(structure.ModelMixin): - """ - A capability of a :class:`NodeTemplate`. Nodes expose zero or more capabilities that can be - matched with :class:`Requirement` instances of other nodes. - - Properties: - - * :code:`name`: Name - * :code:`description`: Description - * :code:`type_name`: Must be represented in the :class:`ModelingContext` - * :code:`min_occurrences`: Minimum number of requirement matches required - * :code:`max_occurrences`: Maximum number of requirement matches allowed - * :code:`valid_source_node_type_names`: Must be represented in the :class:`ModelingContext` - * :code:`properties`: Dict of :class:`Parameter` - """ - __tablename__ = 'capability_template' - - __private_fields__ = ['node_template_fk'] - - # region foreign keys - - @declared_attr - def node_template_fk(cls): - return cls.foreign_key('node_template', nullable=True) - - # endregion - - description = Column(Text) - type_name = Column(Text) - min_occurrences = Column(Integer, default=None) # optional - max_occurrences = Column(Integer, default=None) # optional - # CHECK: type? - valid_source_node_type_names = Column(Text) - - # region many-to-one relationship - @declared_attr - def node_template(cls): - return cls.many_to_one_relationship('node_template') - # endregion - - # region many-to-many relationships - - @declared_attr - def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties') - - # endregion - - def satisfies_requirement(self, - context, - source_node_template, - requirement, - target_node_template): - # Do we match the required capability type? - capability_types = context.modeling.capability_types - if not capability_types.is_descendant(requirement.target_capability_type_name, - self.type_name): - return False - - # Are we in valid_source_node_type_names? - if self.valid_source_node_type_names: - for valid_source_node_type_name in self.valid_source_node_type_names: - if not context.modeling.node_types.is_descendant(valid_source_node_type_name, - source_node_template.type_name): - return False - - # Apply requirement constraints - if requirement.target_node_template_constraints: - for node_type_constraint in requirement.target_node_template_constraints: - if not node_type_constraint(target_node_template, source_node_template): - return False - - return True - - @property - def as_raw(self): - return collections.OrderedDict(( - ('name', self.name), - ('description', self.description), - ('type_name', self.type_name), - ('min_occurrences', self.min_occurrences), - ('max_occurrences', self.max_occurrences), - ('valid_source_node_type_names', self.valid_source_node_type_names), - ('properties', formatting.as_raw_dict(self.properties)))) - - def instantiate(self, context, container): - capability = instance_elements.CapabilityBase(self.name, self.type_name) - capability.min_occurrences = self.min_occurrences - capability.max_occurrences = self.max_occurrences - utils.instantiate_dict(context, container, capability.properties, self.properties) - return capability - - def validate(self, context): - if context.modeling.capability_types.get_descendant(self.type_name) is None: - context.validation.report('capability "%s" refers to an unknown type: %s' - % (self.name, formatting.safe_repr(self.type)), # pylint: disable=no-member - # TODO fix self.type reference - level=validation.Issue.BETWEEN_TYPES) - - utils.validate_dict_values(context, self.properties) - - def coerce_values(self, context, container, report_issues): - utils.coerce_dict_values(context, self, self.properties, report_issues) - - def dump(self, context): - console.puts(context.style.node(self.name)) - if self.description: - console.puts(context.style.meta(self.description)) - with context.style.indent: - console.puts('Type: %s' % context.style.type(self.type_name)) - console.puts( - 'Occurrences: %d%s' - % (self.min_occurrences or 0, (' to %d' % self.max_occurrences) - if self.max_occurrences is not None else ' or more')) - if self.valid_source_node_type_names: - console.puts('Valid source node types: %s' - % ', '.join((str(context.style.type(v)) - for v in self.valid_source_node_type_names))) - dump_parameters(context, self.properties) - -# endregion - - -def dump_parameters(context, parameters, name='Properties'): - if not parameters: - return - console.puts('%s:' % name) - with context.style.indent: - for parameter_name, parameter in parameters.items(): - if parameter.type_name is not None: - console.puts('%s = %s (%s)' % (context.style.property(parameter_name), - context.style.literal(parameter.value), - context.style.type(parameter.type_name))) - else: - console.puts('%s = %s' % (context.style.property(parameter_name), - context.style.literal(parameter.value))) - if parameter.description: - console.puts(context.style.meta(parameter.description)) - - -# TODO (left for tal): Move following two methods to some place parser specific -def deepcopy_with_locators(value): - """ - Like :code:`deepcopy`, but also copies over locators. - """ - - res = deepcopy(value) - copy_locators(res, value) - return res - - -def copy_locators(target, source): - """ - Copies over :code:`_locator` for all elements, recursively. - - Assumes that target and source have exactly the same list/dict structure. - """ - - locator = getattr(source, '_locator', None) - if locator is not None: - try: - setattr(target, '_locator', locator) - except AttributeError: - pass - - if isinstance(target, list) and isinstance(source, list): - for i, _ in enumerate(target): - copy_locators(target[i], source[i]) - elif isinstance(target, dict) and isinstance(source, dict): - for k, v in target.items(): - copy_locators(v, source[k])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/type.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/type.py b/aria/storage/modeling/type.py deleted file mode 100644 index 9e3de3d..0000000 --- a/aria/storage/modeling/type.py +++ /dev/null @@ -1,302 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import json -from collections import namedtuple - -from sqlalchemy import ( - TypeDecorator, - VARCHAR, - event -) -from sqlalchemy.ext import mutable - -from .. import exceptions - - -class _MutableType(TypeDecorator): - """ - Dict representation of type. - """ - @property - def python_type(self): - raise NotImplementedError - - def process_literal_param(self, value, dialect): - pass - - impl = VARCHAR - - def process_bind_param(self, value, dialect): - if value is not None: - value = json.dumps(value) - return value - - def process_result_value(self, value, dialect): - if value is not None: - value = json.loads(value) - return value - - -class Dict(_MutableType): - @property - def python_type(self): - return dict - - -class List(_MutableType): - @property - def python_type(self): - return list - - -class _StrictDictMixin(object): - - @classmethod - def coerce(cls, key, value): - "Convert plain dictionaries to MutableDict." - try: - if not isinstance(value, cls): - if isinstance(value, dict): - for k, v in value.items(): - cls._assert_strict_key(k) - cls._assert_strict_value(v) - return cls(value) - return mutable.MutableDict.coerce(key, value) - else: - return value - except ValueError as e: - raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e))) - - def __setitem__(self, key, value): - self._assert_strict_key(key) - self._assert_strict_value(value) - super(_StrictDictMixin, self).__setitem__(key, value) - - def setdefault(self, key, value): - self._assert_strict_key(key) - self._assert_strict_value(value) - super(_StrictDictMixin, self).setdefault(key, value) - - def update(self, *args, **kwargs): - for k, v in kwargs.items(): - self._assert_strict_key(k) - self._assert_strict_value(v) - super(_StrictDictMixin, self).update(*args, **kwargs) - - @classmethod - def _assert_strict_key(cls, key): - if cls._key_cls is not None and not isinstance(key, cls._key_cls): - raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format( - cls._key_cls, type(key) - )) - - @classmethod - def _assert_strict_value(cls, value): - if cls._value_cls is not None and not isinstance(value, cls._value_cls): - raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format( - cls._value_cls, type(value) - )) - - -class _MutableDict(mutable.MutableDict): - """ - Enables tracking for dict values. - """ - - @classmethod - def coerce(cls, key, value): - "Convert plain dictionaries to MutableDict." - try: - return mutable.MutableDict.coerce(key, value) - except ValueError as e: - raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e))) - - -class _StrictListMixin(object): - - @classmethod - def coerce(cls, key, value): - "Convert plain dictionaries to MutableDict." - try: - if not isinstance(value, cls): - if isinstance(value, list): - for item in value: - cls._assert_item(item) - return cls(value) - return mutable.MutableList.coerce(key, value) - else: - return value - except ValueError as e: - raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e))) - - def __setitem__(self, index, value): - """Detect list set events and emit change events.""" - self._assert_item(value) - super(_StrictListMixin, self).__setitem__(index, value) - - def append(self, item): - self._assert_item(item) - super(_StrictListMixin, self).append(item) - - def extend(self, item): - self._assert_item(item) - super(_StrictListMixin, self).extend(item) - - def insert(self, index, item): - self._assert_item(item) - super(_StrictListMixin, self).insert(index, item) - - @classmethod - def _assert_item(cls, item): - if cls._item_cls is not None and not isinstance(item, cls._item_cls): - raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format( - cls._item_cls, type(item) - )) - - -class _MutableList(mutable.MutableList): - - @classmethod - def coerce(cls, key, value): - "Convert plain dictionaries to MutableDict." - try: - return mutable.MutableList.coerce(key, value) - except ValueError as e: - raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e))) - -_StrictDictID = namedtuple('_StrictDictID', 'key_cls, value_cls') -_StrictValue = namedtuple('_StrictValue', 'type_cls, listener_cls') - - -class _StrictDict(object): - """ - This entire class functions as a factory for strict dicts and their listeners. - No type class, and no listener type class is created more than once. If a relevant type class - exists it is returned. - """ - _strict_map = {} - - def __call__(self, key_cls=None, value_cls=None): - strict_dict_map_key = _StrictDictID(key_cls=key_cls, value_cls=value_cls) - if strict_dict_map_key not in self._strict_map: - key_cls_name = getattr(key_cls, '__name__', str(key_cls)) - value_cls_name = getattr(value_cls, '__name__', str(value_cls)) - # Creating the type class itself. this class would be returned (used by the sqlalchemy - # Column). - strict_dict_cls = type( - 'StrictDict_{0}_{1}'.format(key_cls_name, value_cls_name), - (Dict, ), - {} - ) - # Creating the type listening class. - # The new class inherits from both the _MutableDict class and the _StrictDictMixin, - # while setting the necessary _key_cls and _value_cls as class attributes. - listener_cls = type( - 'StrictMutableDict_{0}_{1}'.format(key_cls_name, value_cls_name), - (_StrictDictMixin, _MutableDict), - {'_key_cls': key_cls, '_value_cls': value_cls} - ) - self._strict_map[strict_dict_map_key] = _StrictValue(type_cls=strict_dict_cls, - listener_cls=listener_cls) - - return self._strict_map[strict_dict_map_key].type_cls - -StrictDict = _StrictDict() - - -class _StrictList(object): - """ - This entire class functions as a factory for strict lists and their listeners. - No type class, and no listener type class is created more than once. If a relevant type class - exists it is returned. - """ - _strict_map = {} - - def __call__(self, item_cls=None): - - if item_cls not in self._strict_map: - item_cls_name = getattr(item_cls, '__name__', str(item_cls)) - # Creating the type class itself. this class would be returned (used by the sqlalchemy - # Column). - strict_list_cls = type( - 'StrictList_{0}'.format(item_cls_name), - (List, ), - {} - ) - # Creating the type listening class. - # The new class inherits from both the _MutableList class and the _StrictListMixin, - # while setting the necessary _item_cls as class attribute. - listener_cls = type( - 'StrictMutableList_{0}'.format(item_cls_name), - (_StrictListMixin, _MutableList), - {'_item_cls': item_cls} - ) - self._strict_map[item_cls] = _StrictValue(type_cls=strict_list_cls, - listener_cls=listener_cls) - - return self._strict_map[item_cls].type_cls - -StrictList = _StrictList() - - -def _mutable_association_listener(mapper, cls): - strict_dict_type_to_listener = \ - dict((v.type_cls, v.listener_cls) for v in _StrictDict._strict_map.values()) - - strict_list_type_to_listener = \ - dict((v.type_cls, v.listener_cls) for v in _StrictList._strict_map.values()) - - for prop in mapper.column_attrs: - column_type = prop.columns[0].type - # Dict Listeners - if type(column_type) in strict_dict_type_to_listener: # pylint: disable=unidiomatic-typecheck - strict_dict_type_to_listener[type(column_type)].associate_with_attribute( - getattr(cls, prop.key)) - elif isinstance(column_type, Dict): - _MutableDict.associate_with_attribute(getattr(cls, prop.key)) - - # List Listeners - if type(column_type) in strict_list_type_to_listener: # pylint: disable=unidiomatic-typecheck - strict_list_type_to_listener[type(column_type)].associate_with_attribute( - getattr(cls, prop.key)) - elif isinstance(column_type, List): - _MutableList.associate_with_attribute(getattr(cls, prop.key)) -_LISTENER_ARGS = (mutable.mapper, 'mapper_configured', _mutable_association_listener) - - -def _register_mutable_association_listener(): - event.listen(*_LISTENER_ARGS) - - -def remove_mutable_association_listener(): - """ - Remove the event listener that associates ``Dict`` and ``List`` column types with - ``MutableDict`` and ``MutableList``, respectively. - - This call must happen before any model instance is instantiated. - This is because once it does, that would trigger the listener we are trying to remove. - Once it is triggered, many other listeners will then be registered. - At that point, it is too late. - - The reason this function exists is that the association listener, interferes with ARIA change - tracking instrumentation, so a way to disable it is required. - - Note that the event listener this call removes is registered by default. - """ - if event.contains(*_LISTENER_ARGS): - event.remove(*_LISTENER_ARGS) - -_register_mutable_association_listener() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage/modeling/utils.py ---------------------------------------------------------------------- diff --git a/aria/storage/modeling/utils.py b/aria/storage/modeling/utils.py deleted file mode 100644 index 75e34f5..0000000 --- a/aria/storage/modeling/utils.py +++ /dev/null @@ -1,139 +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 random import randrange - -from shortuuid import ShortUUID - -from ...utils.console import puts - - -# UUID = ShortUUID() # default alphabet is base57, which is alphanumeric without visually ambiguous -# characters; ID length is 22 -UUID = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123456789') # alphanumeric; ID length is 25 - - -def generate_id_string(length=None): - """ - A random string with a strong guarantee of universal uniqueness (uses UUID). - - The default length is 25 characters. - """ - - the_id = UUID.uuid() - if length is not None: - the_id = the_id[:length] - return the_id - - -def generate_hex_string(): - """ - A random string of 5 hex digits with no guarantee of universal uniqueness. - """ - - return '%05x' % randrange(16 ** 5) - - -def validate_dict_values(context, the_dict): - if not the_dict: - return - validate_list_values(context, the_dict.itervalues()) - - -def validate_list_values(context, the_list): - if not the_list: - return - for value in the_list: - value.validate(context) - - -def coerce_dict_values(context, container, the_dict, report_issues=False): - if not the_dict: - return - coerce_list_values(context, container, the_dict.itervalues(), report_issues) - - -def coerce_list_values(context, container, the_list, report_issues=False): - if not the_list: - return - for value in the_list: - value.coerce_values(context, container, report_issues) - - -def instantiate_dict(context, container, the_dict, from_dict): - if not from_dict: - return - for name, value in from_dict.iteritems(): - value = value.instantiate(context, container) - if value is not None: - the_dict[name] = value - - -def dump_list_values(context, the_list, name): - if not the_list: - return - puts('%s:' % name) - with context.style.indent: - for value in the_list: - value.dump(context) - - -def dump_dict_values(context, the_dict, name): - if not the_dict: - return - dump_list_values(context, the_dict.itervalues(), name) - - -def dump_parameters(context, parameters, name='Properties'): - if not parameters: - return - puts('%s:' % name) - with context.style.indent: - for parameter_name, parameter in parameters.iteritems(): - if parameter.type_name is not None: - puts('%s = %s (%s)' % (context.style.property(parameter_name), - context.style.literal(parameter.value), - context.style.type(parameter.type_name))) - else: - puts('%s = %s' % (context.style.property(parameter_name), - context.style.literal(parameter.value))) - if parameter.description: - puts(context.style.meta(parameter.description)) - - -def dump_interfaces(context, interfaces, name='Interfaces'): - if not interfaces: - return - puts('%s:' % name) - with context.style.indent: - for interface in interfaces.itervalues(): - interface.dump(context) - - -def pluralize(noun): - if noun.endswith('s'): - return '{0}es'.format(noun) - elif noun.endswith('y'): - return '{0}ies'.format(noun[:-1]) - else: - return '{0}s'.format(noun) - - -class classproperty(object): # pylint: disable=invalid-name - def __init__(self, f): - self._func = f - - def __get__(self, instance, owner): - return self._func(owner) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/storage_initializer.py ---------------------------------------------------------------------- diff --git a/aria/storage_initializer.py b/aria/storage_initializer.py deleted file mode 100644 index 8c154df..0000000 --- a/aria/storage_initializer.py +++ /dev/null @@ -1,134 +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 datetime import datetime -from threading import RLock - -from .storage.modeling import model -from .orchestrator import operation -from .utils.formatting import safe_repr -from .utils.console import puts, Colored - - -def initialize_storage(context, model_storage, deployment_id): - blueprint = _create_blueprint(context) - model_storage.blueprint.put(blueprint) - - deployment = _create_deployment(context, blueprint, deployment_id) - model_storage.deployment.put(deployment) - - # Create nodes and node instances - for node_template in context.modeling.model.node_templates.values(): - model_storage.node_template.put(node_template) - - for a_node in context.modeling.instance.find_nodes(node_template.name): - node = _create_node_instance(deployment, node_template, a_node) - model_storage.node.put(node) - - # Create relationships - for node_template in context.modeling.model.node_templates.values(): - for index, requirement_template in enumerate(node_template.requirement_templates): - # We are currently limited only to requirements for specific node templates! - if requirement_template.target_node_template_name: - model_storage.requirement_template.put(requirement_template) - - for node in context.modeling.instance.find_nodes(node_template.name): - for relationship_model in node.relationships: - if relationship_model.source_requirement_index == index: - source_instance = \ - model_storage.node.get_by_name(node.id) - target_instance = \ - model_storage.node.get_by_name( - relationship_model.target_node_id) - relationship = \ - _create_relationship_instance(source_instance, target_instance) - model_storage.relationship.put(relationship) - - -def _create_blueprint(context): - now = datetime.utcnow() - main_file_name = unicode(context.presentation.location) - try: - name = context.modeling.model.metadata.values.get('template_name') - except AttributeError: - name = None - - return model.ServiceTemplate( - plan={}, - name=name or main_file_name, - description=context.modeling.model.description or '', - created_at=now, - updated_at=now, - main_file_name=main_file_name - ) - - -def _create_deployment(context, service_template, service_instance_id): - now = datetime.utcnow() - return model.ServiceInstance( - name='{0}_{1}'.format(service_template.name, service_instance_id), - service_template=service_template, - description=context.modeling.instance.description or '', - created_at=now, - updated_at=now, - workflows={}, - permalink='', - policy_triggers={}, - scaling_groups={} - ) - - -def _create_node_instance(service_instance, node, node_model): - return model.Node( - service_instance=service_instance, - name=node_model.id, - runtime_properties={}, - node_template=node, - state='', - scaling_groups=[] - ) - - -def _create_relationship_instance(source_instance, target_instance): - return model.Relationship( - source_node=source_instance, - target_node=target_instance - ) - - -_TERMINAL_LOCK = RLock() - - -@operation -def _dry_node(ctx, _plugin, _implementation, **kwargs): - with _TERMINAL_LOCK: - print '> node instance: %s' % Colored.red(ctx.node.name) - _dump_implementation(_plugin, _implementation) - - -@operation -def _dry_relationship(ctx, _plugin, _implementation, **kwargs): - with _TERMINAL_LOCK: - puts('> relationship instance: %s -> %s' % ( - Colored.red(ctx.relationship.source_node_instance.name), - Colored.red(ctx.relationship.target_node_instance.name))) - _dump_implementation(_plugin, _implementation) - - -def _dump_implementation(plugin, implementation): - if plugin: - print ' plugin: %s' % Colored.magenta(plugin) - if implementation: - print ' implementation: %s' % Colored.yellow(safe_repr(implementation)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/utils/exceptions.py ---------------------------------------------------------------------- diff --git a/aria/utils/exceptions.py b/aria/utils/exceptions.py index a19eb78..9e3e80f 100644 --- a/aria/utils/exceptions.py +++ b/aria/utils/exceptions.py @@ -15,11 +15,14 @@ import sys import linecache +import traceback as tb import jsonpickle -from clint.textui import indent -from .console import (puts, Colored) +from .console import (puts, indent, Colored) + + +ENTRY_FORMAT = 'File "{filename}", line {lineno}, in {name}' def print_exception(e, full=True, cause=False, traceback=None): @@ -27,14 +30,16 @@ def print_exception(e, full=True, cause=False, traceback=None): Prints the exception with nice colors and such. """ def format_heading(e): - return '%s%s: %s' % (Colored.red('Caused by ') if cause else '', Colored.red( - e.__class__.__name__, bold=True), Colored.red(e)) + return '{0}{1}: {2}'.format( + Colored.red('Caused by ') if cause else '', + Colored.red(e.__class__.__name__, bold=True), + Colored.red(e)) puts(format_heading(e)) if full: if cause: if traceback: - print_traceback(traceback) + print_traceback(traceback, True) else: print_traceback() if hasattr(e, 'cause') and e.cause: @@ -42,7 +47,7 @@ def print_exception(e, full=True, cause=False, traceback=None): print_exception(e.cause, full=full, cause=True, traceback=traceback) -def print_traceback(traceback=None): +def print_traceback(traceback=None, print_last_stack=False): """ Prints the traceback with nice colors and such. """ @@ -51,20 +56,37 @@ def print_traceback(traceback=None): _, _, traceback = sys.exc_info() while traceback is not None: frame = traceback.tb_frame - lineno = traceback.tb_lineno code = frame.f_code filename = code.co_filename + lineno = traceback.tb_lineno name = code.co_name with indent(2): - puts('File "%s", line %s, in %s' % (Colored.blue(filename), - Colored.cyan(lineno), - Colored.cyan(name))) + puts(ENTRY_FORMAT.format(filename=Colored.blue(filename), + lineno=Colored.cyan(lineno), + name=Colored.cyan(name))) linecache.checkcache(filename) line = linecache.getline(filename, lineno, frame.f_globals) if line: with indent(2): - puts(Colored.black(line.strip())) + puts(line.strip()) traceback = traceback.tb_next + if print_last_stack and (traceback is None): + # Print stack of *last* traceback + _print_stack(frame) + + +def _print_stack(frame): + entries = tb.extract_stack(frame) + if not entries: + return + puts(Colored.red('Call stack:')) + with indent(2): + for filename, lineno, name, line in entries: + puts(ENTRY_FORMAT.format(filename=Colored.blue(filename), + lineno=Colored.cyan(lineno), + name=Colored.cyan(name))) + with indent(2): + puts(line) class _WrappedException(Exception): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/utils/formatting.py ---------------------------------------------------------------------- diff --git a/aria/utils/formatting.py b/aria/utils/formatting.py index 3725bc7..8a223e9 100644 --- a/aria/utils/formatting.py +++ b/aria/utils/formatting.py @@ -13,14 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import # so we can import standard 'collections' - import json from types import MethodType + from ruamel import yaml # @UnresolvedImport from .collections import FrozenList, FrozenDict, StrictList, StrictDict, OrderedDict + +PLURALIZE_EXCEPTIONS = {} + + # Add our types to ruamel.yaml (for round trips) yaml.representer.RoundTripRepresenter.add_representer( FrozenList, yaml.representer.RoundTripRepresenter.represent_list) @@ -108,6 +111,18 @@ def string_list_as_string(strings): return ', '.join('"%s"' % safe_str(v) for v in strings) +def pluralize(noun): + plural = PLURALIZE_EXCEPTIONS.get(noun) + if plural is not None: + return plural + elif noun.endswith('s'): + return '{0}es'.format(noun) + elif noun.endswith('y'): + return '{0}ies'.format(noun[:-1]) + else: + return '{0}s'.format(noun) + + def as_raw(value): """ Converts values using their :code:`as_raw` property, if it exists, recursively. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/aria/utils/uuid.py ---------------------------------------------------------------------- diff --git a/aria/utils/uuid.py b/aria/utils/uuid.py new file mode 100644 index 0000000..1f340c6 --- /dev/null +++ b/aria/utils/uuid.py @@ -0,0 +1,66 @@ +# 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 __future__ import absolute_import # so we can import standard 'uuid' + +from random import randrange +from uuid import uuid4 + +from shortuuid import ShortUUID + + +# Alphanumeric without visually ambiguous characters; default length is 22 +UUID_BASE57 = ShortUUID() + +# Lower-case alphanumeric; default length is 25 +UUID_LOWERCASE_ALPHANUMERIC = ShortUUID(alphabet='abcdefghijklmnopqrstuvwxyz0123456789') + + +def generate_uuid(length=None, variant='base57'): + """ + A random string with varying degrees of guarantee of universal uniqueness. + + :param variant: options are: + * 'base57' (the default) uses a mix of upper and lowercase alphanumerics + ensuring no visually ambiguous characters; default length 22 + * 'alphanumeric' uses lowercase alphanumeric; default length 25 + * 'uuid' user lowercase hexadecimal in the classic UUID format, including + dashes; length is always 36 + * 'hex' uses lowercase hexadecimal characters but has no guarantee of + uniqueness; default length of 5 + """ + + if variant == 'base57': + the_id = UUID_BASE57.uuid() + if length is not None: + the_id = the_id[:length] + + elif variant == 'alphanumeric': + the_id = UUID_LOWERCASE_ALPHANUMERIC.uuid() + if length is not None: + the_id = the_id[:length] + + elif variant == 'uuid': + the_id = str(uuid4()) + + elif variant == 'hex': + length = length or 5 + # See: http://stackoverflow.com/a/2782859 + the_id = ('%0' + str(length) + 'x') % randrange(16 ** length) + + else: + raise ValueError('unsupported UUID variant: {0}'.format(variant)) + + return the_id http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/9841ca4a/docs/requirements.txt ---------------------------------------------------------------------- diff --git a/docs/requirements.txt b/docs/requirements.txt index dfd5f79..72b28f1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -10,5 +10,5 @@ # See the License for the specific language governing permissions and # limitations under the License. -Sphinx==1.5b1 -sphinx_rtd_theme==0.1.10a0 +Sphinx==1.5.3 +sphinx_rtd_theme==0.2.4
