http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index 092de51..c9a02eb 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -30,7 +30,7 @@ from sqlalchemy.ext.declarative import declared_attr from ..parser import validation from ..utils import collections, formatting, console -from .bases import TemplateModelMixin +from .mixins import TemplateModelMixin from . import ( utils, types as modeling_types @@ -64,10 +64,10 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public :vartype inputs: {basestring: :class:`Parameter`} :ivar outputs: These parameters are filled in after service installation :vartype outputs: {basestring: :class:`Parameter`} - :ivar operation_templates: Custom operations that can be performed on the service - :vartype operation_templates: {basestring: :class:`OperationTemplate`} - :ivar plugins: Plugins required by services - :vartype plugins: {basestring: :class:`Plugin`} + :ivar workflow_templates: Custom workflows that can be performed on the service + :vartype workflow_templates: {basestring: :class:`OperationTemplate`} + :ivar plugin_specifications: Plugins required by services + :vartype plugin_specifications: {basestring: :class:`PluginSpecification`} :ivar node_types: Base for the node type hierarchy :vartype node_types: :class:`Type` :ivar group_types: Base for the group type hierarchy @@ -82,8 +82,8 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public :vartype interface_types: :class:`Type` :ivar artifact_types: Base for the artifact type hierarchy :vartype artifact_types: :class:`Type` - :ivar plugins: Plugins required to be installed - :vartype plugins: {basestring: :class:`Plugin`} + :ivar plugin_specifications: Plugins required to be installed + :vartype plugin_specifications: {basestring: :class:`PluginSpecification`} :ivar created_at: Creation timestamp :vartype created_at: :class:`datetime.datetime` :ivar updated_at: Update timestamp @@ -101,69 +101,73 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public @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') + return cls._create_many_to_many_relationship('metadata', dict_key='name') @declared_attr def node_templates(cls): - return cls.one_to_many_relationship('node_template') + return cls._create_one_to_many_relationship('node_template') @declared_attr def group_templates(cls): - return cls.one_to_many_relationship('group_template') + return cls._create_one_to_many_relationship('group_template') @declared_attr def policy_templates(cls): - return cls.one_to_many_relationship('policy_template') + return cls._create_one_to_many_relationship('policy_template') @declared_attr def substitution_template(cls): - return cls.one_to_one_relationship('substitution_template') + return cls._create_one_to_one_relationship('substitution_template') @declared_attr def inputs(cls): - return cls.many_to_many_relationship('parameter', table_prefix='inputs', - dict_key='name') + return cls._create_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') + return cls._create_many_to_many_relationship('parameter', table_prefix='outputs', + dict_key='name') @declared_attr - def operation_templates(cls): - return cls.one_to_many_relationship('operation_template', dict_key='name') + def workflow_templates(cls): + return cls._create_one_to_many_relationship('operation_template', dict_key='name') @declared_attr - def plugins(cls): - return cls.one_to_many_relationship('plugin') + def plugin_specifications(cls): + return cls._create_one_to_many_relationship('plugin_specification') @declared_attr def node_types(cls): - return cls.one_to_one_relationship('type', key='node_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='node_type_fk', backreference='') @declared_attr def group_types(cls): - return cls.one_to_one_relationship('type', key='group_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='group_type_fk', backreference='') @declared_attr def policy_types(cls): - return cls.one_to_one_relationship('type', key='policy_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='policy_type_fk', backreference='') @declared_attr def relationship_types(cls): - return cls.one_to_one_relationship('type', key='relationship_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='relationship_type_fk', + backreference='') @declared_attr def capability_types(cls): - return cls.one_to_one_relationship('type', key='capability_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='capability_type_fk', + backreference='') @declared_attr def interface_types(cls): - return cls.one_to_one_relationship('type', key='interface_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='interface_type_fk', + backreference='') @declared_attr def artifact_types(cls): - return cls.one_to_one_relationship('type', key='artifact_type_fk', backreference='') + return cls._create_one_to_one_relationship('type', key='artifact_type_fk', + backreference='') # region orchestration @@ -186,42 +190,42 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public # ServiceTemplate one-to-one to SubstitutionTemplate @declared_attr def substitution_template_fk(cls): - return cls.foreign_key('substitution_template', nullable=True) + return cls._create_foreign_key('substitution_template', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def node_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def group_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def policy_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def relationship_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def capability_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def interface_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # ServiceTemplate one-to-one to Type @declared_attr def artifact_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # endregion @@ -250,7 +254,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public ('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)))) + ('workflow_templates', formatting.as_raw_list(self.workflow_templates)))) @property def types_as_raw(self): @@ -283,7 +287,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public utils.instantiate_list(context, self, service.groups, self.group_templates) utils.instantiate_list(context, self, service.policies, self.policy_templates) - utils.instantiate_dict(context, self, service.operations, self.operation_templates) + utils.instantiate_dict(context, self, service.workflows, self.workflow_templates) if self.substitution_template is not None: service.substitution = self.substitution_template.instantiate(context, container) @@ -308,7 +312,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public 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) + utils.validate_dict_values(context, self.workflow_templates) if self.node_types is not None: self.node_types.validate(context) if self.group_types is not None: @@ -333,7 +337,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public 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) + utils.coerce_dict_values(context, container, self.workflow_templates, report_issues) def dump(self, context): if self.description is not None: @@ -349,7 +353,7 @@ class ServiceTemplateBase(TemplateModelMixin): # pylint: disable=too-many-public self.substitution_template.dump(context) utils.dump_dict_values(context, self.inputs, 'Inputs') utils.dump_dict_values(context, self.outputs, 'Outputs') - utils.dump_dict_values(context, self.operation_templates, 'Operation templates') + utils.dump_dict_values(context, self.workflow_templates, 'Workflow templates') def dump_types(self, context): if self.node_types.children: @@ -407,8 +411,8 @@ class NodeTemplateBase(TemplateModelMixin): :vartype requirement_templates: [:class:`RequirementTemplate`] :ivar target_node_template_constraints: Constraints for filtering relationship targets :vartype target_node_template_constraints: [:class:`FunctionType`] - :ivar plugins: Plugins required to be installed on the node's host - :vartype plugins: {basestring: :class:`Plugin`} + :ivar plugin_specifications: Plugins required to be installed on the node's host + :vartype plugin_specifications: {basestring: :class:`PluginSpecification`} :ivar service_template: Containing service template :vartype service_template: :class:`ServiceTemplate` @@ -426,7 +430,7 @@ class NodeTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') description = Column(Text) default_instances = Column(Integer, default=1) @@ -435,32 +439,32 @@ class NodeTemplateBase(TemplateModelMixin): @declared_attr def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties', - dict_key='name') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') @declared_attr def interface_templates(cls): - return cls.one_to_many_relationship('interface_template', dict_key='name') + return cls._create_one_to_many_relationship('interface_template', dict_key='name') @declared_attr def artifact_templates(cls): - return cls.one_to_many_relationship('artifact_template', dict_key='name') + return cls._create_one_to_many_relationship('artifact_template', dict_key='name') @declared_attr def capability_templates(cls): - return cls.one_to_many_relationship('capability_template', dict_key='name') + return cls._create_one_to_many_relationship('capability_template', dict_key='name') @declared_attr def requirement_templates(cls): - return cls.one_to_many_relationship('requirement_template', - foreign_key='node_template_fk', - backreference='node_template') + return cls._create_one_to_many_relationship('requirement_template', + foreign_key='node_template_fk', + backreference='node_template') target_node_template_constraints = Column(modeling_types.StrictList(FunctionType)) @declared_attr - def plugins(cls): - return cls.many_to_many_relationship('plugin') + def plugin_specifications(cls): + return cls._create_many_to_many_relationship('plugin_specification') # region foreign_keys @@ -470,12 +474,12 @@ class NodeTemplateBase(TemplateModelMixin): # NodeTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # ServiceTemplate one-to-many to NodeTemplate @declared_attr def service_template_fk(cls): - return cls.foreign_key('service_template') + return cls._create_foreign_key('service_template') # endregion @@ -579,22 +583,22 @@ class GroupTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') description = Column(Text) @declared_attr def node_templates(cls): - return cls.many_to_many_relationship('node_template') + return cls._create_many_to_many_relationship('node_template') @declared_attr def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties', - dict_key='name') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') @declared_attr def interface_templates(cls): - return cls.one_to_many_relationship('interface_template', dict_key='name') + return cls._create_one_to_many_relationship('interface_template', dict_key='name') # region foreign keys @@ -604,12 +608,12 @@ class GroupTemplateBase(TemplateModelMixin): # GroupTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # ServiceTemplate one-to-many to GroupTemplate @declared_attr def service_template_fk(cls): - return cls.foreign_key('service_template') + return cls._create_foreign_key('service_template') # endregion @@ -684,22 +688,22 @@ class PolicyTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') description = Column(Text) @declared_attr def node_templates(cls): - return cls.many_to_many_relationship('node_template') + return cls._create_many_to_many_relationship('node_template') @declared_attr def group_templates(cls): - return cls.many_to_many_relationship('group_template') + return cls._create_many_to_many_relationship('group_template') @declared_attr def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties', - dict_key='name') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') # region foreign keys @@ -709,12 +713,12 @@ class PolicyTemplateBase(TemplateModelMixin): # PolicyTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # ServiceTemplate one-to-many to PolicyTemplate @declared_attr def service_template_fk(cls): - return cls.foreign_key('service_template') + return cls._create_foreign_key('service_template') # endregion @@ -781,11 +785,12 @@ class SubstitutionTemplateBase(TemplateModelMixin): @declared_attr def node_type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') @declared_attr def mappings(cls): - return cls.one_to_many_relationship('substitution_template_mapping', dict_key='name') + return cls._create_one_to_many_relationship('substitution_template_mapping', + dict_key='name') # region foreign keys @@ -794,7 +799,7 @@ class SubstitutionTemplateBase(TemplateModelMixin): # SubstitutionTemplate many-to-one to Type @declared_attr def node_type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # endregion @@ -847,15 +852,15 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin): @declared_attr def node_template(cls): - return cls.one_to_one_relationship('node_template') + return cls._create_one_to_one_relationship('node_template') @declared_attr def capability_template(cls): - return cls.one_to_one_relationship('capability_template') + return cls._create_one_to_one_relationship('capability_template') @declared_attr def requirement_template(cls): - return cls.one_to_one_relationship('requirement_template') + return cls._create_one_to_one_relationship('requirement_template') # region foreign keys @@ -867,22 +872,22 @@ class SubstitutionTemplateMappingBase(TemplateModelMixin): # SubstitutionTemplate one-to-many to SubstitutionTemplateMapping @declared_attr def substitution_template_fk(cls): - return cls.foreign_key('substitution_template') + return cls._create_foreign_key('substitution_template') # SubstitutionTemplate one-to-one to NodeTemplate @declared_attr def node_template_fk(cls): - return cls.foreign_key('node_template') + return cls._create_foreign_key('node_template') # SubstitutionTemplate one-to-one to CapabilityTemplate @declared_attr def capability_template_fk(cls): - return cls.foreign_key('capability_template', nullable=True) + return cls._create_foreign_key('capability_template', nullable=True) # SubstitutionTemplate one-to-one to RequirementTemplate @declared_attr def requirement_template_fk(cls): - return cls.foreign_key('requirement_template', nullable=True) + return cls._create_foreign_key('requirement_template', nullable=True) # endregion @@ -965,24 +970,25 @@ class RequirementTemplateBase(TemplateModelMixin): @declared_attr def target_node_type(cls): - return cls.many_to_one_relationship('type', key='target_node_type_fk', backreference='') + return cls._create_many_to_one_relationship('type', key='target_node_type_fk', + backreference='') @declared_attr def target_node_template(cls): - return cls.one_to_one_relationship('node_template', key='target_node_template_fk', - backreference='') + return cls._create_one_to_one_relationship('node_template', key='target_node_template_fk', + backreference='') @declared_attr def target_capability_type(cls): - return cls.one_to_one_relationship('type', key='target_capability_type_fk', - backreference='') + return cls._create_one_to_one_relationship('type', key='target_capability_type_fk', + backreference='') target_capability_name = Column(Text) target_node_template_constraints = Column(modeling_types.StrictList(FunctionType)) @declared_attr def relationship_template(cls): - return cls.one_to_one_relationship('relationship_template') + return cls._create_one_to_one_relationship('relationship_template') # region foreign keys @@ -995,27 +1001,27 @@ class RequirementTemplateBase(TemplateModelMixin): # RequirementTemplate many-to-one to Type @declared_attr def target_node_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # RequirementTemplate one-to-one to NodeTemplate @declared_attr def target_node_template_fk(cls): - return cls.foreign_key('node_template', nullable=True) + return cls._create_foreign_key('node_template', nullable=True) # RequirementTemplate one-to-one to NodeTemplate @declared_attr def target_capability_type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # NodeTemplate one-to-many to RequirementTemplate @declared_attr def node_template_fk(cls): - return cls.foreign_key('node_template') + return cls._create_foreign_key('node_template') # RequirementTemplate one-to-one to RelationshipTemplate @declared_attr def relationship_template_fk(cls): - return cls.foreign_key('relationship_template', nullable=True) + return cls._create_foreign_key('relationship_template', nullable=True) # endregion @@ -1146,18 +1152,18 @@ class RelationshipTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_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') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') @declared_attr def interface_templates(cls): - return cls.one_to_many_relationship('interface_template', dict_key='name') + return cls._create_one_to_many_relationship('interface_template', dict_key='name') # region foreign keys @@ -1166,7 +1172,7 @@ class RelationshipTemplateBase(TemplateModelMixin): # RelationshipTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type', nullable=True) + return cls._create_foreign_key('type', nullable=True) # endregion @@ -1243,7 +1249,7 @@ class CapabilityTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') description = Column(Text) min_occurrences = Column(Integer, default=None) # optional @@ -1251,12 +1257,12 @@ class CapabilityTemplateBase(TemplateModelMixin): @declared_attr def valid_source_node_types(cls): - return cls.many_to_many_relationship('type', table_prefix='valid_sources') + return cls._create_many_to_many_relationship('type', table_prefix='valid_sources') @declared_attr def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties', - dict_key='name') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') # region foreign keys @@ -1266,12 +1272,12 @@ class CapabilityTemplateBase(TemplateModelMixin): # CapabilityTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # NodeTemplate one-to-many to CapabilityTemplate @declared_attr def node_template_fk(cls): - return cls.foreign_key('node_template') + return cls._create_foreign_key('node_template') # endregion @@ -1375,17 +1381,17 @@ class InterfaceTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_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') + return cls._create_many_to_many_relationship('parameter', table_prefix='inputs', + dict_key='name') @declared_attr def operation_templates(cls): - return cls.one_to_many_relationship('operation_template', dict_key='name') + return cls._create_one_to_many_relationship('operation_template', dict_key='name') # region foreign keys @@ -1397,22 +1403,22 @@ class InterfaceTemplateBase(TemplateModelMixin): # InterfaceTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # NodeTemplate one-to-many to InterfaceTemplate @declared_attr def node_template_fk(cls): - return cls.foreign_key('node_template', nullable=True) + return cls._create_foreign_key('node_template', nullable=True) # GroupTemplate one-to-many to InterfaceTemplate @declared_attr def group_template_fk(cls): - return cls.foreign_key('group_template', nullable=True) + return cls._create_foreign_key('group_template', nullable=True) # RelationshipTemplate one-to-many to InterfaceTemplate @declared_attr def relationship_template_fk(cls): - return cls.foreign_key('relationship_template', nullable=True) + return cls._create_foreign_key('relationship_template', nullable=True) # endregion @@ -1458,14 +1464,14 @@ class OperationTemplateBase(TemplateModelMixin): """ An operation in a :class:`InterfaceTemplate`. - Operations are executed by an associated :class:`Plugin` via an executor. + Operations are executed by an associated :class:`PluginSpecification` via an executor. :ivar name: Name (unique for the interface or service template) :vartype name: basestring :ivar description: Human-readable description :vartype description: basestring - :ivar plugin: Associated plugin - :vartype plugin: :class:`Plugin` + :ivar plugin_specification: Associated plugin + :vartype plugin_specification: :class:`PluginSpecification` :ivar implementation: Implementation string (interpreted by the plugin) :vartype implementation: basestring :ivar dependencies: Dependency strings (interpreted by the plugin) @@ -1492,16 +1498,16 @@ class OperationTemplateBase(TemplateModelMixin): description = Column(Text) @declared_attr - def plugin(cls): - return cls.one_to_one_relationship('plugin') + def plugin_specification(cls): + return cls._create_one_to_one_relationship('plugin_specification') 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') + return cls._create_many_to_many_relationship('parameter', table_prefix='inputs', + dict_key='name') executor = Column(Text) max_retries = Column(Integer) @@ -1516,17 +1522,17 @@ class OperationTemplateBase(TemplateModelMixin): # ServiceTemplate one-to-many to OperationTemplate @declared_attr def service_template_fk(cls): - return cls.foreign_key('service_template', nullable=True) + return cls._create_foreign_key('service_template', nullable=True) # InterfaceTemplate one-to-many to OperationTemplate @declared_attr def interface_template_fk(cls): - return cls.foreign_key('interface_template', nullable=True) + return cls._create_foreign_key('interface_template', nullable=True) - # OperationTemplate one-to-one to Plugin + # OperationTemplate one-to-one to PluginSpecification @declared_attr - def plugin_fk(cls): - return cls.foreign_key('plugin', nullable=True) + def plugin_specification_fk(cls): + return cls._create_foreign_key('plugin_specification', nullable=True) # endregion @@ -1548,7 +1554,7 @@ class OperationTemplateBase(TemplateModelMixin): description=utils.deepcopy_with_locators(self.description), implementation=self.implementation, dependencies=self.dependencies, - plugin=self.plugin, + plugin_specification=self.plugin_specification, executor=self.executor, max_retries=self.max_retries, retry_interval=self.retry_interval, @@ -1614,7 +1620,7 @@ class ArtifactTemplateBase(TemplateModelMixin): @declared_attr def type(cls): - return cls.many_to_one_relationship('type') + return cls._create_many_to_one_relationship('type') description = Column(Text) source_path = Column(Text) @@ -1624,8 +1630,8 @@ class ArtifactTemplateBase(TemplateModelMixin): @declared_attr def properties(cls): - return cls.many_to_many_relationship('parameter', table_prefix='properties', - dict_key='name') + return cls._create_many_to_many_relationship('parameter', table_prefix='properties', + dict_key='name') # region foreign keys @@ -1635,12 +1641,12 @@ class ArtifactTemplateBase(TemplateModelMixin): # ArtifactTemplate many-to-one to Type @declared_attr def type_fk(cls): - return cls.foreign_key('type') + return cls._create_foreign_key('type') # NodeTemplate one-to-many to ArtifactTemplate @declared_attr def node_template_fk(cls): - return cls.foreign_key('node_template') + return cls._create_foreign_key('node_template') # endregion
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/api/task.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py index d434da8..744d1b4 100644 --- a/aria/orchestrator/workflows/api/task.py +++ b/aria/orchestrator/workflows/api/task.py @@ -59,10 +59,7 @@ class OperationTask(BaseTask): Represents an operation task in the task_graph """ - SOURCE_OPERATION = 'source' - TARGET_OPERATION = 'target' - - NAME_FORMAT = '{type}:{id}->{interface}/{operation}' + NAME_FORMAT = '{interface}:{operation}@{type}:{id}' def __init__(self, name, @@ -73,8 +70,7 @@ class OperationTask(BaseTask): ignore_failure=None, inputs=None, plugin=None, - runs_on=None, - dry=False): + runs_on=None): """ Creates an operation task using the name, details, node instance and any additional kwargs. @@ -84,12 +80,9 @@ class OperationTask(BaseTask): """ assert isinstance(actor, (models.Node, models.Relationship)) + assert (runs_on is None) or (runs_on in models.Task.RUNS_ON) super(OperationTask, self).__init__() - if dry: - from ..dry import convert_to_dry - plugin, implementation, inputs = convert_to_dry(plugin, implementation, inputs) - # Coerce inputs if inputs is None: inputs = {} @@ -131,13 +124,21 @@ class OperationTask(BaseTask): 'Could not find operation "{0}" on interface "{1}" for node "{2}"'.format( operation_name, interface_name, node.name)) + plugin = None + if operation.plugin_specification: + plugin = cls._find_plugin(operation.plugin_specification, kwargs) + if plugin is None: + raise exceptions.TaskException( + 'Could not find plugin of operation "{0}" on interface "{1}" for node "{2}"' + .format(operation_name, interface_name, node.name)) + return cls( actor=node, name=cls.NAME_FORMAT.format(type='node', - id=node.id, + id=node.name, interface=interface_name, operation=operation_name), - plugin=operation.plugin, + plugin=plugin, implementation=operation.implementation, inputs=cls._merge_inputs(operation.inputs, inputs), runs_on=models.Task.RUNS_ON_NODE, @@ -165,13 +166,21 @@ class OperationTask(BaseTask): 'Could not find operation "{0}" on interface "{1}" for relationship "{2}"'.format( operation_name, interface_name, relationship.name)) + plugin = None + if operation.plugin_specification: + plugin = cls._find_plugin(operation.plugin_specification, kwargs) + if plugin is None: + raise exceptions.TaskException( + 'Could not find plugin of operation "{0}" on interface "{1}" for relationship ' + '"{2}"'.format(operation_name, interface_name, relationship.name)) + return cls( actor=relationship, name=cls.NAME_FORMAT.format(type='relationship', - id=relationship.id, + id=relationship.name, interface=interface_name, operation=operation_name), - plugin=operation.plugin, + plugin=plugin, implementation=operation.implementation, inputs=cls._merge_inputs(operation.inputs, inputs), runs_on=runs_on, @@ -186,6 +195,13 @@ class OperationTask(BaseTask): return None @classmethod + def _find_plugin(cls, plugin_specification, kwargs): + workflow_context = kwargs.get('ctx') if kwargs else None + if workflow_context is None: + workflow_context = context.workflow.current.get() + return plugin_specification.find_plugin(workflow_context.model.plugin.list()) + + @classmethod def _merge_inputs(cls, operation_inputs, override_inputs=None): final_inputs = OrderedDict(operation_inputs) if override_inputs: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/execute_operation.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py index ed4ada3..348f47a 100644 --- a/aria/orchestrator/workflows/builtin/execute_operation.py +++ b/aria/orchestrator/workflows/builtin/execute_operation.py @@ -58,8 +58,7 @@ def execute_operation( type_names=type_names)) if run_by_dependency_order: - filtered_node_ids = set(node_instance.id - for node_instance in filtered_nodes) + filtered_node_ids = set(node_instance.id for node_instance in filtered_nodes) for node in ctx.nodes: if node.id not in filtered_node_ids: subgraphs[node.id] = ctx.task_graph( http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/utils.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/builtin/utils.py b/aria/orchestrator/workflows/builtin/utils.py index 045d47b..84d8293 100644 --- a/aria/orchestrator/workflows/builtin/utils.py +++ b/aria/orchestrator/workflows/builtin/utils.py @@ -17,7 +17,7 @@ from ..api.task import OperationTask from .. import exceptions -def create_node_task(interface_name, operation_name, node, dry=False): +def create_node_task(interface_name, operation_name, node): """ Returns a new operation task if the operation exists in the node, otherwise returns None. """ @@ -25,14 +25,12 @@ def create_node_task(interface_name, operation_name, node, dry=False): try: return OperationTask.for_node(node=node, interface_name=interface_name, - operation_name=operation_name, - dry=dry) + operation_name=operation_name) except exceptions.TaskException: - pass - return None + return None -def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry=False): +def create_relationship_tasks(interface_name, operation_name, runs_on, node): """ Returns a list of operation tasks for each outbound relationship of the node if the operation exists there. @@ -45,8 +43,7 @@ def create_relationship_tasks(interface_name, operation_name, runs_on, node, dry OperationTask.for_relationship(relationship=relationship, interface_name=interface_name, operation_name=operation_name, - runs_on=runs_on, - dry=dry)) + runs_on=runs_on)) except exceptions.TaskException: pass return sequence http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/builtin/workflows.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py index f19c031..6065343 100644 --- a/aria/orchestrator/workflows/builtin/workflows.py +++ b/aria/orchestrator/workflows/builtin/workflows.py @@ -67,128 +67,108 @@ __all__ = ( @workflow(suffix_template='{node.name}') def install_node(graph, node, **kwargs): - dry = kwargs.get('dry', True) - sequence = [] # Create sequence.append( create_node_task( NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CREATE, - node, - dry)) + node)) # Configure sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_SOURCE, Task.RUNS_ON_SOURCE, - node, - dry) + node) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_TARGET, Task.RUNS_ON_TARGET, - node, - dry) + node) sequence.append( create_node_task( NORMATIVE_STANDARD_INTERFACE, NORMATIVE_CONFIGURE, - node, - dry)) + node)) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_SOURCE, Task.RUNS_ON_SOURCE, - node, - dry) + node) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_TARGET, Task.RUNS_ON_TARGET, - node, - dry) + node) # Start - sequence += _create_start_tasks(node, dry) + sequence += _create_start_tasks(node) graph.sequence(*sequence) @workflow(suffix_template='{node.name}') def uninstall_node(graph, node, **kwargs): - dry = kwargs.get('dry', True) - # Stop - sequence = _create_stop_tasks(node, dry) + sequence = _create_stop_tasks(node) # Delete sequence.append( create_node_task( NORMATIVE_STANDARD_INTERFACE, NORMATIVE_DELETE, - node, - dry)) + node)) graph.sequence(*sequence) @workflow(suffix_template='{node.name}') def start_node(graph, node, **kwargs): - dry = kwargs.get('dry', True) - graph.sequence(*_create_start_tasks(node, dry)) + graph.sequence(*_create_start_tasks(node)) @workflow(suffix_template='{node.name}') def stop_node(graph, node, **kwargs): - dry = kwargs.get('dry', True) - graph.sequence(*_create_stop_tasks(node, dry)) + graph.sequence(*_create_stop_tasks(node)) -def _create_start_tasks(node, dry): +def _create_start_tasks(node): sequence = [] sequence.append( create_node_task( NORMATIVE_STANDARD_INTERFACE, NORMATIVE_START, - node, - dry)) + node)) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_SOURCE, Task.RUNS_ON_SOURCE, - node, - dry) + node) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_TARGET, Task.RUNS_ON_TARGET, - node, - dry) + node) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED, Task.RUNS_ON_TARGET, - node, - dry) + node) return sequence -def _create_stop_tasks(node, dry): +def _create_stop_tasks(node): sequence = [] sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_REMOVE_TARGET, Task.RUNS_ON_TARGET, - node, - dry) + node) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED, Task.RUNS_ON_TARGET, - node, - dry) + node) sequence.append( create_node_task( NORMATIVE_STANDARD_INTERFACE, NORMATIVE_STOP, - node, - dry)) + node)) return sequence http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/core/task.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py index 7d8380c..64f2818 100644 --- a/aria/orchestrator/workflows/core/task.py +++ b/aria/orchestrator/workflows/core/task.py @@ -113,15 +113,15 @@ class OperationTask(BaseTask): base_task_model = model_storage.task.model_cls if isinstance(api_task.actor, models.Node): context_cls = operation_context.NodeOperationContext - task_model_cls = base_task_model.as_node_task + create_task_model = base_task_model.for_node elif isinstance(api_task.actor, models.Relationship): context_cls = operation_context.RelationshipOperationContext - task_model_cls = base_task_model.as_relationship_task + create_task_model = base_task_model.for_relationship else: raise RuntimeError('No operation context could be created for {actor.model_cls}' .format(actor=api_task.actor)) - operation_task = task_model_cls( + task_model = create_task_model( name=api_task.name, implementation=api_task.implementation, instance=api_task.actor, @@ -131,20 +131,19 @@ class OperationTask(BaseTask): retry_interval=api_task.retry_interval, ignore_failure=api_task.ignore_failure, plugin=plugin, - plugin_name=plugin.name if plugin is not None else 'execution', execution=self._workflow_context.execution, runs_on=api_task.runs_on ) - self._workflow_context.model.task.put(operation_task) + self._workflow_context.model.task.put(task_model) self._ctx = context_cls(name=api_task.name, model_storage=self._workflow_context.model, resource_storage=self._workflow_context.resource, service_id=self._workflow_context._service_id, - task_id=operation_task.id, + task_id=task_model.id, actor_id=api_task.actor.id, workdir=self._workflow_context._workdir) - self._task_id = operation_task.id + self._task_id = task_model.id self._update_fields = None @contextmanager http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/aria/orchestrator/workflows/dry.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/dry.py b/aria/orchestrator/workflows/dry.py deleted file mode 100644 index 766ea0c..0000000 --- a/aria/orchestrator/workflows/dry.py +++ /dev/null @@ -1,53 +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 threading import RLock - -from ..decorators import operation -from ...utils.collections import OrderedDict -from ...utils.console import puts, Colored -from ...utils.formatting import safe_repr - - -_TERMINAL_LOCK = RLock() - - -def convert_to_dry(plugin, implementation, inputs): # pylint: disable=unused-argument - dry_implementation = '{0}.{1}'.format(__name__, 'dry_operation') - dry_inputs = OrderedDict() - dry_inputs['_implementation'] = implementation - dry_inputs['_plugin'] = plugin.name if plugin is not None else None - return None, dry_implementation, dry_inputs - - -@operation -def dry_operation(ctx, _plugin, _implementation, **kwargs): - with _TERMINAL_LOCK: - print ctx.name - if hasattr(ctx, 'relationship'): - puts('> Relationship: {0} -> {1}'.format( - Colored.red(ctx.relationship.source_node.name), - Colored.red(ctx.relationship.target_node.name))) - else: - puts('> Node: {0}'.format(Colored.red(ctx.node.name))) - puts(' Operation: {0}'.format(Colored.green(ctx.name))) - _dump_implementation(_plugin, _implementation) - - -def _dump_implementation(plugin, implementation): - if plugin: - puts(' Plugin: {0}'.format(Colored.magenta(plugin, bold=True))) - if implementation: - puts(' Implementation: {0}'.format(Colored.magenta(safe_repr(implementation)))) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py ---------------------------------------------------------------------- diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py index 0206e03..a7b2a11 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -27,7 +27,7 @@ from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate, RequirementTemplate, RelationshipTemplate, CapabilityTemplate, GroupTemplate, PolicyTemplate, SubstitutionTemplate, SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate, - ArtifactTemplate, Metadata, Parameter, Plugin) + ArtifactTemplate, Metadata, Parameter, PluginSpecification) from ..data_types import coerce_value @@ -81,12 +81,13 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to create_parameter_models_from_values(model.outputs, topology_template._get_output_values(context)) - # Plugins + # Plugin specifications policies = context.presentation.get('service_template', 'topology_template', 'policies') if policies: for policy in policies.itervalues(): if model.policy_types.get_descendant(policy.type).role == 'plugin': - model.plugins.append(create_plugin_model(context, policy)) + model.plugin_specifications.append( + create_plugin_specification_model(context, policy)) # Node templates node_templates = context.presentation.get('service_template', 'topology_template', @@ -349,7 +350,7 @@ def create_operation_template_model(context, service_template, operation): # pyl implementation = operation.implementation if (implementation is not None) and operation.implementation.primary: - model.plugin, model.implementation = \ + model.plugin_specification, model.implementation = \ parse_implementation_string(context, service_template, operation.implementation.primary) dependencies = implementation.dependencies @@ -427,7 +428,7 @@ def create_substitution_template_model(context, service_template, substitution_m return model -def create_plugin_model(context, policy): +def create_plugin_specification_model(context, policy): properties = policy.properties def get(name): @@ -436,18 +437,16 @@ def create_plugin_model(context, policy): now = datetime.now() - model = Plugin(name=policy._name, - archive_name=get('archive_name') or '', - distribution=get('distribution'), - distribution_release=get('distribution_release'), - distribution_version=get('distribution_version'), - package_name=get('package_name') or '', - package_source=get('package_source'), - package_version=get('package_version'), - supported_platform=get('supported_platform'), - supported_py_versions=get('supported_py_versions'), - uploaded_at=now, - wheels=get('wheels') or []) + model = PluginSpecification(name=policy._name, + archive_name=get('archive_name') or '', + distribution=get('distribution'), + distribution_release=get('distribution_release'), + distribution_version=get('distribution_version'), + package_name=get('package_name') or '', + package_source=get('package_source'), + package_version=get('package_version'), + supported_platform=get('supported_platform'), + supported_py_versions=get('supported_py_versions')) return model @@ -665,15 +664,15 @@ def parse_implementation_string(context, service_template, implementation): plugin_name = implementation[:index].strip() if plugin_name == 'execution': - plugin = None + plugin_specification = None else: - plugin = None - for the_plugin in service_template.plugins: - if the_plugin.name == plugin_name: - plugin = the_plugin + plugin_specification = None + for a_plugin_specification in service_template.plugin_specifications: + if a_plugin_specification.name == plugin_name: + plugin_specification = a_plugin_specification break - if plugin is None: + if plugin_specification is None: raise ValueError('unknown plugin: "{0}"'.format(plugin_name)) implementation = implementation[index+1:].strip() - return plugin, implementation + return plugin_specification, implementation http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/end2end/test_orchestrator.py ---------------------------------------------------------------------- diff --git a/tests/end2end/test_orchestrator.py b/tests/end2end/test_orchestrator.py index fac6207..f25134f 100644 --- a/tests/end2end/test_orchestrator.py +++ b/tests/end2end/test_orchestrator.py @@ -19,6 +19,7 @@ from aria.orchestrator.runner import Runner from aria.orchestrator.workflows.builtin import BUILTIN_WORKFLOWS from aria.utils.imports import import_fullname from aria.utils.collections import OrderedDict +from aria.cli.dry import convert_to_dry from tests.parser.service_templates import consume_node_cellar @@ -37,6 +38,8 @@ def test_custom(): def _workflow(workflow_name): context, _ = consume_node_cellar() + convert_to_dry(context.modeling.instance) + # TODO: this logic will eventually stabilize and be part of the ARIA API, # likely somewhere in aria.orchestrator.workflows if workflow_name in BUILTIN_WORKFLOWS: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/mock/models.py ---------------------------------------------------------------------- diff --git a/tests/mock/models.py b/tests/mock/models.py index 716254e..483da7d 100644 --- a/tests/mock/models.py +++ b/tests/mock/models.py @@ -191,6 +191,7 @@ def create_execution(service): def create_plugin(package_name='package', package_version='0.1'): return models.Plugin( + name='test_plugin', archive_name='archive_name', distribution='distribution', distribution_release='dist_release', @@ -205,5 +206,20 @@ def create_plugin(package_name='package', package_version='0.1'): ) +def create_plugin_specification(package_name='package', package_version='0.1'): + return models.PluginSpecification( + name='test_plugin', + archive_name='archive_name', + distribution='distribution', + distribution_release='dist_release', + distribution_version='dist_version', + package_name=package_name, + package_source='source', + package_version=package_version, + supported_platform='any', + supported_py_versions=['python27'] + ) + + def _dictify(item): return dict(((item.name, item),)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/__init__.py ---------------------------------------------------------------------- diff --git a/tests/modeling/__init__.py b/tests/modeling/__init__.py new file mode 100644 index 0000000..072ef54 --- /dev/null +++ b/tests/modeling/__init__.py @@ -0,0 +1,34 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from sqlalchemy import ( + Column, + Text, + Integer, +) + +from aria.modeling import ( + models, + types as modeling_types, + mixins +) + + +class MockModel(models.aria_declarative_base, mixins.ModelMixin): #pylint: disable=abstract-method + __tablename__ = 'mock_model' + model_dict = Column(modeling_types.Dict) + model_list = Column(modeling_types.List) + value = Column(Integer) + name = Column(Text) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_mixins.py ---------------------------------------------------------------------- diff --git a/tests/modeling/test_mixins.py b/tests/modeling/test_mixins.py new file mode 100644 index 0000000..a60412f --- /dev/null +++ b/tests/modeling/test_mixins.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. + +import pytest + +import sqlalchemy + +from aria.storage import ( + ModelStorage, + sql_mapi, + exceptions +) +from aria import modeling + +from ..storage import ( + release_sqlite_storage, + init_inmemory_model_storage +) +from . import MockModel +from ..mock import ( + models, + context as mock_context +) + + [email protected] +def storage(): + base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, + initiator=init_inmemory_model_storage) + base_storage.register(MockModel) + yield base_storage + release_sqlite_storage(base_storage) + + [email protected](scope='module', autouse=True) +def module_cleanup(): + modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) # pylint: disable=no-member + + [email protected] +def context(tmpdir): + ctx = mock_context.simple(str(tmpdir)) + yield ctx + release_sqlite_storage(ctx.model) + + +def test_inner_dict_update(storage): + inner_dict = {'inner_value': 1} + + mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0}) + storage.mock_model.put(mock_model) + + storage_mm = storage.mock_model.get(mock_model.id) + assert storage_mm == mock_model + + storage_mm.model_dict['inner_dict']['inner_value'] = 2 + storage_mm.model_dict['value'] = -1 + storage.mock_model.update(storage_mm) + storage_mm = storage.mock_model.get(storage_mm.id) + + assert storage_mm.model_dict['inner_dict']['inner_value'] == 2 + assert storage_mm.model_dict['value'] == -1 + + +def test_inner_list_update(storage): + mock_model = MockModel(model_list=[0, [1]]) + storage.mock_model.put(mock_model) + + storage_mm = storage.mock_model.get(mock_model.id) + assert storage_mm == mock_model + + storage_mm.model_list[1][0] = 'new_inner_value' + storage_mm.model_list[0] = 'new_value' + storage.mock_model.update(storage_mm) + storage_mm = storage.mock_model.get(storage_mm.id) + + assert storage_mm.model_list[1][0] == 'new_inner_value' + assert storage_mm.model_list[0] == 'new_value' + + +def test_model_to_dict(context): + service = context.service + service = service.to_dict() + + expected_keys = [ + 'description', + 'created_at', + 'permalink', + 'scaling_groups', + 'updated_at' + ] + + for expected_key in expected_keys: + assert expected_key in service + + +def test_relationship_model_ordering(context): + service = context.model.service.get_by_name(models.SERVICE_NAME) + source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME) + target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME) + + new_node_template = modeling.models.NodeTemplate( + name='new_node_template', + type=source_node.type, + default_instances=1, + min_instances=1, + max_instances=1, + service_template=service.service_template + ) + + new_node = modeling.models.Node( + name='new_node', + type=source_node.type, + runtime_properties={}, + service=service, + version=None, + node_template=new_node_template, + state='', + scaling_groups=[] + ) + + source_node.outbound_relationships.append(modeling.models.Relationship( + source_node=source_node, + target_node=new_node, + )) + + new_node.outbound_relationships.append(modeling.models.Relationship( # pylint: disable=no-member + source_node=new_node, + target_node=target_node, + )) + + context.model.node_template.put(new_node_template) + context.model.node.put(new_node) + context.model.node.refresh(source_node) + context.model.node.refresh(target_node) + + def flip_and_assert(node, direction): + """ + Reversed the order of relationships and assert effects took place. + :param node: the node instance to operate on + :param direction: the type of relationships to flip (inbound/outbound) + :return: + """ + assert direction in ('inbound', 'outbound') + + relationships = getattr(node, direction + '_relationships') + assert len(relationships) == 2 + + reversed_relationship = list(reversed(relationships)) + assert relationships != reversed_relationship + + relationships[:] = reversed_relationship + context.model.node.update(node) + assert relationships == reversed_relationship + + flip_and_assert(source_node, 'outbound') + flip_and_assert(target_node, 'inbound') + + +class StrictClass(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin): + __tablename__ = 'strict_class' + + strict_dict = sqlalchemy.Column(modeling.types.StrictDict(basestring, basestring)) + strict_list = sqlalchemy.Column(modeling.types.StrictList(basestring)) + + +def test_strict_dict(): + + strict_class = StrictClass() + + def assert_strict(sc): + with pytest.raises(exceptions.StorageError): + sc.strict_dict = {'key': 1} + + with pytest.raises(exceptions.StorageError): + sc.strict_dict = {1: 'value'} + + with pytest.raises(exceptions.StorageError): + sc.strict_dict = {1: 1} + + assert_strict(strict_class) + strict_class.strict_dict = {'key': 'value'} + assert strict_class.strict_dict == {'key': 'value'} + + assert_strict(strict_class) + with pytest.raises(exceptions.StorageError): + strict_class.strict_dict['key'] = 1 + with pytest.raises(exceptions.StorageError): + strict_class.strict_dict[1] = 'value' + with pytest.raises(exceptions.StorageError): + strict_class.strict_dict[1] = 1 + + +def test_strict_list(): + strict_class = StrictClass() + + def assert_strict(sc): + with pytest.raises(exceptions.StorageError): + sc.strict_list = [1] + + assert_strict(strict_class) + strict_class.strict_list = ['item'] + assert strict_class.strict_list == ['item'] + + assert_strict(strict_class) + with pytest.raises(exceptions.StorageError): + strict_class.strict_list[0] = 1 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/dd5bfa93/tests/modeling/test_model_storage.py ---------------------------------------------------------------------- diff --git a/tests/modeling/test_model_storage.py b/tests/modeling/test_model_storage.py new file mode 100644 index 0000000..bb778d4 --- /dev/null +++ b/tests/modeling/test_model_storage.py @@ -0,0 +1,102 @@ +# 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 pytest + +from aria.storage import ( + ModelStorage, + exceptions, + sql_mapi +) +from aria import (application_model_storage, modeling) +from ..storage import (release_sqlite_storage, init_inmemory_model_storage) + +from . import MockModel + + [email protected] +def storage(): + base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, + initiator=init_inmemory_model_storage) + base_storage.register(MockModel) + yield base_storage + release_sqlite_storage(base_storage) + + [email protected](scope='module', autouse=True) +def module_cleanup(): + modeling.models.aria_declarative_base.metadata.remove(MockModel.__table__) #pylint: disable=no-member + + +def test_storage_base(storage): + with pytest.raises(AttributeError): + storage.non_existent_attribute() + + +def test_model_storage(storage): + mock_model = MockModel(value=0, name='model_name') + storage.mock_model.put(mock_model) + + assert storage.mock_model.get_by_name('model_name') == mock_model + + assert [mm_from_storage for mm_from_storage in storage.mock_model.iter()] == [mock_model] + assert [mm_from_storage for mm_from_storage in storage.mock_model] == [mock_model] + + storage.mock_model.delete(mock_model) + with pytest.raises(exceptions.StorageError): + storage.mock_model.get(mock_model.id) + + +def test_application_storage_factory(): + storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI, + initiator=init_inmemory_model_storage) + + assert storage.service_template + assert storage.node_template + assert storage.group_template + assert storage.policy_template + assert storage.substitution_template + assert storage.substitution_template_mapping + assert storage.requirement_template + assert storage.relationship_template + assert storage.capability_template + assert storage.interface_template + assert storage.operation_template + assert storage.artifact_template + + assert storage.service + assert storage.node + assert storage.group + assert storage.policy + assert storage.substitution + assert storage.substitution_mapping + assert storage.relationship + assert storage.capability + assert storage.interface + assert storage.operation + assert storage.artifact + + assert storage.execution + assert storage.service_update + assert storage.service_update_step + assert storage.service_modification + assert storage.plugin + assert storage.task + + assert storage.parameter + assert storage.type + assert storage.metadata + + release_sqlite_storage(storage)
