Move types into service template; continue fixing tests
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/36eb2d24 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/36eb2d24 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/36eb2d24 Branch: refs/heads/ARIA-105-integrate-modeling Commit: 36eb2d24c2d0791738e6b9f88a81fc5f906745ce Parents: 091152f Author: Tal Liron <[email protected]> Authored: Fri Mar 3 17:42:53 2017 -0600 Committer: Tal Liron <[email protected]> Committed: Fri Mar 3 17:42:53 2017 -0600 ---------------------------------------------------------------------- aria/cli/commands.py | 33 +++-- aria/modeling/bases.py | 5 +- aria/modeling/misc.py | 68 ++++++--- aria/modeling/service.py | 22 +-- aria/modeling/service_template.py | 122 +++++++++++++-- aria/modeling/utils.py | 83 ++++------- aria/orchestrator/context/common.py | 9 +- aria/orchestrator/decorators.py | 6 +- aria/orchestrator/workflows/api/task.py | 29 ++-- aria/orchestrator/workflows/api/task_graph.py | 4 +- .../orchestrator/workflows/builtin/workflows.py | 21 +-- aria/orchestrator/workflows/core/task.py | 13 +- aria/orchestrator/workflows/dry.py | 10 +- aria/parser/modeling/context.py | 69 +-------- aria/parser/modeling/types.py | 146 ------------------ aria/parser/modeling/utils.py | 146 ------------------ aria/utils/formatting.py | 19 ++- aria/utils/uuid.py | 66 +++++++++ .../simple_v1_0/modeling/__init__.py | 113 ++++++++------ tests/end2end/test_orchestrator.py | 18 ++- tests/mock/models.py | 147 ++++++++++--------- tests/mock/topology.py | 42 +++--- .../context/test_resource_render.py | 4 +- tests/orchestrator/context/test_serialize.py | 2 +- tests/orchestrator/context/test_workflow.py | 12 +- .../execution_plugin/test_common.py | 4 +- tests/orchestrator/execution_plugin/test_ssh.py | 6 +- tests/orchestrator/test_runner.py | 4 +- tests/orchestrator/workflows/api/test_task.py | 20 +-- .../orchestrator/workflows/core/test_engine.py | 14 +- .../workflows/executor/test_executor.py | 6 +- .../workflows/executor/test_process_executor.py | 10 +- .../tosca-simple-1.0/node-cellar/workflows.py | 18 ++- tests/storage/test_models.py | 32 ++-- tests/storage/test_structures.py | 2 +- 35 files changed, 596 insertions(+), 729 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/cli/commands.py ---------------------------------------------------------------------- diff --git a/aria/cli/commands.py b/aria/cli/commands.py index 25a4e0d..52d4f14 100644 --- a/aria/cli/commands.py +++ b/aria/cli/commands.py @@ -228,30 +228,35 @@ class WorkflowCommand(BaseCommand): def _get_workflow(self, context, workflow_name): if workflow_name in BUILTIN_WORKFLOWS: - workflow_fn = import_fullname('aria.orchestrator.workflows.builtin.%s' % workflow_name) + workflow_fn = import_fullname('aria.orchestrator.workflows.builtin.{0}'.format( + workflow_name)) inputs = {} else: + workflow = None + for policy in context.modeling.instance.policies: + if policy.name == workflow_name: + workflow = policy + break + + if workflow is None: + raise AttributeError('workflow policy does not exist: "{0}"'.format(workflow_name)) + if workflow.type.role != 'workflow': + raise AttributeError('policy is not a workflow: "{0}"'.format(workflow_name)) + try: - policy = context.modeling.instance.policies[workflow_name] - except KeyError: - raise AttributeError('workflow policy does not exist: "%s"' % workflow_name) - if context.modeling.policy_types.get_role(policy.type_name) != 'workflow': - raise AttributeError('policy is not a workflow: "%s"' % workflow_name) - - try: - sys.path.append(policy.properties['implementation'].value) + sys.path.append(workflow.properties['implementation'].value) except KeyError: pass - workflow_fn = import_fullname(policy.properties['function'].value) + workflow_fn = import_fullname(workflow.properties['function'].value) - for k in policy.properties: + for k in workflow.properties: if k in WORKFLOW_DECORATOR_RESERVED_ARGUMENTS: - raise AttributeError('workflow policy "%s" defines a reserved property: "%s"' % - (workflow_name, k)) + raise AttributeError('workflow policy "{0}" defines a reserved property: "{1}"' + .format(workflow_name, k)) inputs = OrderedDict([ - (k, v.value) for k, v in policy.properties.iteritems() + (k, v.value) for k, v in workflow.properties.iteritems() if k not in WorkflowCommand.WORKFLOW_POLICY_INTERNAL_PROPERTIES ]) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/modeling/bases.py ---------------------------------------------------------------------- diff --git a/aria/modeling/bases.py b/aria/modeling/bases.py index c1b2f7a..7fe218d 100644 --- a/aria/modeling/bases.py +++ b/aria/modeling/bases.py @@ -36,6 +36,7 @@ from sqlalchemy import ( ) from . import utils +from ..utils import formatting class ModelMixin(object): @@ -153,7 +154,7 @@ class ModelMixin(object): """ if backreference is None: - backreference = utils.pluralize(cls.__tablename__) + backreference = formatting.pluralize(cls.__tablename__) backref_kwargs = backref_kwargs or {} backref_kwargs.setdefault('uselist', True) @@ -360,7 +361,7 @@ class ModelMixin(object): class ModelIDMixin(object): id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(Text, nullable=True, index=True) + name = Column(Text, index=True) @classmethod def id_column_name(cls): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/modeling/misc.py ---------------------------------------------------------------------- diff --git a/aria/modeling/misc.py b/aria/modeling/misc.py index 0b50f9a..b61a787 100644 --- a/aria/modeling/misc.py +++ b/aria/modeling/misc.py @@ -14,6 +14,7 @@ # limitations under the License. import cPickle as pickle +import logging from sqlalchemy import ( Column, @@ -23,9 +24,9 @@ from sqlalchemy import ( from sqlalchemy.ext.declarative import declared_attr from ..storage import exceptions -from ..parser.modeling import utils as parser_utils from ..utils import collections, formatting, console from .bases import InstanceModelMixin, TemplateModelMixin +from . import utils class ParameterBase(TemplateModelMixin): @@ -42,11 +43,11 @@ class ParameterBase(TemplateModelMixin): __tablename__ = 'parameter' - name = Column(Text, nullable=False) - type_name = Column(Text, nullable=False) + name = Column(Text) + type_name = Column(Text) # Check: value type - _value = Column(Binary, nullable=True) + _value = Column(Binary, name='value') description = Column(Text) @property @@ -64,7 +65,7 @@ class ParameterBase(TemplateModelMixin): try: return pickle.loads(self._value) except BaseException: - raise exceptions.StorageError('Bad format for parameter of type "{0}": {1}'.format( + raise exceptions.StorageError('bad format for parameter of type "{0}": {1}'.format( self.type_name, self._value)) @value.setter @@ -74,8 +75,9 @@ class ParameterBase(TemplateModelMixin): else: try: self._value = pickle.dumps(value) - except pickle.PicklingError: - # TODO debug log + except (pickle.PicklingError, TypeError): + logging.getLogger('aria').warn('Could not pickle parameter of type "{0}": {1}' + .format(self.type_name, value)) self._value = pickle.dumps(str(value)) def instantiate(self, context, container): @@ -87,8 +89,21 @@ class ParameterBase(TemplateModelMixin): def coerce_values(self, context, container, report_issues): if self.value is not None: - self.value = parser_utils.coerce_value(context, container, self.value, - report_issues) + self.value = utils.coerce_value(context, container, self.value, + report_issues) + + def dump(self, context): + if self.type_name is not None: + console.puts('{0}: {1} ({2})'.format( + context.style.property(self.name), + context.style.literal(self.value), + context.style.type(self.type_name))) + else: + console.puts('{0}: {1}'.format( + context.style.property(self.name), + context.style.literal(self.value))) + if self.description: + console.puts(context.style.meta(self.description)) class TypeBase(InstanceModelMixin): @@ -98,9 +113,9 @@ class TypeBase(InstanceModelMixin): __tablename__ = 'type' - variant = Column(Text) + variant = Column(Text, nullable=False) description = Column(Text) - role = Column(Text) + _role = Column(Text, name='role') @declared_attr def parent(cls): @@ -120,6 +135,21 @@ class TypeBase(InstanceModelMixin): return cls.foreign_key('type', nullable=True) # endregion + + @property + def role(self): + def get_role(the_type): + if the_type is None: + return None + elif the_type._role is None: + return get_role(the_type.parent) + return the_type._role + + return get_role(self) + + @role.setter + def role(self, value): + self._role = value def is_descendant(self, base_name, name): base = self.get_descendant(base_name) @@ -143,16 +173,6 @@ class TypeBase(InstanceModelMixin): for descendant in child.iter_descendants(): yield descendant - def get_role(self, name): - def _get_role(the_type): - if the_type is None: - return None - elif the_type.role is None: - return _get_role(self.parent) - return the_type.role - - return _get_role(self.get_descendant(name)) - @property def as_raw(self): return collections.OrderedDict(( @@ -193,7 +213,6 @@ class MetadataBase(TemplateModelMixin): __tablename__ = 'metadata' - name = Column(Text, nullable=False) value = Column(Text) @property @@ -206,3 +225,8 @@ class MetadataBase(TemplateModelMixin): from . import models return models.Metadata(name=self.name, value=self.value) + + def dump(self, context): + console.puts('{0}: {1}'.format( + context.style.property(self.name), + context.style.literal(self.value))) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/modeling/service.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service.py b/aria/modeling/service.py index 1309472..e555b70 100644 --- a/aria/modeling/service.py +++ b/aria/modeling/service.py @@ -216,7 +216,7 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods def dump(self, context): if self.description is not None: console.puts(context.style.meta(self.description)) - utils.dump_parameters(context, self.meta_data, 'Metadata') + utils.dump_dict_values(context, self.meta_data, 'Metadata') for node in self.nodes: node.dump(context) for group in self.groups: @@ -225,8 +225,8 @@ class ServiceBase(InstanceModelMixin): # pylint: disable=too-many-public-methods policy.dump(context) if self.substitution is not None: self.substitution.dump(context) - utils.dump_parameters(context, self.inputs, 'Inputs') - utils.dump_parameters(context, self.outputs, 'Outputs') + utils.dump_dict_values(context, self.inputs, 'Inputs') + utils.dump_dict_values(context, self.outputs, 'Outputs') utils.dump_dict_values(context, self.operations, 'Operations') def dump_graph(self, context): @@ -493,7 +493,7 @@ class NodeBase(InstanceModelMixin): with context.style.indent: console.puts('Type: {0}'.format(context.style.type(self.type.name))) console.puts('Template: {0}'.format(context.style.node(self.node_template.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interfaces) utils.dump_dict_values(context, self.artifacts, 'Artifacts') utils.dump_dict_values(context, self.capabilities, 'Capabilities') @@ -574,7 +574,7 @@ class GroupBase(InstanceModelMixin): console.puts('Group: {0}'.format(context.style.node(self.name))) with context.style.indent: console.puts('Type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interfaces) if self.nodes: console.puts('Member nodes:') @@ -654,7 +654,7 @@ class PolicyBase(InstanceModelMixin): console.puts('Policy: {0}'.format(context.style.node(self.name))) with context.style.indent: console.puts('Type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') if self.nodes: console.puts('Target nodes:') with context.style.indent: @@ -914,7 +914,7 @@ class RelationshipBase(InstanceModelMixin): if (self.relationship_template is not None) and self.relationship_template.name: console.puts('Relationship template: {0}'.format( context.style.node(self.relationship_template.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interfaces, 'Interfaces') @@ -1008,7 +1008,7 @@ class CapabilityBase(InstanceModelMixin): ' to {0:d}'.format(self.max_occurrences) if self.max_occurrences is not None else ' or more')) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') class InterfaceBase(InstanceModelMixin): @@ -1102,7 +1102,7 @@ class InterfaceBase(InstanceModelMixin): console.puts(context.style.meta(self.description)) with context.style.indent: console.puts('Interface type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.inputs, 'Inputs') + utils.dump_dict_values(context, self.inputs, 'Inputs') utils.dump_dict_values(context, self.operations, 'Operations') @@ -1211,7 +1211,7 @@ class OperationBase(InstanceModelMixin): if self.retry_interval is not None: console.puts('Retry interval: {0}'.format( context.style.literal(self.retry_interval))) - utils.dump_parameters(context, self.inputs, 'Inputs') + utils.dump_dict_values(context, self.inputs, 'Inputs') class ArtifactBase(InstanceModelMixin): @@ -1307,4 +1307,4 @@ class ArtifactBase(InstanceModelMixin): if self.repository_credential: console.puts('Repository credential: {0}'.format( context.style.literal(self.repository_credential))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/modeling/service_template.py ---------------------------------------------------------------------- diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py index b4c15bb..1f2d1f3 100644 --- a/aria/modeling/service_template.py +++ b/aria/modeling/service_template.py @@ -97,6 +97,34 @@ class ServiceTemplateBase(TemplateModelMixin): def operation_templates(cls): return cls.one_to_many_relationship('operation_template', dict_key='name') + @declared_attr + def node_types(cls): + return cls.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='') + + @declared_attr + def capability_types(cls): + return cls.one_to_one_relationship('type', key='capability_type_fk', backreference='') + + @declared_attr + def relationship_types(cls): + return cls.one_to_one_relationship('type', key='relationship_type_fk', backreference='') + + @declared_attr + def policy_types(cls): + return cls.one_to_one_relationship('type', key='policy_type_fk', backreference='') + + @declared_attr + def artifact_types(cls): + return cls.one_to_one_relationship('type', key='artifact_type_fk', backreference='') + + @declared_attr + def interface_types(cls): + return cls.one_to_one_relationship('type', key='interface_type_fk', backreference='') + # region orchestration created_at = Column(DateTime, nullable=False, index=True) @@ -118,6 +146,41 @@ class ServiceTemplateBase(TemplateModelMixin): def substitution_template_fk(cls): return cls.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) + + # ServiceTemplate one-to-one to Type + @declared_attr + def group_type_fk(cls): + return cls.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) + + # ServiceTemplate one-to-one to Type + @declared_attr + def relationship_type_fk(cls): + return cls.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) + + # ServiceTemplate one-to-one to Type + @declared_attr + def artifact_type_fk(cls): + return cls.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) + # endregion def get_node_template(self, node_template_name): @@ -147,6 +210,17 @@ class ServiceTemplateBase(TemplateModelMixin): ('outputs', formatting.as_raw_dict(self.outputs)), ('operation_templates', formatting.as_raw_list(self.operation_templates)))) + @property + def types_as_raw(self): + return collections.OrderedDict(( + ('node_types', formatting.as_raw(self.node_types)), + ('group_types', formatting.as_raw(self.group_types)), + ('capability_types', formatting.as_raw(self.capability_types)), + ('relationship_types', formatting.as_raw(self.relationship_types)), + ('policy_types', formatting.as_raw(self.policy_types)), + ('artifact_types', formatting.as_raw(self.artifact_types)), + ('interface_types', formatting.as_raw(self.interface_types)))) + def instantiate(self, context, container): from . import models now = datetime.now() @@ -208,7 +282,7 @@ class ServiceTemplateBase(TemplateModelMixin): def dump(self, context): if self.description is not None: console.puts(context.style.meta(self.description)) - utils.dump_parameters(context, self.meta_data, 'Metadata') + utils.dump_dict_values(context, self.meta_data, 'Metadata') for node_template in self.node_templates: node_template.dump(context) @@ -218,10 +292,36 @@ class ServiceTemplateBase(TemplateModelMixin): policy_template.dump(context) if self.substitution_template is not None: self.substitution_template.dump(context) - utils.dump_parameters(context, self.inputs, 'Inputs') - utils.dump_parameters(context, self.outputs, 'Outputs') + 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') + def dump_types(self, context): + if self.node_types.children: + console.puts('Node types:') + self.node_types.dump(context) + if self.group_types.children: + console.puts('Group types:') + self.group_types.dump(context) + if self.capability_types.children: + console.puts('Capability types:') + self.capability_types.dump(context) + if self.relationship_types.children: + console.puts('Relationship types:') + self.relationship_types.dump(context) + if self.policy_types.children: + console.puts('Policy types:') + self.policy_types.dump(context) + if self.policy_trigger_types.children: + console.puts('Policy trigger types:') + self.policy_trigger_types.dump(context) + if self.artifact_types.children: + console.puts('Artifact types:') + self.artifact_types.dump(context) + if self.interface_types.children: + console.puts('Interface types:') + self.interface_types.dump(context) + class NodeTemplateBase(TemplateModelMixin): """ @@ -360,7 +460,7 @@ class NodeTemplateBase(TemplateModelMixin): ' to {0:d}'.format(self.max_instances) if self.max_instances is not None else ' or more')) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interface_templates) utils.dump_dict_values(context, self.artifact_templates, 'Artifact templates') utils.dump_dict_values(context, self.capability_templates, 'Capability templates') @@ -453,7 +553,7 @@ class GroupTemplateBase(TemplateModelMixin): console.puts(context.style.meta(self.description)) with context.style.indent: console.puts('Type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interface_templates) if self.node_templates: console.puts('Member node templates: {0}'.format(', '.join( @@ -542,7 +642,7 @@ class PolicyTemplateBase(TemplateModelMixin): console.puts(context.style.meta(self.description)) with context.style.indent: console.puts('Type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') if self.node_templates: console.puts('Target node templates: {0}'.format(', '.join( (str(context.style.node(v.name)) for v in self.node_templates)))) @@ -943,7 +1043,7 @@ class RelationshipTemplateBase(TemplateModelMixin): if self.description: console.puts(context.style.meta(self.description)) with context.style.indent: - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') utils.dump_interfaces(context, self.interface_templates, 'Interface templates') @@ -1063,7 +1163,7 @@ class CapabilityTemplateBase(TemplateModelMixin): console.puts('Valid source node types: {0}'.format( ', '.join((str(context.style.type(v.name)) for v in self.valid_source_node_types)))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') class InterfaceTemplateBase(TemplateModelMixin): @@ -1155,7 +1255,7 @@ class InterfaceTemplateBase(TemplateModelMixin): console.puts(context.style.meta(self.description)) with context.style.indent: console.puts('Interface type: {0}'.format(context.style.type(self.type.name))) - utils.dump_parameters(context, self.inputs, 'Inputs') + utils.dump_dict_values(context, self.inputs, 'Inputs') utils.dump_dict_values(context, self.operation_templates, 'Operation templates') @@ -1268,7 +1368,7 @@ class OperationTemplateBase(TemplateModelMixin): if self.retry_interval is not None: console.puts('Retry interval: {0}'.format( context.style.literal(self.retry_interval))) - utils.dump_parameters(context, self.inputs, 'Inputs') + utils.dump_dict_values(context, self.inputs, 'Inputs') class ArtifactTemplateBase(TemplateModelMixin): @@ -1364,7 +1464,7 @@ class ArtifactTemplateBase(TemplateModelMixin): if self.repository_credential: console.puts('Repository credential: {0}'.format( context.style.literal(self.repository_credential))) - utils.dump_parameters(context, self.properties) + utils.dump_dict_values(context, self.properties, 'Properties') def deepcopy_with_locators(value): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/modeling/utils.py ---------------------------------------------------------------------- diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py index ca418a6..a2f0bfc 100644 --- a/aria/modeling/utils.py +++ b/aria/modeling/utils.py @@ -13,37 +13,32 @@ # See the License for the specific language governing permissions and # limitations under the License. -from random import randrange - -from shortuuid import ShortUUID - +from ..parser.exceptions import InvalidValueError +from ..parser.presentation import Value +from ..utils.collections import OrderedDict 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 ShortUUID). - - 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) +from .exceptions import CannotEvaluateFunctionException + + +def coerce_value(context, container, value, report_issues=False): + if isinstance(value, Value): + value = value.value + + if isinstance(value, list): + return [coerce_value(context, container, v, report_issues) for v in value] + elif isinstance(value, dict): + return OrderedDict((k, coerce_value(context, container, v, report_issues)) + for k, v in value.items()) + elif hasattr(value, '_evaluate'): + try: + value = value._evaluate(context, container) + value = coerce_value(context, container, value, report_issues) + except CannotEvaluateFunctionException: + pass + except InvalidValueError as e: + if report_issues: + context.validation.report(e.issue) + return value def validate_dict_values(context, the_dict): @@ -105,25 +100,6 @@ def dump_dict_values(context, the_dict, name): dump_list_values(context, the_dict.itervalues(), name) -def dump_parameters(context, parameters, name='Properties'): - if not parameters: - return - puts('{0}:'.format(name)) - with context.style.indent: - for parameter_name, parameter in parameters.items(): - if hasattr(parameter, 'type_name') and (parameter.type_name is not None): - puts('{0} = {1} ({2})'.format( - context.style.property(parameter_name), - context.style.literal(parameter.value), - context.style.type(parameter.type_name))) - else: - puts('{0} = {1}'.format( - context.style.property(parameter_name), - context.style.literal(parameter.value))) - if hasattr(parameter, 'description') and parameter.description: - puts(context.style.meta(parameter.description)) - - def dump_interfaces(context, interfaces, name='Interfaces'): if not interfaces: return @@ -133,15 +109,6 @@ def dump_interfaces(context, interfaces, name='Interfaces'): 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 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/context/common.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py index f0a3b3e..77e5da7 100644 --- a/aria/orchestrator/context/common.py +++ b/aria/orchestrator/context/common.py @@ -12,15 +12,16 @@ # 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. + """ A common context for both workflow and operation """ -from uuid import uuid4 import jinja2 -from aria import logger -from aria.storage import exceptions +from ... import logger +from ...storage import exceptions +from ...utils.uuid import generate_uuid class BaseContext(logger.LoggerMixin): @@ -38,7 +39,7 @@ class BaseContext(logger.LoggerMixin): **kwargs): super(BaseContext, self).__init__(**kwargs) self._name = name - self._id = str(uuid4()) + self._id = generate_uuid(variant='uuid') self._model = model_storage self._resource = resource_storage self._service_id = service_id http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/decorators.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/decorators.py b/aria/orchestrator/decorators.py index bbe43f4..e90afc7 100644 --- a/aria/orchestrator/decorators.py +++ b/aria/orchestrator/decorators.py @@ -17,10 +17,10 @@ Workflow and operation decorators """ -from uuid import uuid4 from functools import partial, wraps -from aria.utils.validation import validate_function_arguments +from ..utils.validation import validate_function_arguments +from ..utils.uuid import generate_uuid from . import context from .workflows.api import task_graph @@ -74,4 +74,4 @@ def operation(func=None, toolbelt=False, suffix_template=''): def _generate_name(func_name, ctx, suffix_template, **custom_kwargs): return '{func_name}.{suffix}'.format( func_name=func_name, - suffix=suffix_template.format(ctx=ctx, **custom_kwargs) or str(uuid4())) + suffix=suffix_template.format(ctx=ctx, **custom_kwargs) or generate_uuid(variant='uuid')) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/workflows/api/task.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py index 4f35ffc..d90b2b1 100644 --- a/aria/orchestrator/workflows/api/task.py +++ b/aria/orchestrator/workflows/api/task.py @@ -16,10 +16,10 @@ """ Provides the tasks to be entered into the task graph """ -from uuid import uuid4 from ....modeling import models from ....utils.collections import OrderedDict +from ....utils.uuid import generate_uuid from ... import context from .. import exceptions @@ -34,7 +34,7 @@ class BaseTask(object): self._workflow_context = ctx else: self._workflow_context = context.workflow.current.get() - self._id = str(uuid4()) + self._id = generate_uuid(variant='uuid') @property def id(self): @@ -122,7 +122,7 @@ class OperationTask(BaseTask): """ assert isinstance(node, models.Node) - operation = _get_operation(node.interfaces, interface_name, operation_name) + operation = cls._get_operation(node.interfaces, interface_name, operation_name) if operation is None: raise exceptions.TaskException( 'Could not find operation "{0}" on interface "{1}" for node "{2}"'.format( @@ -130,7 +130,7 @@ class OperationTask(BaseTask): return cls( actor=node, - name='{0}.{1}@{2}'.format(interface_name, operation_name, node.name), + name='{0}.{1}'.format(interface_name, operation_name), plugin=operation.plugin, implementation=operation.implementation, inputs=cls._merge_inputs(operation.inputs, inputs), @@ -153,7 +153,7 @@ class OperationTask(BaseTask): assert isinstance(relationship, models.Relationship) assert runs_on in models.Task.RUNS_ON - operation = _get_operation(relationship.interfaces, interface_name, operation_name) + operation = cls._get_operation(relationship.interfaces, interface_name, operation_name) if operation is None: raise exceptions.TaskException( 'Could not find operation "{0}" on interface "{1}" for relationship "{2}"'.format( @@ -161,10 +161,7 @@ class OperationTask(BaseTask): return cls( actor=relationship, - name='{0}.{1}@{2}->{3}'.format(interface_name, - operation_name, - relationship.source_node.name, - relationship.target_node.name), + name='{0}.{1}'.format(interface_name, operation_name), plugin=operation.plugin, implementation=operation.implementation, inputs=cls._merge_inputs(operation.inputs, inputs), @@ -173,6 +170,13 @@ class OperationTask(BaseTask): **kwargs) @classmethod + def _get_operation(cls, interfaces, interface_name, operation_name): + interface = interfaces.get(interface_name) + if interface is not None: + return interface.operations.get(operation_name) + return None + + @classmethod def _merge_inputs(cls, operation_inputs, override_inputs=None): final_inputs = OrderedDict(operation_inputs) if override_inputs: @@ -216,10 +220,3 @@ class StubTask(BaseTask): """ pass - - -def _get_operation(interfaces, interface_name, operation_name): - interface = interfaces.get(interface_name) - if interface is not None: - return interface.operations.get(operation_name) - return None http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/workflows/api/task_graph.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/api/task_graph.py b/aria/orchestrator/workflows/api/task_graph.py index c88d343..2ead4d0 100644 --- a/aria/orchestrator/workflows/api/task_graph.py +++ b/aria/orchestrator/workflows/api/task_graph.py @@ -17,7 +17,7 @@ Task graph. Used by users to build workflows """ -from uuid import uuid4 +from ....utils.uuid import generate_uuid from collections import Iterable from networkx import DiGraph, topological_sort @@ -49,7 +49,7 @@ class TaskGraph(object): def __init__(self, name): self.name = name - self._id = str(uuid4()) + self._id = generate_uuid(variant='uuid') self._graph = DiGraph() def __repr__(self): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/workflows/builtin/workflows.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/builtin/workflows.py b/aria/orchestrator/workflows/builtin/workflows.py index b714236..f19c031 100644 --- a/aria/orchestrator/workflows/builtin/workflows.py +++ b/aria/orchestrator/workflows/builtin/workflows.py @@ -17,8 +17,9 @@ TSOCA normative lifecycle workflows. """ -from .utils import (create_node_task, create_relationship_tasks) from ... import workflow +from ....modeling.models import Task +from .utils import (create_node_task, create_relationship_tasks) NORMATIVE_STANDARD_INTERFACE = 'Standard' # 'tosca.interfaces.node.lifecycle.Standard' @@ -81,13 +82,13 @@ def install_node(graph, node, **kwargs): sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_SOURCE, - 'source', + Task.RUNS_ON_SOURCE, node, dry) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_PRE_CONFIGURE_TARGET, - 'target', + Task.RUNS_ON_TARGET, node, dry) sequence.append( @@ -98,13 +99,13 @@ def install_node(graph, node, **kwargs): sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_SOURCE, - 'source', + Task.RUNS_ON_SOURCE, node, dry) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_POST_CONFIGURE_TARGET, - 'target', + Task.RUNS_ON_TARGET, node, dry) @@ -153,19 +154,19 @@ def _create_start_tasks(node, dry): sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_SOURCE, - 'source', + Task.RUNS_ON_SOURCE, node, dry) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_ADD_TARGET, - 'target', + Task.RUNS_ON_TARGET, node, dry) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED, - 'target', + Task.RUNS_ON_TARGET, node, dry) return sequence @@ -176,13 +177,13 @@ def _create_stop_tasks(node, dry): sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_REMOVE_TARGET, - 'target', + Task.RUNS_ON_TARGET, node, dry) sequence += \ create_relationship_tasks( NORMATIVE_CONFIGURE_INTERFACE, NORMATIVE_TARGET_CHANGED, - 'target', + Task.RUNS_ON_TARGET, node, dry) sequence.append( http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/workflows/core/task.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/core/task.py b/aria/orchestrator/workflows/core/task.py index 8856b2d..7d8380c 100644 --- a/aria/orchestrator/workflows/core/task.py +++ b/aria/orchestrator/workflows/core/task.py @@ -16,6 +16,7 @@ """ Workflow tasks """ + from contextlib import contextmanager from datetime import datetime from functools import ( @@ -37,7 +38,7 @@ def _locked(func=None): @wraps(func) def _wrapper(self, value, **kwargs): if self._update_fields is None: - raise exceptions.TaskException("Task is not in update mode") + raise exceptions.TaskException('Task is not in update mode') return func(self, value, **kwargs) return _wrapper @@ -72,35 +73,35 @@ class StubTask(BaseTask): class StartWorkflowTask(StubTask): """ - Tasks marking a workflow start + Task marking a workflow start """ pass class EndWorkflowTask(StubTask): """ - Tasks marking a workflow end + Task marking a workflow end """ pass class StartSubWorkflowTask(StubTask): """ - Tasks marking a subworkflow start + Task marking a subworkflow start """ pass class EndSubWorkflowTask(StubTask): """ - Tasks marking a subworkflow end + Task marking a subworkflow end """ pass class OperationTask(BaseTask): """ - Operation tasks + Operation task """ def __init__(self, api_task, *args, **kwargs): http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/orchestrator/workflows/dry.py ---------------------------------------------------------------------- diff --git a/aria/orchestrator/workflows/dry.py b/aria/orchestrator/workflows/dry.py index 240a9f2..766ea0c 100644 --- a/aria/orchestrator/workflows/dry.py +++ b/aria/orchestrator/workflows/dry.py @@ -35,17 +35,19 @@ def convert_to_dry(plugin, implementation, inputs): # pylint: disable=unused-arg @operation def dry_operation(ctx, _plugin, _implementation, **kwargs): with _TERMINAL_LOCK: + print ctx.name if hasattr(ctx, 'relationship'): - puts('> relationship instance: %s -> %s' % ( + puts('> Relationship: {0} -> {1}'.format( Colored.red(ctx.relationship.source_node.name), Colored.red(ctx.relationship.target_node.name))) else: - puts('> node instance: %s' % Colored.red(ctx.node.name)) + 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: %s' % Colored.magenta(plugin)) + puts(' Plugin: {0}'.format(Colored.magenta(plugin, bold=True))) if implementation: - puts(' implementation: %s' % Colored.yellow(safe_repr(implementation))) + puts(' Implementation: {0}'.format(Colored.magenta(safe_repr(implementation)))) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/parser/modeling/context.py ---------------------------------------------------------------------- diff --git a/aria/parser/modeling/context.py b/aria/parser/modeling/context.py index 81c458b..52e724a 100644 --- a/aria/parser/modeling/context.py +++ b/aria/parser/modeling/context.py @@ -18,7 +18,7 @@ import itertools from ...utils.collections import StrictDict, prune, OrderedDict from ...utils.formatting import as_raw from ...utils.console import puts -from .utils import generate_id_string +from ...utils.uuid import generate_uuid class IdType(object): @@ -34,7 +34,7 @@ class IdType(object): UNIVERSAL_RANDOM = 2 """ - Universally unique ID (UUID): 25 random safe characters. + Universally unique ID (UUID): 22 random safe characters. """ @@ -48,13 +48,6 @@ class ModelingContext(object): * :code:`id_type`: Type of IDs to use for instances * :code:`id_max_length`: Maximum allowed instance ID length * :code:`inputs`: Dict of inputs values - * :code:`node_types`: The generated hierarchy of node types - * :code:`group_types`: The generated hierarchy of group types - * :code:`capability_types`: The generated hierarchy of capability types - * :code:`relationship_types`: The generated hierarchy of relationship types - * :code:`policy_types`: The generated hierarchy of policy types - * :code:`artifact_types`: The generated hierarchy of artifact types - * :code:`interface_types`: The generated hierarchy of interface types """ def __init__(self): @@ -67,13 +60,6 @@ class ModelingContext(object): self.id_type = IdType.UNIVERSAL_RANDOM self.id_max_length = 63 # See: http://www.faqs.org/rfcs/rfc1035.html self.inputs = StrictDict(key_class=basestring) - self.node_types = Type() - self.group_types = Type() - self.capability_types = Type() - self.relationship_types = Type() - self.policy_types = Type() - self.artifact_types = Type() - self.interface_types = Type() self._serial_id_counter = itertools.count(1) self._locally_unique_ids = set() @@ -83,13 +69,6 @@ class ModelingContext(object): model_storage.service_template.put(self.template) if self.instance is not None: model_storage.service.put(self.instance) - model_storage.type.put(self.node_types) - model_storage.type.put(self.group_types) - model_storage.type.put(self.capability_types) - model_storage.type.put(self.relationship_types) - model_storage.type.put(self.policy_types) - model_storage.type.put(self.artifact_types) - model_storage.type.put(self.interface_types) def generate_node_id(self, template_name): return self.node_id_format.format( @@ -101,31 +80,19 @@ class ModelingContext(object): return self._serial_id_counter.next() elif self.id_type == IdType.LOCAL_RANDOM: - the_id = generate_id_string(6) + the_id = generate_uuid(6) while the_id in self._locally_unique_ids: - the_id = generate_id_string(6) + the_id = generate_uuid(6) self._locally_unique_ids.add(the_id) return the_id - return generate_id_string() + return generate_uuid() def set_input(self, name, value): self.inputs[name] = value # TODO: coerce to validate type @property - def types_as_raw(self): - return OrderedDict(( - ('node_types', as_raw(self.node_types)), - ('group_types', as_raw(self.group_types)), - ('capability_types', as_raw(self.capability_types)), - ('relationship_types', as_raw(self.relationship_types)), - ('policy_types', as_raw(self.policy_types)), - ('policy_trigger_types', as_raw(self.policy_trigger_types)), - ('artifact_types', as_raw(self.artifact_types)), - ('interface_types', as_raw(self.interface_types)))) - - @property def template_as_raw(self): raw = self.template.as_raw prune(raw) @@ -136,29 +103,3 @@ class ModelingContext(object): raw = self.instance.as_raw prune(raw) return raw - - def dump_types(self, context): - if self.node_types.children: - puts('Node types:') - self.node_types.dump(context) - if self.group_types.children: - puts('Group types:') - self.group_types.dump(context) - if self.capability_types.children: - puts('Capability types:') - self.capability_types.dump(context) - if self.relationship_types.children: - puts('Relationship types:') - self.relationship_types.dump(context) - if self.policy_types.children: - puts('Policy types:') - self.policy_types.dump(context) - if self.policy_trigger_types.children: - puts('Policy trigger types:') - self.policy_trigger_types.dump(context) - if self.artifact_types.children: - puts('Artifact types:') - self.artifact_types.dump(context) - if self.interface_types.children: - puts('Interface types:') - self.interface_types.dump(context) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/parser/modeling/types.py ---------------------------------------------------------------------- diff --git a/aria/parser/modeling/types.py b/aria/parser/modeling/types.py deleted file mode 100644 index 0a232fc..0000000 --- a/aria/parser/modeling/types.py +++ /dev/null @@ -1,146 +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 ...utils.collections import StrictList, StrictDict, OrderedDict -from ...utils.formatting import as_raw -from ...utils.console import puts - - -class Type(object): - """ - Represents a type and its children. - """ - - def __init__(self, name): - if not isinstance(name, basestring): - raise ValueError('must set name (string)') - - self.name = name - self.description = None - self.role = None - self.children = StrictList(value_class=Type) - - def get_parent(self, name): - for child in self.children: - if child.name == name: - return self - parent = child.get_parent(name) - if parent is not None: - return parent - return None - - def is_descendant(self, base_name, name): - base = self.get_descendant(base_name) - if base is not None: - if base.get_descendant(name) is not None: - return True - return False - - def get_descendant(self, name): - if self.name == name: - return self - for child in self.children: - found = child.get_descendant(name) - if found is not None: - return found - return None - - def iter_descendants(self): - for child in self.children: - yield child - for descendant in child.iter_descendants(): - yield descendant - - def get_role(self, name): - def _get_role(the_type): - if the_type is None: - return None - elif the_type.role is None: - return _get_role(self.get_parent(the_type.name)) - return the_type.role - - return _get_role(self.get_descendant(name)) - - @property - def as_raw(self): - return OrderedDict(( - ('name', self.name), - ('description', self.description), - ('role', self.role))) - - def dump(self, context): - if self.name: - puts(context.style.type(self.name)) - with context.style.indent: - for child in self.children: - child.dump(context) - - def append_raw_children(self, types): - for child in self.children: - raw_child = as_raw(child) - raw_child['parent'] = self.name - types.append(raw_child) - child.append_raw_children(types) - - -class RelationshipType(Type): - def __init__(self, name): - super(RelationshipType, self).__init__(name) - - self.properties = StrictDict(key_class=basestring) - self.source_interfaces = StrictDict(key_class=basestring) - self.target_interfaces = StrictDict(key_class=basestring) - - -class PolicyType(Type): - def __init__(self, name): - super(PolicyType, self).__init__(name) - - self.implementation = None - self.properties = StrictDict(key_class=basestring) - - -class PolicyTriggerType(Type): - def __init__(self, name): - super(PolicyTriggerType, self).__init__(name) - - self.implementation = None - self.properties = StrictDict(key_class=basestring) - - -class TypeHierarchy(Type): - """ - Represents a single-parent derivation :class:`Type` hierarchy. - """ - - def __init__(self): - super(TypeHierarchy, self).__init__(name='') - self.name = None # TODO Calling the super __init__ with name='' and then setting it to None - # is an ugly workaround. We need to improve this. here is the reason for the current state: - # In this module there is a class named `Type`. Its `__init__` gets has a `name` argument - # that raises an exception of `name` is not an instance of `basestring`. Here are some - # classes that inherit from `Type`: RelationshipType, PolicyType, PolicyTriggerType. - # But `TypeHierarchy` also inherits from `Type`. And its `__init__` does not call its super - # `__init__`, which causes pylint to yell. As you can clearly see, it also sets `name` to - # None. But calling super __init__ with name=None raises an exception. We tried modifying - # the Type class hierarchies, but it was not that simple. Also calling with name='' without - # setting `name` to None later on raises parsing validation issues. - self.children = StrictList(value_class=Type) - - @property - def as_raw(self): - types = [] - self.append_raw_children(types) - return types http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/aria/parser/modeling/utils.py ---------------------------------------------------------------------- diff --git a/aria/parser/modeling/utils.py b/aria/parser/modeling/utils.py deleted file mode 100644 index 06d5f34..0000000 --- a/aria/parser/modeling/utils.py +++ /dev/null @@ -1,146 +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 ...modeling.exceptions import CannotEvaluateFunctionException -from ...utils.collections import OrderedDict -from ...utils.console import puts -from ..exceptions import InvalidValueError -from ..presentation import Value - -# 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 coerce_value(context, container, value, report_issues=False): - if isinstance(value, Value): - value = value.value - - if isinstance(value, list): - return [coerce_value(context, container, v, report_issues) for v in value] - elif isinstance(value, dict): - return OrderedDict((k, coerce_value(context, container, v, report_issues)) - for k, v in value.items()) - elif hasattr(value, '_evaluate'): - try: - value = value._evaluate(context, container) - value = coerce_value(context, container, value, report_issues) - except CannotEvaluateFunctionException: - pass - except InvalidValueError as e: - if report_issues: - context.validation.report(e.issue) - return value - - -def validate_dict_values(context, the_dict): - if not the_dict: - return - validate_list_values(context, the_dict.values()) - - -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) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/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/36eb2d24/aria/utils/uuid.py ---------------------------------------------------------------------- diff --git a/aria/utils/uuid.py b/aria/utils/uuid.py new file mode 100644 index 0000000..b5f39f8 --- /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/36eb2d24/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 c55ccdd..959f3c4 100644 --- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py +++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py @@ -13,6 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +Creates ARIA service template models based on the TOSCA presentation. + +Relies on many helper methods in the presentation classes. +""" + import re from types import FunctionType from datetime import datetime @@ -35,43 +41,37 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to # Metadata metadata = context.presentation.get('service_template', 'metadata') if metadata is not None: - model.meta_data['template_name'] = Metadata(value=metadata.template_name) - model.meta_data['template_author'] = Metadata(value=metadata.template_author) - model.meta_data['template_version'] = Metadata(value=metadata.template_version) - custom = metadata.custom - if custom: - for name, v in custom.iteritems(): - model.meta_data[name] = Metadata(value=v) + create_metadata_models(context, model, metadata) # Types + model.node_types = Type(variant='node') create_types(context, - context.modeling.node_types, - context.presentation.get('service_template', 'node_types'), - 'node') + model.node_types, + context.presentation.get('service_template', 'node_types')) + model.group_types = Type(variant='group') create_types(context, - context.modeling.group_types, - context.presentation.get('service_template', 'group_types'), - 'group') + model.group_types, + context.presentation.get('service_template', 'group_types')) + model.policy_types = Type(variant='policy') create_types(context, - context.modeling.policy_types, - context.presentation.get('service_template', 'policy_types'), - 'policy') + model.policy_types, + context.presentation.get('service_template', 'policy_types')) + model.relationship_types = Type(variant='relationship') create_types(context, - context.modeling.relationship_types, - context.presentation.get('service_template', 'relationship_types'), - 'relationship') + model.relationship_types, + context.presentation.get('service_template', 'relationship_types')) + model.capability_types = Type(variant='capability') create_types(context, - context.modeling.capability_types, - context.presentation.get('service_template', 'capability_types'), - 'capability') + model.capability_types, + context.presentation.get('service_template', 'capability_types')) + model.artifact_types = Type(variant='artifact') create_types(context, - context.modeling.interface_types, - context.presentation.get('service_template', 'interface_types'), - 'interface') + model.artifact_types, + context.presentation.get('service_template', 'artifact_types')) + model.interface_types = Type(variant='interface') create_types(context, - context.modeling.artifact_types, - context.presentation.get('service_template', 'artifact_types'), - 'artifact') + model.interface_types, + context.presentation.get('service_template', 'interface_types')) # Topology template topology_template = context.presentation.get('service_template', 'topology_template') @@ -85,7 +85,7 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to policies = context.presentation.get('service_template', 'topology_template', 'policies') if policies: for policy in policies.itervalues(): - if context.modeling.policy_types.get_role(policy.type) == 'plugin': + if model.policy_types.get_descendant(policy.type).role == 'plugin': model.plugins[policy._name] = create_plugin_model(context, policy) # Node templates @@ -119,9 +119,23 @@ def create_service_template_model(context): # pylint: disable=too-many-locals,to return model +def create_metadata_models(context, service_template, metadata): + service_template.meta_data['template_name'] = Metadata(name='template_name', + value=metadata.template_name) + service_template.meta_data['template_author'] = Metadata(name='template_author', + value=metadata.template_author) + service_template.meta_data['template_version'] = Metadata(name='template_version', + value=metadata.template_version) + custom = metadata.custom + if custom: + for name, value in custom.iteritems(): + service_template.meta_data[name] = Metadata(name=name, + value=value) + + def create_node_template_model(context, service_template, node_template): node_type = node_template._get_type(context) - node_type = context.modeling.node_types.get_descendant(node_type._name) + node_type = service_template.node_types.get_descendant(node_type._name) model = NodeTemplate(name=node_template._name, type=node_type) @@ -139,14 +153,14 @@ def create_node_template_model(context, service_template, node_template): artifacts = node_template._get_artifacts(context) if artifacts: for artifact_name, artifact in artifacts.iteritems(): - model.artifact_templates[artifact_name] = create_artifact_template_model(context, - artifact) + model.artifact_templates[artifact_name] = \ + create_artifact_template_model(context, service_template, artifact) capabilities = node_template._get_capabilities(context) if capabilities: for capability_name, capability in capabilities.iteritems(): model.capability_templates[capability_name] = \ - create_capability_template_model(context, capability) + create_capability_template_model(context, service_template, capability) if model.target_node_template_constraints: model.target_node_template_constraints = [] @@ -170,7 +184,7 @@ def fix_node_template_model(context, service_template, node_template): def create_group_template_model(context, service_template, group): group_type = group._get_type(context) - group_type = context.modeling.group_types.get_descendant(group_type._name) + group_type = service_template.group_types.get_descendant(group_type._name) model = GroupTemplate(name=group._name, type=group_type) @@ -193,7 +207,7 @@ def create_group_template_model(context, service_template, group): def create_policy_template_model(context, service_template, policy): policy_type = policy._get_type(context) - policy_type = context.modeling.policy_types.get_descendant(policy_type._name) + policy_type = service_template.policy_types.get_descendant(policy_type._name) model = PolicyTemplate(name=policy._name, type=policy_type) @@ -223,7 +237,7 @@ def create_requirement_template_model(context, service_template, requirement): node, node_variant = requirement._get_node(context) if node is not None: if node_variant == 'node_type': - node_type = context.modeling.node_types.get_descendant(node._name) + node_type = service_template.node_types.get_descendant(node._name) model['target_node_type'] = node_type else: node_template = service_template.get_node_template(node._name) @@ -254,13 +268,13 @@ def create_requirement_template_model(context, service_template, requirement): def create_relationship_template_model(context, service_template, relationship): relationship_type, relationship_type_variant = relationship._get_type(context) if relationship_type_variant == 'relationship_type': - relationship_type = context.modeling.relationship_types.get_descendant( + relationship_type = service_template.relationship_types.get_descendant( relationship_type._name) model = RelationshipTemplate(type=relationship_type) else: relationship_template = relationship_type relationship_type = relationship_template._get_type(context) - relationship_type = context.modeling.relationship_types.get_descendant( + relationship_type = service_template.relationship_types.get_descendant( relationship_type._name) model = RelationshipTemplate(type=relationship_type, name=relationship_template._name) @@ -274,9 +288,9 @@ def create_relationship_template_model(context, service_template, relationship): return model -def create_capability_template_model(context, capability): +def create_capability_template_model(context, service_template, capability): capability_type = capability._get_type(context) - capability_type = context.modeling.capability_types.get_descendant(capability_type._name) + capability_type = service_template.capability_types.get_descendant(capability_type._name) model = CapabilityTemplate(name=capability._name, type=capability_type) @@ -293,7 +307,7 @@ def create_capability_template_model(context, capability): if valid_source_types: for valid_source_type in valid_source_types: # TODO: handle shortcut type names - node_type = context.modeling.node_types.get_descendant(valid_source_type) + node_type = service_template.node_types.get_descendant(valid_source_type) model.valid_source_node_types.append(node_type) create_parameter_models_from_assignments(model.properties, capability.properties) @@ -303,7 +317,7 @@ def create_capability_template_model(context, capability): def create_interface_template_model(context, service_template, interface): interface_type = interface._get_type(context) - interface_type = context.modeling.interface_types.get_descendant(interface_type._name) + interface_type = service_template.interface_types.get_descendant(interface_type._name) model = InterfaceTemplate(name=interface._name, type=interface_type) @@ -353,9 +367,9 @@ def create_operation_template_model(context, service_template, operation): # pyl return model -def create_artifact_template_model(context, artifact): +def create_artifact_template_model(context, service_template, artifact): artifact_type = artifact._get_type(context) - artifact_type = context.modeling.artifact_types.get_descendant(artifact_type._name) + artifact_type = service_template.artifact_types.get_descendant(artifact_type._name) model = ArtifactTemplate(name=artifact._name, type=artifact_type, source_path=artifact.file) @@ -380,7 +394,7 @@ def create_artifact_template_model(context, artifact): def create_substitution_template_model(context, service_template, substitution_mappings): - node_type = context.modeling.node_types.get_descendant(substitution_mappings.node_type) + node_type = service_template.node_types.get_descendant(substitution_mappings.node_type) model = SubstitutionTemplate(node_type=node_type) capabilities = substitution_mappings.capabilities @@ -442,7 +456,7 @@ def create_plugin_model(context, policy): # Utils # -def create_types(context, root, types, variant): +def create_types(context, root, types): if types is None: return @@ -457,15 +471,18 @@ def create_types(context, root, types, variant): if root.get_descendant(name) is None: parent_type = the_type._get_parent(context) model = Type(name=the_type._name, - variant=variant) + role=the_type._get_extension('role')) if the_type.description: model.description = the_type.description.value - model.role = the_type._get_extension('role') if parent_type is None: + model.parent = root + model.variant = root.variant root.children.append(model) else: container = root.get_descendant(parent_type._name) if container is not None: + model.parent = container + model.variant = container.variant container.children.append(model) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/36eb2d24/tests/end2end/test_orchestrator.py ---------------------------------------------------------------------- diff --git a/tests/end2end/test_orchestrator.py b/tests/end2end/test_orchestrator.py index f930577..fac6207 100644 --- a/tests/end2end/test_orchestrator.py +++ b/tests/end2end/test_orchestrator.py @@ -17,7 +17,6 @@ import sys from aria.orchestrator.runner import Runner from aria.orchestrator.workflows.builtin import BUILTIN_WORKFLOWS -from aria.parser.modeling.storage import initialize_storage from aria.utils.imports import import_fullname from aria.utils.collections import OrderedDict @@ -44,17 +43,22 @@ def _workflow(workflow_name): workflow_fn = import_fullname('aria.orchestrator.workflows.builtin.' + workflow_name) inputs = {} else: - policy = context.modeling.instance.policies[workflow_name] - sys.path.append(policy.properties['implementation'].value) + workflow = None + for policy in context.modeling.instance.policies: + if policy.name == workflow_name: + workflow = policy + break - workflow_fn = import_fullname(policy.properties['function'].value) + sys.path.append(workflow.properties['implementation'].value) + workflow_fn = import_fullname(workflow.properties['function'].value) inputs = OrderedDict([ - (k, v.value) for k, v in policy.properties.iteritems() + (k, v.value) for k, v in workflow.properties.iteritems() if k not in WORKFLOW_POLICY_INTERNAL_PROPERTIES ]) def _initialize_storage(model_storage): - initialize_storage(context, model_storage, 1) + context.modeling.store(model_storage) - runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage, 1) + runner = Runner(workflow_name, workflow_fn, inputs, _initialize_storage, + lambda: context.modeling.instance.id) runner.run()
