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/07/26 15:51:23 UTC
incubator-ariatosca git commit: fixed all of the tests,
still remain fix the dump_types,
figure out the relationship between the context issues and the handler issues,
is the handler as a singleton is realy necessary,
figure out the local imports [Forced Update!]
Repository: incubator-ariatosca
Updated Branches:
refs/heads/ARIA-174-Refactor-instantiation-phase a0e776fa8 -> 3a4ed87ba (forced update)
fixed all of the tests, still remain fix the dump_types, figure out the relationship between the context issues and the handler issues, is the handler as a singleton is realy necessary, figure out the local imports
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/3a4ed87b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/3a4ed87b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/3a4ed87b
Branch: refs/heads/ARIA-174-Refactor-instantiation-phase
Commit: 3a4ed87ba1b525c1477582a6348ccc7089d6fded
Parents: 50d4c1d
Author: max-orlov <ma...@gigaspaces.com>
Authored: Wed Jul 26 18:03:26 2017 +0300
Committer: max-orlov <ma...@gigaspaces.com>
Committed: Wed Jul 26 18:51:16 2017 +0300
----------------------------------------------------------------------
aria/orchestrator/topology/__init__.py | 12 ++-
aria/orchestrator/topology/instance.py | 136 ++++++++++---------------
aria/orchestrator/topology/template.py | 51 ++++------
aria/parser/consumption/consumer.py | 4 +-
aria/parser/consumption/modeling.py | 6 +-
aria/parser/modeling/context.py | 6 +-
aria/parser/validation/context.py | 59 +----------
aria/parser/validation/issue.py | 68 ++++++++++++-
tests/instantiation/test_configuration.py | 13 +--
9 files changed, 165 insertions(+), 190 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/orchestrator/topology/__init__.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/__init__.py b/aria/orchestrator/topology/__init__.py
index 5c7747b..afa5334 100644
--- a/aria/orchestrator/topology/__init__.py
+++ b/aria/orchestrator/topology/__init__.py
@@ -14,6 +14,8 @@
# limitations under the License.
from StringIO import StringIO
+from ...parser.validation import issue
+from ...parser.consumption.style import Style
from ...modeling import models
from ...utils import console
from . import (
@@ -23,7 +25,7 @@ from . import (
)
-class Handler(object):
+class Handler(issue.Reporter):
_init_map = {
models.ServiceTemplate: models.Service,
@@ -52,8 +54,7 @@ class Handler(object):
class TopologyStylizer(object):
def __init__(self):
- from aria.parser.consumption import style
- self._style = style.Style()
+ self._style = Style()
self._str = StringIO()
def write(self, str_):
@@ -71,9 +72,10 @@ class Handler(object):
except AttributeError:
return super(Handler.TopologyStylizer, self).__getattribute__(item)
- def __init__(self, model_storage=None):
+ def __init__(self, model_storage=None, *args, **kwargs):
# TODO: model storage is required only for the list of plugins, can we get it
# somewhere else?
+ super(Handler, self).__init__(*args, **kwargs)
self._model_storage = model_storage
self._handlers = dict(self._init_handlers(instance), **self._init_handlers(template))
@@ -217,7 +219,7 @@ class Handler(object):
def find_hosts(self, service):
for node in service.nodes.values():
- service.host = self._find_host(node)
+ node.host = self._find_host(node)
def configure_operations(self, model, **kwargs):
if isinstance(model, dict):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/orchestrator/topology/instance.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/instance.py b/aria/orchestrator/topology/instance.py
index 5990792..e160d2b 100644
--- a/aria/orchestrator/topology/instance.py
+++ b/aria/orchestrator/topology/instance.py
@@ -13,14 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+from ... parser.modeling import context
from ... modeling import models
+from ... utils import formatting
from .. import execution_plugin
from .. import decorators
from . import common
-# TODO: this should this be here?
-from aria.utils import formatting
-
class Artifact(common._InstanceHandler):
@@ -129,16 +128,12 @@ class Node(common._OperatorHolderHandler):
**kwargs)
def validate(self, **kwargs):
- # TODO: fix the context
- # context = ConsumptionContext.get_thread_local()
- # if len(self._template.name) > context.modeling.id_max_length:
- # pass
- # context.validation.report('"{0}" has an ID longer than the limit of {1:d} characters: '
- # '{2:d}'.format(
- # self.name,
- # context.modeling.id_max_length,
- # len(self.name)),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ if len(self._model.name) > context.ID_MAX_LENGTH:
+ pass
+ self._topology.report('"{0}" has an ID longer than the limit of {1:d} characters: '
+ '{2:d}'.format(
+ self._model.name, context.ID_MAX_LENGTH, len(self._model.name)),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
self._validate(self._model.properties,
self._model.attributes,
@@ -166,18 +161,13 @@ class Node(common._OperatorHolderHandler):
self._topology.configure_operations(relationship)
def validate_capabilities(self):
- # TODO: fix
- # context = ConsumptionContext.get_thread_local()
satisfied = False
for capability in self._model.capabilities.itervalues():
if not capability.has_enough_relationships:
- # context.validation.report('capability "{0}" of node "{1}" requires at least {2:d} '
- # 'relationships but has {3:d}'.format(
- # capability.name,
- # self.name,
- # capability.min_occurrences,
- # capability.occurrences),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ self._topology.report('capability "{0}" of node "{1}" requires at least {2:d} '
+ 'relationships but has {3:d}'.format(
+ capability.name, self._model.name, capability.min_occurrences, capability.occurrences),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
satisfied = False
return satisfied
@@ -188,22 +178,16 @@ class Node(common._OperatorHolderHandler):
target_node_template, target_node_capability = self._find_target(requirement_template)
if target_node_template is not None:
satisfied = self._satisfy_capability(
- target_node_capability, target_node_template,
- requirement_template)
+ target_node_capability, target_node_template, requirement_template)
else:
- # TODO: fix
-
- # context = ConsumptionContext.get_thread_local()
- # context.validation.report('requirement "{0}" of node "{1}" has no target node '
- # 'template'.format(requirement_template.name, self.name),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ self._topology.report('requirement "{0}" of node "{1}" has no target node template'.
+ format(requirement_template.name, self._model.name),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
satisfied = False
return satisfied
def _satisfy_capability(self, target_node_capability, target_node_template,
requirement_template):
- # TODO: fix reporting
- # context = ConsumptionContext.get_thread_local()
# Find target nodes
target_nodes = target_node_template.nodes
if target_nodes:
@@ -236,38 +220,32 @@ class Node(common._OperatorHolderHandler):
self._model.outbound_relationships.append(relationship_model)
return True
else:
- # context.validation.report('requirement "{0}" of node "{1}" targets node '
- # 'template "{2}" but its instantiated nodes do not '
- # 'have enough capacity'.format(
- # requirement_template.name,
- # self.name,
- # target_node_template.name),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ self._topology.report('requirement "{0}" of node "{1}" targets node '
+ 'template "{2}" but its instantiated nodes do not '
+ 'have enough capacity'.format(
+ requirement_template.name, self._model.name, target_node_template.name),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
return False
else:
- # context.validation.report('requirement "{0}" of node "{1}" targets node template '
- # '"{2}" but it has no instantiated nodes'.format(
- # requirement_template.name,
- # self.name,
- # target_node_template.name),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ self._topology.report('requirement "{0}" of node "{1}" targets node template '
+ '"{2}" but it has no instantiated nodes'.format(
+ requirement_template.name, self._model.name, target_node_template.name),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
return False
def _find_target(self, requirement_template):
# We might already have a specific node template, so we'll just verify it
if requirement_template.target_node_template is not None:
if not self._model.node_template.is_target_node_template_valid(requirement_template.target_node_template):
- # TODO: fix
- pass
- # context.validation.report('requirement "{0}" of node template "{1}" is for node '
- # 'template "{2}" but it does not match constraints'.format(
- # self.name,
- # self.target_node_template.name,
- # source_node_template.name),
- # level=validation.Issue.BETWEEN_TYPES)
+ self._topology.report('requirement "{0}" of node template "{1}" is for node '
+ 'template "{2}" but it does not match constraints'.format(
+ requirement_template.name,
+ requirement_template.target_node_template.name,
+ self._model.node_template.name),
+ level=self._topology.Issue.BETWEEN_TYPES)
if (requirement_template.target_capability_type is not None or
requirement_template.target_capability_name is not None):
- target_node_capability = self._get_capability_from_requirement(requirement_template)
+ target_node_capability = self._get_capability(requirement_template)
if target_node_capability is None:
return None, None
else:
@@ -285,22 +263,27 @@ class Node(common._OperatorHolderHandler):
if not self._model.node_template.is_target_node_template_valid(target_node_template):
continue
- if requirement_template.target_node_template:
- target_node_capability = self._get_capability_from_requirement(requirement_template)
- if target_node_capability is None:
- continue
- else:
- return target_node_template, target_node_capability
+ target_node_capability = self._get_capability(requirement_template,
+ target_node_template)
+
+ if target_node_capability is None:
+ continue
+
+ return target_node_template, target_node_capability
return None, None
- def _get_capability_from_requirement(self, requirement_template):
- for capability_template in requirement_template.target_node_template.capability_templates.values():
- if self._satisfies_requirement(capability_template, requirement_template):
+ def _get_capability(self, requirement_template, target_node_template=None):
+ target_node_template = target_node_template or requirement_template.target_node_template
+
+ for capability_template in target_node_template.capability_templates.values():
+ if self._satisfies_requirement(
+ capability_template, requirement_template, target_node_template):
return capability_template
+
return None
- def _satisfies_requirement(self, capability_template, requirement_template):
+ def _satisfies_requirement(self, capability_template, requirement_template, target_node_template):
# Do we match the required capability type?
if (requirement_template.target_capability_type and
requirement_template.target_capability_type.get_descendant(
@@ -318,7 +301,7 @@ class Node(common._OperatorHolderHandler):
if requirement_template.target_node_template_constraints:
for node_template_constraint in requirement_template.target_node_template_constraints:
if not node_template_constraint.matches(
- self._model.node_template, requirement_template.target_node_template):
+ self._model.node_template, target_node_template):
return False
return True
@@ -396,13 +379,10 @@ class Operation(common._OperatorHolderHandler):
# Check for reserved arguments
used_reserved_names = decorators.OPERATION_DECORATOR_RESERVED_ARGUMENTS.intersection(
self._model.arguments.keys())
- # if used_reserved_names:
- # # context = ConsumptionContext.get_thread_local()
- # context.validation.report('using reserved arguments in operation "{0}": {1}'
- # .format(
- # self.name,
- # formatting.string_list_as_string(used_reserved_names)),
- # level=validation.Issue.EXTERNAL)
+ if used_reserved_names:
+ self._topology.report('using reserved arguments in operation "{0}": {1}'.format(
+ self._model.name, formatting.string_list_as_string(used_reserved_names)),
+ level=self._topology.Issue.EXTERNAL)
class Policy(common._InstanceHandler):
@@ -535,15 +515,11 @@ class Substitution(common._InstanceHandler):
class SubstitutionMapping(common._InstanceHandler):
def validate(self, **kwargs):
- # context = ConsumptionContext.get_thread_local()
if (self._model.capability is None) and (self._model.requirement_template is None):
- pass
- # TODO: handler reports
- # context.validation.report('mapping "{0}" refers to neither capability nor a requirement'
- # ' in node: {1}'.format(
- # self.name,
- # formatting.safe_repr(self.node.name)),
- # level=validation.Issue.BETWEEN_TYPES)
+ self._topology.report('mapping "{0}" refers to neither capability nor a requirement'
+ ' in node: {1}'.format(
+ self._model.name, formatting.safe_repr(self._model.node.name)),
+ level=self._topology.Issue.BETWEEN_TYPES)
def dump(self, console):
if self._model.capability is not None:
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/orchestrator/topology/template.py
----------------------------------------------------------------------
diff --git a/aria/orchestrator/topology/template.py b/aria/orchestrator/topology/template.py
index 2d1319a..917f7a8 100644
--- a/aria/orchestrator/topology/template.py
+++ b/aria/orchestrator/topology/template.py
@@ -15,6 +15,7 @@
from datetime import datetime
+from ...utils import formatting
from ...modeling import utils as modeling_utils
from . import utils, common
@@ -60,10 +61,8 @@ class ServiceTemplate(common._TemplateHandler):
plugin = plugin_specification.plugin
service.plugins[plugin.name] = plugin
else:
- # TODO: fix the context report usage
- pass
- # self._context.validation.report('specified plugin not found: {0}'.format(
- # plugin_specification.name), level=validation.Issue.EXTERNAL)
+ self._topology.report('specified plugin not found: {0}'.format(
+ plugin_specification.name), level=self._topology.Issue.EXTERNAL)
service.meta_data = self._topology.instantiate(self._model.meta_data)
for node_template in self._model.node_templates.itervalues():
@@ -81,8 +80,7 @@ class ServiceTemplate(common._TemplateHandler):
return service
- @staticmethod
- def _scaling(node_template):
+ def _scaling(self, node_template):
scaling = {}
def extract_property(properties, name):
@@ -125,16 +123,10 @@ class ServiceTemplate(common._TemplateHandler):
scaling['max_instances'] < scaling['min_instances'] or
scaling['default_instances'] < scaling['min_instances'] or
scaling['default_instances'] > scaling['max_instances']):
- pass
- # TODO: fix this
- # context = ConsumptionContext.get_thread_local()
- # context.validation.report('invalid scaling parameters for node template "{0}": '
- # 'min={1}, max={2}, default={3}'.format(
- # self.name,
- # scaling['min_instances'],
- # scaling['max_instances'],
- # scaling['default_instances']),
- # level=validation.Issue.BETWEEN_TYPES)
+ self._topology.report(
+ 'invalid scaling parameters for node template "{0}": min={min_instances}, max='
+ '{max_instances}, default={default_instances}'.format(self._model.name, **scaling),
+ level=self._topology.Issue.BETWEEN_TYPES)
return scaling
@@ -475,12 +467,11 @@ class SubstitutionTemplateMapping(common._TemplateHandler):
node_template = self._model.requirement_template.node_template
nodes = node_template.nodes
if len(nodes) == 0:
- # TODO: manage the context report
- # self._context.validation.report(
- # 'mapping "{0}" refers to node template "{1}" but there are no node instances'.
- # format(self._template.mapped_name,
- # self._template.node_template.name),
- # level=validation.Issue.BETWEEN_INSTANCES)
+ self._topology.report(
+ 'mapping "{0}" refers to node template "{1}" but there are no node instances'.
+ format(self._model.mapped_name,
+ self._model.node_template.name),
+ level=self._topology.Issue.BETWEEN_INSTANCES)
return None
# The TOSCA spec does not provide a way to choose the node,
# so we will just pick the first one
@@ -494,18 +485,14 @@ class SubstitutionTemplateMapping(common._TemplateHandler):
return substitution_mapping
def validate(self):
- # context = ConsumptionContext.get_thread_local()
if all([
self._model.capability_template is None,
self._model.requirement_template is None
]):
- pass
- # TODO: handle reporting
- # context.validation.report('mapping "{0}" refers to neither capability nor a requirement'
- # ' in node template: {1}'.format(
- # self.name,
- # formatting.safe_repr(self.node_template.name)),
- # level=validation.Issue.BETWEEN_TYPES)
+ self._topology.report('mapping "{0}" refers to neither capability nor a requirement '
+ 'in node template: {1}'.format(
+ self._model.name, formatting.safe_repr(self._model.node_template.name)),
+ level=self._topology.Issue.BETWEEN_TYPES)
class RelationshipTemplate(common._TemplateHandler):
@@ -535,9 +522,7 @@ class RelationshipTemplate(common._TemplateHandler):
return relationship
def validate(self):
- # TODO: either type or name must be set
- self._validate(self._model.properties,
- self._model.interface_templates)
+ self._validate(self._model.properties, self._model.interface_templates)
class OperationTemplate(common._TemplateHandler):
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/parser/consumption/consumer.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/consumer.py b/aria/parser/consumption/consumer.py
index 4c79aab..8acbf31 100644
--- a/aria/parser/consumption/consumer.py
+++ b/aria/parser/consumption/consumer.py
@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from aria.orchestrator import topology
-
from ...exceptions import AriaException
from ...utils.exceptions import print_exception
@@ -29,6 +27,8 @@ class Consumer(object):
"""
def __init__(self, context):
+ from aria.orchestrator import topology
+
self.handler = topology.handler
self.context = context
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/parser/consumption/modeling.py
----------------------------------------------------------------------
diff --git a/aria/parser/consumption/modeling.py b/aria/parser/consumption/modeling.py
index 8216816..828cc30 100644
--- a/aria/parser/consumption/modeling.py
+++ b/aria/parser/consumption/modeling.py
@@ -13,8 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from aria.orchestrator import topology
-
from .consumer import Consumer, ConsumerChain
from ...utils.formatting import json_dumps, yaml_dumps
from ... import exceptions
@@ -108,7 +106,7 @@ class InstantiateServiceInstance(Consumer):
self.context.validation.report('InstantiateServiceInstance consumer: missing service '
'template')
return
- self.context.modeling.instance = topology.handler.instantiate(
+ self.context.modeling.instance = self.handler.instantiate(
self.context.modeling.template,
inputs=dict(self.context.modeling.inputs)
)
@@ -125,7 +123,7 @@ class InstantiateServiceInstance(Consumer):
CoerceServiceInstanceValues
)).consume()
- if self.context.validation.dump_issues():
+ if self.handler.dump_issues():
raise exceptions.InstantiationError('Failed to instantiate service template `{0}`'
.format(self.context.modeling.template.name))
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/aria/parser/modeling/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/modeling/context.py b/aria/parser/modeling/context.py
index 3d75617..286ab60 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
+
+1
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/3a4ed87b/aria/parser/validation/context.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/context.py b/aria/parser/validation/context.py
index ef641bd..a245518 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.Reporter):
"""
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/3a4ed87b/aria/parser/validation/issue.py
----------------------------------------------------------------------
diff --git a/aria/parser/validation/issue.py b/aria/parser/validation/issue.py
index db8065d..f4304b7 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,57 @@ class Issue(object):
if details:
heading_str += ', ' + details
return heading_str
+
+
+class Reporter(object):
+
+ Issue = Issue
+
+ def __init__(self, *args, **kwargs):
+ super(Reporter, 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 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
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/3a4ed87b/tests/instantiation/test_configuration.py
----------------------------------------------------------------------
diff --git a/tests/instantiation/test_configuration.py b/tests/instantiation/test_configuration.py
index 659e334..d4d91da 100644
--- a/tests/instantiation/test_configuration.py
+++ b/tests/instantiation/test_configuration.py
@@ -17,6 +17,7 @@ import pytest
from tests.parser.service_templates import consume_literal
from aria.modeling.utils import parameters_as_values
+from aria.orchestrator.topology import handler
TEMPLATE = """
@@ -165,9 +166,9 @@ def test_remote(service):
def test_reserved_arguments(broken_service_issues):
- assert len(broken_service_issues) == 2
- assert any(
- all([issue.message.startswith('using reserved arguments in operation "operation":'),
- 'ctx' in issue.message,
- 'toolbelt' in issue.message])
- for issue in broken_service_issues)
+ assert len(broken_service_issues) == 1
+ assert len(handler.issues) == 1
+ issue = handler.issues[0].message
+ assert all([issue.startswith('using reserved arguments in operation "operation": '),
+ 'ctx' in issue,
+ 'toolbelt' in issue])