You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ariatosca.apache.org by mx...@apache.org on 2017/10/22 07:51:30 UTC
[20/30] incubator-ariatosca git commit: ARIA-174 Refactor
instantiation phase
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/orchestrator/topology/topology.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/topology.py b/aria/orchestrator/topology/topology.py
new file mode 100644
index 0000000..8ee33d1
--- /dev/null
+++ b/aria/orchestrator/topology/topology.py
@@ -0,0 +1,223 @@
+# 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 ...parser.validation import issue
+from ...modeling import models
+from ...utils import console
+from . import (
+ template_handler,
+ instance_handler,
+ common
+)
+
+
+class Topology(issue.ReporterMixin):
+
+ _init_map = {
+ models.ServiceTemplate: models.Service,
+ models.ArtifactTemplate: models.Artifact,
+ models.CapabilityTemplate: models.Capability,
+ models.GroupTemplate: models.Group,
+ models.InterfaceTemplate: models.Interface,
+ models.NodeTemplate: models.Node,
+ models.PolicyTemplate: models.Policy,
+ models.SubstitutionTemplate: models.Substitution,
+ models.RelationshipTemplate: models.Relationship,
+ models.OperationTemplate: models.Operation,
+ models.SubstitutionTemplateMapping: models.SubstitutionMapping,
+
+ # Common
+ models.Metadata: models.Metadata,
+ models.Attribute: models.Attribute,
+ models.Property: models.Property,
+ models.Input: models.Input,
+ models.Output: models.Output,
+ models.Configuration: models.Configuration,
+ models.Argument: models.Argument,
+ models.Type: models.Type
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(Topology, self).__init__(*args, **kwargs)
+ self._model_cls_to_handler = dict(self._init_handlers(instance_handler),
+ **self._init_handlers(template_handler))
+
+ @staticmethod
+ def _init_handlers(module_):
+ """
+ Register handlers from a handler module to the models
+
+ :param module_: The module to look for handlers
+ :return: a dict where the key is the models class, and the value is the handler class
+ associated with it from the provided module
+ """
+ handlers = {}
+ for attribute_name in dir(module_):
+ if attribute_name.startswith('_'):
+ continue
+ attribute = getattr(module_, attribute_name)
+ if isinstance(attribute, type) and issubclass(attribute, common.HandlerBase):
+ handlers[getattr(models, attribute_name)] = attribute
+ return handlers
+
+ def instantiate(self, model, **kwargs):
+ """
+ instantiate the provided model
+
+ :param model:
+ :param kwargs:
+ :return:
+ """
+ if isinstance(model, dict):
+ return dict((name, self.instantiate(value, **kwargs))
+ for name, value in model.iteritems())
+ elif isinstance(model, list):
+ return list(self.instantiate(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ model_instance_cls = self._init_map[model.__class__]
+ return _handler(self, model).instantiate(model_instance_cls, **kwargs)
+
+ def validate(self, model, **kwargs):
+ if isinstance(model, dict):
+ return self.validate(model.values(), **kwargs)
+ elif isinstance(model, list):
+ return all(self.validate(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ return _handler(self, model).validate(**kwargs)
+
+ def dump(self, model, out_stream=None, title=None, **kwargs):
+ out_stream = out_stream or console.TopologyStylizer()
+
+ # if model is empty, no need to print out the section name
+ if model and title:
+ out_stream.write('{0}:'.format(title))
+
+ if isinstance(model, dict):
+ if str(out_stream):
+ with out_stream.indent():
+ return self.dump(model.values(), out_stream=out_stream, **kwargs)
+ else:
+ return self.dump(model.values(), out_stream=out_stream, **kwargs)
+
+ elif isinstance(model, list):
+ for value in model:
+ self.dump(value, out_stream=out_stream, **kwargs)
+
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ _handler(self, model).dump(out_stream=out_stream, **kwargs)
+
+ return out_stream
+
+ def dump_graph(self, service):
+ out_stream = console.TopologyStylizer()
+ for node in service.nodes.itervalues():
+ if not node.inbound_relationships:
+ self._dump_graph_node(out_stream, node)
+ return out_stream
+
+ def _dump_graph_node(self, out_stream, node, capability=None):
+ out_stream.write(out_stream.node_style(node.name))
+ if capability is not None:
+ out_stream.write('{0} ({1})'.format(out_stream.property_style(capability.name),
+ out_stream.type_style(capability.type.name)))
+ if node.outbound_relationships:
+ with out_stream.indent():
+ for relationship_model in node.outbound_relationships:
+ styled_relationship_name = out_stream.property_style(relationship_model.name)
+ if relationship_model.type is not None:
+ out_stream.write('-> {0} ({1})'.format(
+ styled_relationship_name,
+ out_stream.type_style(relationship_model.type.name)))
+ else:
+ out_stream.write('-> {0}'.format(styled_relationship_name))
+ with out_stream.indent(3):
+ self._dump_graph_node(out_stream,
+ relationship_model.target_node,
+ relationship_model.target_capability)
+
+ def coerce(self, model, **kwargs):
+ if isinstance(model, dict):
+ return self.coerce(model.values(), **kwargs)
+ elif isinstance(model, list):
+ return all(self.coerce(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ return _handler(self, model).coerce(**kwargs)
+
+ def dump_types(self, service_template, out_stream=None):
+ out_stream = out_stream or console.TopologyStylizer()
+ self.dump(service_template.node_types, out_stream, 'Node types')
+ self.dump(service_template.group_types, out_stream, 'Group types')
+ self.dump(service_template.capability_types, out_stream, 'Capability types')
+ self.dump(service_template.relationship_types, out_stream, 'Relationship types')
+ self.dump(service_template.policy_types, out_stream, 'Policy types')
+ self.dump(service_template.artifact_types, out_stream, 'Artifact types')
+ self.dump(service_template.interface_types, out_stream, 'Interface types')
+
+ return out_stream
+
+ def satisfy_requirements(self, model, **kwargs):
+ if isinstance(model, dict):
+ return self.satisfy_requirements(model.values(), **kwargs)
+ elif isinstance(model, list):
+ return all(self.satisfy_requirements(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ return _handler(self, model).satisfy_requirements(**kwargs)
+
+ def validate_capabilities(self, model, **kwargs):
+ if isinstance(model, dict):
+ return self.validate_capabilities(model.values(), **kwargs)
+ elif isinstance(model, list):
+ return all(self.validate_capabilities(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ return _handler(self, model).validate_capabilities(**kwargs)
+
+ def _find_host(self, node):
+ if node.type.role == 'host':
+ return node
+
+ def target_has_role(rel, role):
+ return (rel.target_capability is not None and
+ rel.target_capability.type.role == role)
+
+ for outbound_relationship in node.outbound_relationships:
+ if target_has_role(outbound_relationship, 'host'):
+ host = self._find_host(outbound_relationship.target_node)
+ if host is not None:
+ return host
+ for inbound_relationship in node.inbound_relationships:
+ if target_has_role(inbound_relationship, 'feature'):
+ host = self._find_host(inbound_relationship.source_node)
+ if host is not None:
+ return host
+ return None
+
+ def assign_hosts(self, service):
+ for node in service.nodes.values():
+ node.host = self._find_host(node)
+
+ def configure_operations(self, model, **kwargs):
+ if isinstance(model, dict):
+ return self.configure_operations(model.values(), **kwargs)
+ elif isinstance(model, list):
+ return all(self.configure_operations(value, **kwargs) for value in model)
+ elif model is not None:
+ _handler = self._model_cls_to_handler[model.__class__]
+ return _handler(self, model).configure_operations(**kwargs)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/orchestrator/topology/utils.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/utils.py b/aria/orchestrator/topology/utils.py
new file mode 100644
index 0000000..ec74391
--- /dev/null
+++ b/aria/orchestrator/topology/utils.py
@@ -0,0 +1,48 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from copy import deepcopy
+
+
+def deepcopy_with_locators(value):
+ """
+ Like :func:`deepcopy`, but also copies over locators.
+ """
+
+ res = deepcopy(value)
+ copy_locators(res, value)
+ return res
+
+
+def copy_locators(target, source):
+ """
+ Copies over ``_locator`` for all elements, recursively.
+
+ Assumes that target and source have exactly the same list/dict structure.
+ """
+
+ locator = getattr(source, '_locator', None)
+ if locator is not None:
+ try:
+ setattr(target, '_locator', locator)
+ except AttributeError:
+ pass
+
+ if isinstance(target, list) and isinstance(source, list):
+ for i, _ in enumerate(target):
+ copy_locators(target[i], source[i])
+ elif isinstance(target, dict) and isinstance(source, dict):
+ for k, v in target.items():
+ copy_locators(v, source[k])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/orchestrator/workflow_runner.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflow_runner.py b/aria/orchestrator/workflow_runner.py
index 0eac61c..4dbf29b 100644
--- a/aria/orchestrator/workflow_runner.py
+++ b/aria/orchestrator/workflow_runner.py
@@ -141,9 +141,8 @@ class WorkflowRunner(object):
supplied_inputs=inputs or {})
modeling_utils.validate_required_inputs_are_supplied(declared_inputs=workflow_inputs,
supplied_inputs=inputs or {})
- execution.inputs = modeling_utils.merge_parameter_values(inputs,
- workflow_inputs,
- model_cls=models.Input)
+ execution.inputs = modeling_utils.merge_parameter_values(
+ inputs, workflow_inputs, model_cls=models.Input)
# TODO: these two following calls should execute atomically
self._validate_no_active_executions(execution)
self._model_storage.execution.put(execution)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/orchestrator/workflows/api/task.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index ec96d27..6ce4a00 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -137,14 +137,13 @@ class OperationTask(BaseTask):
operation = self.actor.interfaces[self.interface_name].operations[self.operation_name]
self.plugin = operation.plugin
self.function = operation.function
- self.arguments = modeling_utils.merge_parameter_values(arguments,
- operation.arguments,
- model_cls=models.Argument)
+ self.arguments = modeling_utils.merge_parameter_values(arguments, operation.arguments)
actor = self.actor
if hasattr(actor, '_wrapped'):
# Unwrap instrumented model
actor = actor._wrapped
+
if isinstance(actor, models.Node):
self._context_cls = context.operation.NodeOperationContext
elif isinstance(actor, models.Relationship):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/consumption/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/__init__.py b/aria/parser/consumption/__init__.py
index bd4b29c..f9caf5f 100644
--- a/aria/parser/consumption/__init__.py
+++ b/aria/parser/consumption/__init__.py
@@ -20,7 +20,6 @@ Consumption package.
:nosignatures:
aria.parser.consumption.ConsumptionContext
- aria.parser.consumption.Style
Consumers
---------
@@ -47,7 +46,6 @@ Consumers
from .exceptions import ConsumerException
from .context import ConsumptionContext
-from .style import Style
from .consumer import (
Consumer,
ConsumerChain
@@ -70,7 +68,6 @@ from .inputs import Inputs
__all__ = (
'ConsumerException',
'ConsumptionContext',
- 'Style',
'Consumer',
'ConsumerChain',
'Read',
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/consumption/consumer.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/consumer.py b/aria/parser/consumption/consumer.py
index 4f4c614..878a161 100644
--- a/aria/parser/consumption/consumer.py
+++ b/aria/parser/consumption/consumer.py
@@ -27,6 +27,9 @@ class Consumer(object):
"""
def __init__(self, context):
+ from ...orchestrator import topology
+
+ self.topology = topology.Topology()
self.context = context
def consume(self):
@@ -73,6 +76,10 @@ class ConsumerChain(Consumer):
handle_exception(consumer, e)
else:
raise e
+
+ if consumer.topology.has_issues:
+ self.context.validation.extend_issues(consumer.topology.issues)
+
if self.context.validation.has_issues:
break
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/consumption/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/context.py b/aria/parser/consumption/context.py
index 6fa61f4..9164984 100644
--- a/aria/parser/consumption/context.py
+++ b/aria/parser/consumption/context.py
@@ -16,12 +16,12 @@
import sys
import threading
+from ...utils import console
from ..validation import ValidationContext
from ..loading import LoadingContext
from ..reading import ReadingContext
from ..presentation import PresentationContext
from ..modeling import ModelingContext
-from .style import Style
_thread_locals = threading.local()
@@ -58,12 +58,12 @@ class ConsumptionContext(object):
def __init__(self, set_thread_local=True):
self.args = []
self.out = sys.stdout
- self.style = Style()
self.validation = ValidationContext()
self.loading = LoadingContext()
self.reading = ReadingContext()
self.presentation = PresentationContext()
self.modeling = ModelingContext()
+ self.style = console.TopologyStylizer()
if set_thread_local:
self.set_thread_local()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/consumption/modeling.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/modeling.py b/aria/parser/consumption/modeling.py
index 44027b9..221b308 100644
--- a/aria/parser/consumption/modeling.py
+++ b/aria/parser/consumption/modeling.py
@@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ...utils.formatting import json_dumps, yaml_dumps
from .consumer import Consumer, ConsumerChain
+from ...utils.formatting import json_dumps, yaml_dumps
class DeriveServiceTemplate(Consumer):
@@ -42,7 +42,7 @@ class CoerceServiceTemplateValues(Consumer):
"""
def consume(self):
- self.context.modeling.template.coerce_values(True)
+ self.topology.coerce(self.context.modeling.template, report_issues=True)
class ValidateServiceTemplate(Consumer):
@@ -51,7 +51,7 @@ class ValidateServiceTemplate(Consumer):
"""
def consume(self):
- self.context.modeling.template.validate()
+ self.topology.validate(self.context.modeling.template)
class ServiceTemplate(ConsumerChain):
@@ -74,7 +74,7 @@ class ServiceTemplate(ConsumerChain):
raw = self.context.modeling.template_as_raw
self.context.write(json_dumps(raw, indent=indent))
else:
- self.context.modeling.template.dump()
+ self.context.write(self.topology.dump(self.context.modeling.template))
class Types(Consumer):
@@ -92,7 +92,7 @@ class Types(Consumer):
raw = self.context.modeling.types_as_raw
self.context.write(json_dumps(raw, indent=indent))
else:
- self.context.modeling.template.dump_types()
+ self.topology.dump_types(self.context, self.context.modeling.template)
class InstantiateServiceInstance(Consumer):
@@ -105,9 +105,10 @@ class InstantiateServiceInstance(Consumer):
self.context.validation.report('InstantiateServiceInstance consumer: missing service '
'template')
return
-
- self.context.modeling.template.instantiate(None, None,
- inputs=dict(self.context.modeling.inputs))
+ self.context.modeling.instance = self.topology.instantiate(
+ self.context.modeling.template,
+ inputs=dict(self.context.modeling.inputs)
+ )
class CoerceServiceInstanceValues(Consumer):
@@ -116,7 +117,7 @@ class CoerceServiceInstanceValues(Consumer):
"""
def consume(self):
- self.context.modeling.instance.coerce_values(True)
+ self.topology.coerce(self.context.modeling.instance, report_issues=True)
class ValidateServiceInstance(Consumer):
@@ -125,7 +126,7 @@ class ValidateServiceInstance(Consumer):
"""
def consume(self):
- self.context.modeling.instance.validate()
+ self.topology.validate(self.context.modeling.instance)
class SatisfyRequirements(Consumer):
@@ -134,7 +135,7 @@ class SatisfyRequirements(Consumer):
"""
def consume(self):
- self.context.modeling.instance.satisfy_requirements()
+ self.topology.satisfy_requirements(self.context.modeling.instance)
class ValidateCapabilities(Consumer):
@@ -143,7 +144,7 @@ class ValidateCapabilities(Consumer):
"""
def consume(self):
- self.context.modeling.instance.validate_capabilities()
+ self.topology.validate_capabilities(self.context.modeling.instance)
class FindHosts(Consumer):
@@ -152,7 +153,7 @@ class FindHosts(Consumer):
"""
def consume(self):
- self.context.modeling.instance.find_hosts()
+ self.topology.assign_hosts(self.context.modeling.instance)
class ConfigureOperations(Consumer):
@@ -161,7 +162,7 @@ class ConfigureOperations(Consumer):
"""
def consume(self):
- self.context.modeling.instance.configure_operations()
+ self.topology.configure_operations(self.context.modeling.instance)
class ServiceInstance(ConsumerChain):
@@ -193,4 +194,5 @@ class ServiceInstance(ConsumerChain):
raw = self.context.modeling.instance_as_raw
self.context.write(json_dumps(raw, indent=indent))
else:
- self.context.modeling.instance.dump()
+ str_rep = self.topology.dump(self.context.modeling.instance)
+ self.context.write(str_rep)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/consumption/style.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/style.py b/aria/parser/consumption/style.py
deleted file mode 100644
index 1b52cd3..0000000
--- a/aria/parser/consumption/style.py
+++ /dev/null
@@ -1,54 +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.console import Colored, indent
-from ...utils.formatting import safe_repr
-
-
-class Style(object):
- def __init__(self, indentation=2):
- self.indentation = indentation
-
- @property
- def indent(self):
- return indent(self.indentation)
-
- @staticmethod
- def section(value):
- return Colored.cyan(value, bold=True)
-
- @staticmethod
- def type(value):
- return Colored.blue(value, bold=True)
-
- @staticmethod
- def node(value):
- return Colored.red(value, bold=True)
-
- @staticmethod
- def property(value):
- return Colored.magenta(value, bold=True)
-
- @staticmethod
- def literal(value):
- return Colored.magenta(safe_repr(value))
-
- @staticmethod
- def meta(value):
- return Colored.green(value)
-
- @staticmethod
- def required(value):
- return Colored.white(value)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/modeling/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/context.py b/aria/parser/modeling/context.py
index 3d75617..d8a1f7a 100644
--- a/aria/parser/modeling/context.py
+++ b/aria/parser/modeling/context.py
@@ -19,6 +19,10 @@ from ...utils.collections import StrictDict, prune
from ...utils.uuid import generate_uuid
+# See: http://www.faqs.org/rfcs/rfc1035.html
+ID_MAX_LENGTH = 63
+
+
class IdType(object):
LOCAL_SERIAL = 0
"""
@@ -61,7 +65,7 @@ class ModelingContext(object):
#self.id_type = IdType.LOCAL_SERIAL
#self.id_type = IdType.LOCAL_RANDOM
self.id_type = IdType.UNIVERSAL_RANDOM
- self.id_max_length = 63 # See: http://www.faqs.org/rfcs/rfc1035.html
+ self.id_max_length = ID_MAX_LENGTH
self.inputs = StrictDict(key_class=basestring)
self._serial_id_counter = itertools.count(1)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/presentation/fields.py
----------------------------------------------------------------------
diff --git a/aria/parser/presentation/fields.py b/aria/parser/presentation/fields.py
index 959bad1..5c08d4a 100644
--- a/aria/parser/presentation/fields.py
+++ b/aria/parser/presentation/fields.py
@@ -571,7 +571,7 @@ class Field(object):
def _dump_primitive(self, context, value):
if hasattr(value, 'as_raw'):
value = as_raw(value)
- puts('%s: %s' % (self.name, context.style.literal(value)))
+ puts('%s: %s' % (self.name, context.style.literal_style(value)))
# primitive list
@@ -606,11 +606,11 @@ class Field(object):
def _dump_primitive_list(self, context, value):
puts('%s:' % self.name)
- with context.style.indent:
+ with context.style.indent():
for primitive in value:
if hasattr(primitive, 'as_raw'):
primitive = as_raw(primitive)
- puts(context.style.literal(primitive))
+ puts(context.style.literal_style(primitive))
# primitive dict
@@ -635,11 +635,11 @@ class Field(object):
def _dump_primitive_dict(self, context, value):
puts('%s:' % self.name)
- with context.style.indent:
+ with context.style.indent():
for v in value.itervalues():
if hasattr(v, 'as_raw'):
v = as_raw(v)
- puts(context.style.literal(v))
+ puts(context.style.literal_style(v))
# object
@@ -654,7 +654,7 @@ class Field(object):
def _dump_object(self, context, value):
puts('%s:' % self.name)
- with context.style.indent:
+ with context.style.indent():
if hasattr(value, '_dump'):
value._dump(context)
@@ -669,7 +669,7 @@ class Field(object):
def _dump_object_list(self, context, value):
puts('%s:' % self.name)
- with context.style.indent:
+ with context.style.indent():
for v in value:
if hasattr(v, '_dump'):
v._dump(context)
@@ -685,7 +685,7 @@ class Field(object):
def _dump_object_dict(self, context, value):
puts('%s:' % self.name)
- with context.style.indent:
+ with context.style.indent():
for v in value.itervalues():
if hasattr(v, '_dump'):
v._dump(context)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/presentation/presentation.py
----------------------------------------------------------------------
diff --git a/aria/parser/presentation/presentation.py b/aria/parser/presentation/presentation.py
index fb71a1e..3f9f86d 100644
--- a/aria/parser/presentation/presentation.py
+++ b/aria/parser/presentation/presentation.py
@@ -37,13 +37,13 @@ class Value(object):
def _dump(self, context):
if self.type is not None:
- puts(context.style.type(self.type))
+ puts(context.style.type_style(self.type))
if self.value is not None:
- puts(context.style.literal(self.value))
+ puts(context.style.literal_style(self.value))
if self.description is not None:
- puts(context.style.meta(self.description))
+ puts(context.style.meta_style(self.description))
if self.required is not None:
- puts(context.style.required(self.required))
+ puts(context.style.required_style(self.required))
class PresentationBase(HasCachedMethods):
@@ -142,7 +142,7 @@ class PresentationBase(HasCachedMethods):
if self._name:
puts(context.style.node(self._name))
- with context.style.indent:
+ with context.style.indent():
self._dump_content(context)
else:
self._dump_content(context)
@@ -162,7 +162,7 @@ class PresentationBase(HasCachedMethods):
for field_name in self._iter_field_names(): # pylint: disable=no-member
self._dump_field(context, field_name)
else:
- puts(context.style.literal(self._raw))
+ puts(context.style.literal_style(self._raw))
def _dump_field(self, context, field_name):
"""
@@ -242,7 +242,7 @@ class AsIsPresentation(PresentationBase):
def _dump(self, context):
if hasattr(self._raw, '_dump'):
puts(context.style.node(self._name))
- with context.style.indent:
+ with context.style.indent():
self._raw._dump(context)
else:
super(AsIsPresentation, self)._dump(context)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/reading/__init__.py
----------------------------------------------------------------------
diff --git a/aria/parser/reading/__init__.py b/aria/parser/reading/__init__.py
index 065ca56..c110585 100644
--- a/aria/parser/reading/__init__.py
+++ b/aria/parser/reading/__init__.py
@@ -24,8 +24,6 @@ Reading package.
JinjaReader
JsonReader
Locator
- deepcopy_with_locators
- copy_locators
RawReader
Reader
ReaderSource
@@ -36,7 +34,7 @@ Reading package.
from .raw import RawReader
from .reader import Reader
from .yaml import YamlReader
-from .locator import (Locator, deepcopy_with_locators, copy_locators)
+from .locator import Locator
from .json import JsonReader
from .jinja import JinjaReader
from .context import ReadingContext
@@ -57,8 +55,6 @@ __all__ = (
'ReadingContext',
'RawReader',
'Locator',
- 'deepcopy_with_locators',
- 'copy_locators',
'YamlReader',
'JsonReader',
'JinjaReader')
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/reading/locator.py
----------------------------------------------------------------------
diff --git a/aria/parser/reading/locator.py b/aria/parser/reading/locator.py
index 965164d..57b4d50 100644
--- a/aria/parser/reading/locator.py
+++ b/aria/parser/reading/locator.py
@@ -10,9 +10,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from copy import deepcopy
-
-
from ...utils.console import puts, Colored, indent
@@ -120,35 +117,3 @@ class Locator(object):
def __str__(self):
# Should be in same format as Issue.locator_as_str
return '"%s":%d:%d' % (self.location, self.line, self.column)
-
-
-def deepcopy_with_locators(value):
- """
- Like :func:`deepcopy`, but also copies over locators.
- """
-
- res = deepcopy(value)
- copy_locators(res, value)
- return res
-
-
-def copy_locators(target, source):
- """
- Copies over ``_locator`` for all elements, recursively.
-
- Assumes that target and source have exactly the same list/dict structure.
- """
-
- locator = getattr(source, '_locator', None)
- if locator is not None:
- try:
- setattr(target, '_locator', locator)
- except AttributeError:
- pass
-
- if isinstance(target, list) and isinstance(source, list):
- for i, _ in enumerate(target):
- copy_locators(target[i], source[i])
- elif isinstance(target, dict) and isinstance(source, dict):
- for k, v in target.items():
- copy_locators(v, source[k])
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/validation/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/context.py b/aria/parser/validation/context.py
index ef641bd..da9eef6 100644
--- a/aria/parser/validation/context.py
+++ b/aria/parser/validation/context.py
@@ -13,15 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from .issue import Issue
-from ...utils.threading import LockedList
-from ...utils.collections import FrozenList
-from ...utils.exceptions import print_exception
-from ...utils.console import puts, Colored, indent
-from ...utils.formatting import as_raw
+from . import issue
-class ValidationContext(object):
+class ValidationContext(issue.ReporterMixin):
"""
Validation context.
@@ -35,53 +30,7 @@ class ValidationContext(object):
:vartype max_level: int
"""
- def __init__(self):
+ def __init__(self, *args, **kwargs):
+ super(ValidationContext, self).__init__(*args, **kwargs)
self.allow_unknown_fields = False
self.allow_primitive_coersion = False
- self.max_level = Issue.ALL
-
- self._issues = LockedList()
-
- def report(self, message=None, exception=None, location=None, line=None,
- column=None, locator=None, snippet=None, level=Issue.PLATFORM, issue=None):
- if issue is None:
- issue = Issue(message, exception, location, line, column, locator, snippet, level)
-
- # Avoid duplicate issues
- with self._issues:
- for i in self._issues:
- if str(i) == str(issue):
- return
-
- self._issues.append(issue)
-
- @property
- def has_issues(self):
- return len(self._issues) > 0
-
- @property
- def issues(self):
- issues = [i for i in self._issues if i.level <= self.max_level]
- issues.sort(key=lambda i: (i.level, i.location, i.line, i.column, i.message))
- return FrozenList(issues)
-
- @property
- def issues_as_raw(self):
- return [as_raw(i) for i in self.issues]
-
- def dump_issues(self):
- issues = self.issues
- if issues:
- puts(Colored.blue('Validation issues:', bold=True))
- with indent(2):
- for issue in issues:
- puts(Colored.blue(issue.heading_as_str))
- details = issue.details_as_str
- if details:
- with indent(3):
- puts(details)
- if issue.exception is not None:
- with indent(3):
- print_exception(issue.exception)
- return True
- return False
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/parser/validation/issue.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/issue.py b/aria/parser/validation/issue.py
index db8065d..42fc580 100644
--- a/aria/parser/validation/issue.py
+++ b/aria/parser/validation/issue.py
@@ -15,8 +15,14 @@
from __future__ import absolute_import # so we can import standard 'collections'
-from ...utils.collections import OrderedDict
-from ...utils.type import full_type_name
+from ...utils import (
+ collections,
+ type,
+ threading,
+ exceptions,
+ console,
+ formatting
+)
class Issue(object):
@@ -82,14 +88,14 @@ class Issue(object):
@property
def as_raw(self):
- return OrderedDict((
+ return collections.OrderedDict((
('level', self.level),
('message', self.message),
('location', self.location),
('line', self.line),
('column', self.column),
('snippet', self.snippet),
- ('exception', full_type_name(self.exception) if self.exception else None)))
+ ('exception', type.full_type_name(self.exception) if self.exception else None)))
@property
def locator_as_str(self):
@@ -124,3 +130,61 @@ class Issue(object):
if details:
heading_str += ', ' + details
return heading_str
+
+
+class ReporterMixin(object):
+
+ Issue = Issue
+
+ def __init__(self, *args, **kwargs):
+ super(ReporterMixin, self).__init__(*args, **kwargs)
+ self._issues = threading.LockedList()
+ self.max_level = self.Issue.ALL
+
+ def report(self, message=None, exception=None, location=None, line=None,
+ column=None, locator=None, snippet=None, level=Issue.PLATFORM, issue=None):
+ if issue is None:
+ issue = self.Issue(message, exception, location, line, column, locator, snippet, level)
+
+ # Avoid duplicate issues
+ with self._issues:
+ for i in self._issues:
+ if str(i) == str(issue):
+ return
+
+ self._issues.append(issue)
+
+ @property
+ def has_issues(self):
+ return len(self._issues) > 0
+
+ @property
+ def issues(self):
+ issues = [i for i in self._issues if i.level <= self.max_level]
+ issues.sort(key=lambda i: (i.level, i.location, i.line, i.column, i.message))
+ return collections.FrozenList(issues)
+
+ @property
+ def issues_as_raw(self):
+ return [formatting.as_raw(i) for i in self.issues]
+
+ def extend_issues(self, *issues):
+ with self._issues:
+ self._issues.extend(*issues)
+
+ def dump_issues(self):
+ issues = self.issues
+ if issues:
+ console.puts(console.Colored.blue('Validation issues:', bold=True))
+ with console.indent(2):
+ for issue in issues:
+ console.puts(console.Colored.blue(issue.heading_as_str))
+ details = issue.details_as_str
+ if details:
+ with console.indent(3):
+ console.puts(details)
+ if issue.exception is not None:
+ with console.indent(3):
+ exceptions.print_exception(issue.exception)
+ return True
+ return False
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/utils/__init__.py
----------------------------------------------------------------------
diff --git a/aria/utils/__init__.py b/aria/utils/__init__.py
index 2a957a9..43dd882 100644
--- a/aria/utils/__init__.py
+++ b/aria/utils/__init__.py
@@ -16,3 +16,50 @@
"""
General-purpose utilities package.
"""
+
+from . import (
+ archive,
+ argparse,
+ caching,
+ collections,
+ console,
+ exceptions,
+ file,
+ formatting,
+ http,
+ imports,
+ openclose,
+ plugin,
+ process,
+ specification,
+ threading,
+ type,
+ uris,
+ uuid,
+ validation,
+ versions
+)
+
+
+__all__ = (
+ 'archive',
+ 'argparse',
+ 'caching',
+ 'collections',
+ 'console',
+ 'exceptions',
+ 'file',
+ 'formatting',
+ 'http',
+ 'imports',
+ 'openclose',
+ 'plugin',
+ 'process',
+ 'specification',
+ 'threading',
+ 'type',
+ 'uris',
+ 'uuid',
+ 'validation',
+ 'versions'
+)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/aria/utils/console.py
----------------------------------------------------------------------
diff --git a/aria/utils/console.py b/aria/utils/console.py
index 2f6f622..81e8cf8 100644
--- a/aria/utils/console.py
+++ b/aria/utils/console.py
@@ -19,19 +19,64 @@ Abstraction API above terminal color libraries.
import os
import sys
+from StringIO import StringIO
from contextlib import contextmanager
-from .formatting import safe_str
from ..cli import color
+from . import formatting
_indent_string = ''
+class TopologyStylizer(object):
+ def __init__(self, indentation=0):
+ self._str = StringIO()
+ self._indentation = indentation
+
+ def write(self, string):
+ self._str.write(' ' * self._indentation)
+ self._str.write(string)
+ self._str.write(os.linesep)
+
+ @contextmanager
+ def indent(self, indentation=2):
+ self._indentation += indentation
+ yield
+ self._indentation -= indentation
+
+ @staticmethod
+ def type_style(value):
+ return Colored.blue(value, bold=True)
+
+ @staticmethod
+ def node_style(value):
+ return Colored.red(value, bold=True)
+
+ @staticmethod
+ def property_style(value):
+ return Colored.magenta(value, bold=True)
+
+ @staticmethod
+ def literal_style(value):
+ return Colored.magenta(formatting.safe_repr(value))
+
+ @staticmethod
+ def required_style(value):
+ return Colored.white(value)
+
+ @staticmethod
+ def meta_style(value):
+ return Colored.green(value)
+
+ def __str__(self):
+ return self._str.getvalue()
+
+
def puts(string='', newline=True, stream=sys.stdout):
stream.write(_indent_string)
- stream.write(safe_str(string))
+ stream.write(formatting.safe_str(string))
if newline:
stream.write(os.linesep)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/extensions/aria_extension_tosca/simple_v1_0/misc.py
----------------------------------------------------------------------
diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py
index 23beb3c..a65ff41 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/misc.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py
@@ -52,7 +52,7 @@ class Description(AsIsPresentation):
def _dump(self, context):
value = as_raw(self.value)
- puts(context.style.meta(value))
+ puts(context.style.meta_style(value))
@allow_unknown_fields
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/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 2620150..1f90d29 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -497,8 +497,11 @@ def create_plugin_specification_model(context, policy):
def create_workflow_operation_template_model(context, service_template, policy):
- model = OperationTemplate(name=policy._name,
- service_template=service_template)
+ model = OperationTemplate(name=policy._name)
+ # since we use backpopulates, these fields are populated upon commit, we get a weird(temporary)
+ # behavior where in previous code service_template.workflow_templates is a dict which has None
+ # as key for the value of model.
+ service_template.workflow_templates[model.name] = model
if policy.description:
model.description = policy.description.value
@@ -606,7 +609,7 @@ def create_parameter_model_from_value(template_parameter, template_parameter_nam
def create_parameter_models_from_assignments(properties, source_properties, model_cls):
if source_properties:
for property_name, prop in source_properties.iteritems():
- properties[property_name] = model_cls(name=property_name, # pylint: disable=unexpected-keyword-arg
+ properties[property_name] = model_cls(name=property_name, # pylint: disable=unexpected-keyword-arg
type_name=prop.value.type,
value=prop.value.value,
description=prop.value.description)
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/tests/parser/service_templates.py
----------------------------------------------------------------------
diff --git a/tests/parser/service_templates.py b/tests/parser/service_templates.py
index 55cd4ad..9e8fcae 100644
--- a/tests/parser/service_templates.py
+++ b/tests/parser/service_templates.py
@@ -47,6 +47,7 @@ def consume_use_case(use_case_name, consumer_class_name='instance', cache=True):
assert not context.validation.has_issues
return context, dumper
+
def consume_types_use_case(use_case_name, consumer_class_name='instance', cache=True):
cachedmethod.ENABLED = cache
uri = get_service_template_uri('tosca-simple-1.0', 'types', use_case_name,
@@ -61,12 +62,23 @@ def consume_types_use_case(use_case_name, consumer_class_name='instance', cache=
assert not context.validation.has_issues
return context, dumper
+
def consume_node_cellar(consumer_class_name='instance', cache=True):
+ consume_test_case(
+ get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml'),
+ consumer_class_name=consumer_class_name,
+ inputs_uri=get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'inputs.yaml'),
+ cache=cache
+
+ )
+
+
+def consume_test_case(uri, inputs_uri=None, consumer_class_name='instance', cache=True):
cachedmethod.ENABLED = cache
- uri = get_service_template_uri('tosca-simple-1.0', 'node-cellar', 'node-cellar.yaml')
+ uri = get_service_template_uri(uri)
context = create_context(uri)
- context.args.append('--inputs=' + get_service_template_uri('tosca-simple-1.0', 'node-cellar',
- 'inputs.yaml'))
+ if inputs_uri:
+ context.args.append('--inputs=' + get_service_template_uri(inputs_uri))
consumer, dumper = create_consumer(context, consumer_class_name)
consumer.consume()
context.validation.dump_issues()
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/tests/parser/test_reqs_caps.py
----------------------------------------------------------------------
diff --git a/tests/parser/test_reqs_caps.py b/tests/parser/test_reqs_caps.py
new file mode 100644
index 0000000..e92aec4
--- /dev/null
+++ b/tests/parser/test_reqs_caps.py
@@ -0,0 +1,29 @@
+# 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 .service_templates import consume_test_case
+from ..helpers import get_service_template_uri
+
+
+def test_satisfy_capability_type():
+ consume_reqs_caps_template1('instance')
+
+
+def consume_reqs_caps_template1(consumer_class_name, cache=True):
+ consume_test_case(
+ get_service_template_uri('tosca-simple-1.0', 'reqs_caps', 'reqs_caps1.yaml'),
+ consumer_class_name=consumer_class_name,
+ cache=cache
+ )
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
----------------------------------------------------------------------
diff --git a/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml b/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
new file mode 100644
index 0000000..466a78e
--- /dev/null
+++ b/tests/resources/service-templates/tosca-simple-1.0/reqs_caps/reqs_caps1.yaml
@@ -0,0 +1,40 @@
+# 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.
+
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+capability_types:
+ Socket:
+ derived_from: tosca.capabilities.Root
+
+node_types:
+ Socket:
+ derived_from: tosca.nodes.Root
+ capabilities:
+ socket: Socket
+
+ Plug:
+ derived_from: tosca.nodes.Root
+ requirements:
+ - plug:
+ capability: Socket
+
+topology_template:
+ node_templates:
+ socket:
+ type: Socket
+
+ plug:
+ type: Plug
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/df2b916e/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index 8ca1480..8a4d613 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -23,8 +23,6 @@ from sqlalchemy import (
MetaData
)
-from aria.modeling import models
-
class TestFileSystem(object):