ARIA-1 Parser test suite
* This commit additionally fixes many parser bugs revealed by the test
suite, which includes adding validations that were missing.
* A new "extensions" tox suite is introduced.
* The /tests/parser cases were refactored into /tests/topology and
/tests/extensions.
* The Hello World example was fixed and refactored, as it in fact had
invalid TOSCA (it previously passed due to a missing validation).
* Parser performance was greatly improved by:
1. Switching to the YAML C library
2. Aggressive caching of parsed presentations
3. The ability to skip importing the TOSCA profile
4. The ability to skip validation of normative types
5. A new deepcopy_fast util
6. A new BlockingExecutor that is faster for single-threaded use
* Unicode is now fully supported for all validation and log messages. This
requires the use a unicode (u'' notation) for all .format specs.
* Additionally, PyLint comment directives have been standardized by
pushing them to column 100.
diff --git a/.travis.yml b/.travis.yml
index b264ab3..c6c63fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -13,7 +13,6 @@
# We need to set "sudo: true" in order to use a virtual machine instead of a container, because
# SSH tests fail in the container. See:
# https://docs.travis-ci.com/user/reference/overview/#Virtualization-environments
-
dist: trusty
sudo: true
@@ -23,16 +22,17 @@
- '2.7'
env:
- # The PYTEST_PROCESSES environment var is used in tox.ini to override the --numprocesses argument
- # for PyTest's xdist plugin. The reason this is necessary is that conventional Travis environments
- # may report a large amount of available CPUs, but they they are greatly restricted. Through trial
- # and error we found that more than 1 process may result in failures.
- - PYTEST_PROCESSES=1 TOX_ENV=pylint_core
- - PYTEST_PROCESSES=1 TOX_ENV=pylint_tests
- - PYTEST_PROCESSES=1 TOX_ENV=core
- - PYTEST_PROCESSES=1 TOX_ENV=e2e
- - PYTEST_PROCESSES=1 TOX_ENV=ssh
- - PYTEST_PROCESSES=1 TOX_ENV=docs
+ # The CONCURRENCY environment var is used in tox.ini to override the --numprocesses argument
+ # for PyTest's xdist plugin and the --jobs argument for PyLint. The reason this is necessary is
+ # that in automatic concurrency mode the Travis environments may report a large amount of
+ # available cores, but concurrent tests may fail.
+ - CONCURRENCY=1 TOX_ENV=pylint_core
+ - CONCURRENCY=1 TOX_ENV=pylint_tests
+ - CONCURRENCY=1 TOX_ENV=core
+ - CONCURRENCY=1 TOX_ENV=extensions
+ - CONCURRENCY=1 TOX_ENV=e2e
+ - CONCURRENCY=1 TOX_ENV=ssh
+ - CONCURRENCY=1 TOX_ENV=docs
before_install:
# Create SSH keys for SSH tests
diff --git a/Makefile b/Makefile
index e68538e..584ca45 100644
--- a/Makefile
+++ b/Makefile
@@ -31,6 +31,7 @@
-find . -maxdepth 1 -type f -name '.coverage' -delete
-find . -type f -name '*.pyc' -delete
-find . -type d -name '__pycache__' -prune -exec rm -rf {} \; 2>/dev/null
+ -find . -type d -name '*.egg-info' -exec rm -rf {} \; 2>/dev/null
install:
pip install .[ssh]
@@ -54,10 +55,12 @@
tox -e pylint_core \
-e pylint_tests \
-e core \
+ -e extensions \
-e e2e \
-e ssh \
-e docs
./requirements.txt: ./requirements.in
pip install --upgrade "pip-tools>=1.9.0"
+ rm ./requirements.txt
pip-compile --output-file ./requirements.txt ./requirements.in
diff --git a/README.rst b/README.rst
index c905277..42b5fb3 100644
--- a/README.rst
+++ b/README.rst
@@ -63,7 +63,7 @@
yum install -y python-devel gcc libffi-devel openssl-devel
pip install apache-ariatosca[ssh]
-**Archlinux**::
+**Arch Linux**::
pacman -Syu --noconfirm python2 gcc libffi openssl
pip2 install apache-ariatosca[ssh]
@@ -73,7 +73,7 @@
# no additional system requirements are needed
pip install apache-ariatosca[ssh]
-**MacOS**::
+**macOS**::
# TODO
@@ -112,26 +112,14 @@
aria service-templates delete my-service-template
-Contribution
-------------
-
-You are welcome and encouraged to participate and contribute to the ARIA project.
-
-Please see our guide to
-`Contributing to ARIA
-<https://cwiki.apache.org/confluence/display/ARIATOSCA/Contributing+to+ARIA>`__.
-
-Feel free to also provide feedback on the mailing lists (see `Resources <#user-content-resources>`__
-section).
-
-
Resources
---------
-- `ARIA homepage <http://ariatosca.incubator.apache.org/>`__
-- `ARIA wiki <https://cwiki.apache.org/confluence/display/AriaTosca>`__
-- `Issue tracker <https://issues.apache.org/jira/browse/ARIA>`__
-- `ARIA revisions released <https://dist.apache.org/repos/dist/dev/incubator/ariatosca//>`__
+- `Main site <http://ariatosca.incubator.apache.org/>`__
+- `API and CLI documentation <http://ariatosca.incubator.apache.org/docs/html/>`__
+- `Wiki <https://cwiki.apache.org/confluence/display/AriaTosca>`__
+- `Releases <https://dist.apache.org/repos/dist/dev/incubator/ariatosca//>`__
+- `Issue tracker <https://issues.apache.org/jira/browse/ARIA>`__
- Dev mailing list: dev@ariatosca.incubator.apache.org
- User mailing list: user@ariatosca.incubator.apache.org
@@ -150,6 +138,27 @@
ARIA is licensed under the
`Apache License 2.0 <https://github.com/apache/incubator-ariatosca/blob/master/LICENSE>`__.
+
+Contribution
+------------
+
+You are welcome and encouraged to participate and contribute to the ARIA project.
+
+Please see our guide to
+`Contributing to ARIA
+<https://cwiki.apache.org/confluence/display/ARIATOSCA/Contributing+to+ARIA>`__.
+
+Feel free to also provide feedback on the mailing lists (see `Resources <#user-content-resources>`__
+section).
+
+
+Code of Conduct
+---------------
+
+The ARIA TOSCA Project follows
+`the Apache Code of Conduct <https://www.apache.org/foundation/policies/conduct.html>`__.
+
+
.. |Build Status| image:: https://img.shields.io/travis/apache/incubator-ariatosca/master.svg
:target: https://travis-ci.org/apache/incubator-ariatosca
.. |Appveyor Build Status| image:: https://img.shields.io/appveyor/ci/ApacheSoftwareFoundation/incubator-ariatosca/master.svg
@@ -165,9 +174,3 @@
:target: https://github.com/apache/incubator-ariatosca/pulls
.. |Closed Pull Requests| image:: https://img.shields.io/github/issues-pr-closed-raw/apache/incubator-ariatosca.svg
:target: https://github.com/apache/incubator-ariatosca/pulls?q=is%3Apr+is%3Aclosed
-
-
-Code of Conduct
----------------
-
-The ARIA TOSCA Project follows `The Apache Code of Conduct <https://www.apache.org/foundation/policies/conduct.html>`__.
diff --git a/appveyor.yml b/appveyor.yml
index f7d70e6..3e0385a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -20,6 +20,7 @@
- PYTHON: "C:\\Python27"
PYTHON_VERSION: 2.7.8
PYTHON_ARCH: 32
+ CONCURRENCY: 1
build: false
diff --git a/aria/__init__.py b/aria/__init__.py
index 9bd6f8f..980a2bb 100644
--- a/aria/__init__.py
+++ b/aria/__init__.py
@@ -23,9 +23,8 @@
aria_package_name = 'apache-ariatosca'
__version__ = pkg_resources.get_distribution(aria_package_name).version
-
-from .orchestrator.decorators import workflow, operation # pylint: disable=wrong-import-position
-from . import ( # pylint: disable=wrong-import-position
+from .orchestrator.decorators import (workflow, operation) # pylint: disable=wrong-import-position
+from . import ( # pylint: disable=wrong-import-position
extension,
utils,
parser,
@@ -47,23 +46,19 @@
def install_aria_extensions(strict=True):
"""
- Iterates all Python packages with names beginning with ``aria_extension_`` and all
- ``aria_extension`` entry points and loads them.
+ Loads all Python packages with names beginning with ``aria_extension_`` and calls their
+ ``aria_extension`` initialization entry points if they have them.
- It then invokes all registered extension functions.
-
- :param strict: if set to ``True``, Tries to load extensions with
- dependency versions under consideration. Otherwise tries to load the
- required package without version consideration. Defaults to True.
+ :param strict: if ``True`` tries to load extensions while taking into account the versions
+ of their dependencies, otherwise ignores versions
:type strict: bool
"""
for loader, module_name, _ in iter_modules():
if module_name.startswith('aria_extension_'):
loader.find_module(module_name).load_module(module_name)
for entry_point in pkg_resources.iter_entry_points(group='aria_extension'):
- # It should be possible to enable non strict loading - use the package
- # that is already installed inside the environment, and forgo the
- # version demand
+ # It should be possible to enable non strict loading - use the package that is already
+ # installed inside the environment, and forgo the version demand
if strict:
entry_point.load()
else:
diff --git a/aria/cli/commands/services.py b/aria/cli/commands/services.py
index 6752899..32622a9 100644
--- a/aria/cli/commands/services.py
+++ b/aria/cli/commands/services.py
@@ -137,7 +137,7 @@
@aria.pass_logger
def create(service_template_name,
service_name,
- inputs, # pylint: disable=redefined-outer-name
+ inputs, # pylint: disable=redefined-outer-name
model_storage,
resource_storage,
plugin_manager,
diff --git a/aria/cli/utils.py b/aria/cli/utils.py
index 697ff37..1b5d666 100644
--- a/aria/cli/utils.py
+++ b/aria/cli/utils.py
@@ -58,7 +58,7 @@
'There already a exists a {model_class} with the same name' \
.format(model_class=model_class, name=name, linesep=os.linesep)
trace = sys.exc_info()[2]
- raise type(e), type(e)(new_message), trace # pylint: disable=raising-non-exception
+ raise type(e), type(e)(new_message), trace # pylint: disable=raising-non-exception
def download_file(url):
@@ -107,7 +107,7 @@
filled_length = min(bar_length, int(round(bar_length * read_bytes / float(total_bytes))))
percents = min(100.00, round(100.00 * (read_bytes / float(total_bytes)), 2))
- bar = '#' * filled_length + '-' * (bar_length - filled_length) # pylint: disable=blacklisted-name
+ bar = '#' * filled_length + '-' * (bar_length - filled_length) # pylint: disable=blacklisted-name
# The \r caret makes sure the cursor moves back to the beginning of the line
sys.stdout.write('\r{0} {1} |{2}| {3}%'.format(action, file_name, bar, percents))
diff --git a/aria/modeling/functions.py b/aria/modeling/functions.py
index 554bbfb..f3f0f22 100644
--- a/aria/modeling/functions.py
+++ b/aria/modeling/functions.py
@@ -66,7 +66,7 @@
self.final = final
-def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches
+def evaluate(value, container_holder, report_issues=False): # pylint: disable=too-many-branches
"""
Recursively attempts to call ``__evaluate__``. If an evaluation occurred will return an
:class:`Evaluation`, otherwise it will be ``None``. If any evaluation is non-final, then the
diff --git a/aria/modeling/mixins.py b/aria/modeling/mixins.py
index d58c25a..eb1ac83 100644
--- a/aria/modeling/mixins.py
+++ b/aria/modeling/mixins.py
@@ -201,7 +201,7 @@
@property
@caching.cachedmethod
- def container(self): # pylint: disable=too-many-return-statements,too-many-branches
+ def container(self): # pylint: disable=too-many-return-statements,too-many-branches
"""
The logical container for this parameter, which would be another model: service, node,
group, or policy (or their templates).
@@ -319,7 +319,7 @@
type_name = canonical_type_name(value)
if type_name is None:
type_name = full_type_name(value)
- return cls(name=name, # pylint: disable=unexpected-keyword-arg
+ return cls(name=name, # pylint: disable=unexpected-keyword-arg
type_name=type_name,
value=value,
description=description)
diff --git a/aria/modeling/orchestration.py b/aria/modeling/orchestration.py
index 4d4f0fe..da91295 100644
--- a/aria/modeling/orchestration.py
+++ b/aria/modeling/orchestration.py
@@ -436,7 +436,7 @@
return self.node or self.relationship
@orm.validates('max_attempts')
- def validate_max_attempts(self, _, value): # pylint: disable=no-self-use
+ def validate_max_attempts(self, _, value): # pylint: disable=no-self-use
"""
Validates that max attempts is either -1 or a positive number.
"""
diff --git a/aria/modeling/service_common.py b/aria/modeling/service_common.py
index d1f6b00..6ca80ee 100644
--- a/aria/modeling/service_common.py
+++ b/aria/modeling/service_common.py
@@ -22,7 +22,8 @@
from sqlalchemy import (
Column,
Text,
- Boolean
+ Boolean,
+ PickleType
)
from sqlalchemy.ext.declarative import declared_attr
@@ -90,7 +91,7 @@
""")
@classmethod
- def wrap(cls, name, value, description=None, required=True): # pylint: disable=arguments-differ
+ def wrap(cls, name, value, description=None, required=True): # pylint: disable=arguments-differ
input = super(InputBase, cls).wrap(name, value, description)
input.required = required
return input
@@ -587,12 +588,11 @@
:ivar name: name
:vartype name: basestring
:ivar value: value
- :vartype value: basestring
"""
__tablename__ = 'metadata'
- value = Column(Text)
+ value = Column(PickleType)
@property
def as_raw(self):
diff --git a/aria/modeling/service_instance.py b/aria/modeling/service_instance.py
index 01c4da9..21c1029 100644
--- a/aria/modeling/service_instance.py
+++ b/aria/modeling/service_instance.py
@@ -319,7 +319,7 @@
# region one_to_one relationships
@declared_attr
- def host(cls): # pylint: disable=method-hidden
+ def host(cls): # pylint: disable=method-hidden
"""
Node in which we are hosted (can be ``None``).
@@ -510,7 +510,7 @@
@classmethod
def determine_state(cls, op_name, is_transitional):
"""
- :returns the state the node should be in as a result of running the operation on this node.
+ :return: the state the node should be in as a result of running the operation on this node.
E.g. if we are running tosca.interfaces.node.lifecycle.Standard.create, then
the resulting state should either 'creating' (if the task just started) or 'created'
diff --git a/aria/modeling/service_template.py b/aria/modeling/service_template.py
index cd0adb4..0933407 100644
--- a/aria/modeling/service_template.py
+++ b/aria/modeling/service_template.py
@@ -1415,7 +1415,7 @@
('name', self.name),
('description', self.description),
('type_name', self.type.name),
- ('inputs', formatting.as_raw_dict(self.inputs)), # pylint: disable=no-member
+ ('inputs', formatting.as_raw_dict(self.inputs)), # pylint: disable=no-member
# TODO fix self.properties reference
('operation_templates', formatting.as_raw_list(self.operation_templates))))
@@ -1714,7 +1714,7 @@
return relationship.many_to_one(cls, 'service_template')
@declared_attr
- def plugin(cls): # pylint: disable=method-hidden
+ def plugin(cls): # pylint: disable=method-hidden
"""
Matched plugin.
diff --git a/aria/modeling/utils.py b/aria/modeling/utils.py
index 6e851f2..1b6b375 100644
--- a/aria/modeling/utils.py
+++ b/aria/modeling/utils.py
@@ -35,7 +35,7 @@
# Just here to make sure Sphinx doesn't grab the base constructor's docstring
super(ModelJSONEncoder, self).__init__(*args, **kwargs)
- def default(self, o): # pylint: disable=method-hidden
+ def default(self, o): # pylint: disable=method-hidden
from .mixins import ModelMixin
if isinstance(o, ModelMixin):
if hasattr(o, 'value'):
diff --git a/aria/orchestrator/context/common.py b/aria/orchestrator/context/common.py
index 3c5f618..90205fd 100644
--- a/aria/orchestrator/context/common.py
+++ b/aria/orchestrator/context/common.py
@@ -108,9 +108,9 @@
execution_id=self._execution_id)
def __repr__(self):
- return (
- '{name}(name={self.name}, '
- 'deployment_id={self._service_id}, '
+ return ( # pylint: disable=redundant-keyword-arg
+ u'{name}(name={self.name}, '
+ u'deployment_id={self._service_id}, '
.format(name=self.__class__.__name__, self=self))
@contextmanager
diff --git a/aria/orchestrator/context/operation.py b/aria/orchestrator/context/operation.py
index 8613ec3..7f6612e 100644
--- a/aria/orchestrator/context/operation.py
+++ b/aria/orchestrator/context/operation.py
@@ -40,10 +40,10 @@
self._register_logger(task_id=self.task.id, level=logger_level)
def __repr__(self):
- details = 'function={task.function}; ' \
- 'operation_arguments={task.arguments}'\
+ details = u'function={task.function}; ' \
+ u'operation_arguments={task.arguments}'\
.format(task=self.task)
- return '{name}({0})'.format(details, name=self.name)
+ return u'{name}({0})'.format(details, name=self.name)
@property
def task(self):
@@ -65,9 +65,9 @@
"""
if self.task.plugin is None:
return None
- plugin_workdir = '{0}/plugins/{1}/{2}'.format(self._workdir,
- self.service.id,
- self.task.plugin.name)
+ plugin_workdir = u'{0}/plugins/{1}/{2}'.format(self._workdir,
+ self.service.id,
+ self.task.plugin.name)
file.makedirs(plugin_workdir)
return plugin_workdir
diff --git a/aria/orchestrator/context/workflow.py b/aria/orchestrator/context/workflow.py
index 738d2fd..5a323a6 100644
--- a/aria/orchestrator/context/workflow.py
+++ b/aria/orchestrator/context/workflow.py
@@ -73,7 +73,7 @@
"""
Iterates over nodes templates.
"""
- key = 'service_{0}'.format(self.model.node_template.model_cls.name_column_name())
+ key = u'service_{0}'.format(self.model.node_template.model_cls.name_column_name())
return self.model.node_template.iter(
filters={
@@ -86,7 +86,7 @@
"""
Iterates over nodes.
"""
- key = 'service_{0}'.format(self.model.node.model_cls.name_column_name())
+ key = u'service_{0}'.format(self.model.node.model_cls.name_column_name())
return self.model.node.iter(
filters={
key: getattr(self.service, self.service.name_column_name())
diff --git a/aria/orchestrator/decorators.py b/aria/orchestrator/decorators.py
index 4b163d6..4de0397 100644
--- a/aria/orchestrator/decorators.py
+++ b/aria/orchestrator/decorators.py
@@ -80,6 +80,6 @@
def _generate_name(func_name, ctx, suffix_template, **custom_kwargs):
- return '{func_name}.{suffix}'.format(
+ return u'{func_name}.{suffix}'.format(
func_name=func_name,
suffix=suffix_template.format(ctx=ctx, **custom_kwargs) or generate_uuid(variant='uuid'))
diff --git a/aria/orchestrator/execution_plugin/common.py b/aria/orchestrator/execution_plugin/common.py
index ce6746c..1c279d3 100644
--- a/aria/orchestrator/execution_plugin/common.py
+++ b/aria/orchestrator/execution_plugin/common.py
@@ -35,13 +35,13 @@
split = script_path.split('://')
schema = split[0]
suffix = script_path.split('/')[-1]
- file_descriptor, dest_script_path = tempfile.mkstemp(suffix='-{0}'.format(suffix))
+ file_descriptor, dest_script_path = tempfile.mkstemp(suffix=u'-{0}'.format(suffix))
os.close(file_descriptor)
try:
if schema in ('http', 'https'):
response = requests.get(script_path)
if response.status_code == 404:
- ctx.task.abort('Failed to download script: {0} (status code: {1})'
+ ctx.task.abort(u'Failed to download script: {0} (status code: {1})'
.format(script_path, response.status_code))
content = response.text
with open(dest_script_path, 'wb') as f:
@@ -84,7 +84,7 @@
if isinstance(v, (dict, list, tuple, bool, int, float)):
v = json.dumps(v)
if quote_json_env_vars:
- v = "'{0}'".format(v)
+ v = u"'{0}'".format(v)
if is_windows():
# These <k,v> environment variables will subsequently
# be used in a subprocess.Popen() call, as the `env` parameter.
@@ -102,9 +102,9 @@
command = script_path
command_prefix = process.get('command_prefix')
if command_prefix:
- command = '{0} {1}'.format(command_prefix, command)
+ command = u'{0} {1}'.format(command_prefix, command)
if args:
- command = ' '.join([command] + [str(a) for a in args])
+ command = u' '.join([command] + [str(a) for a in args])
process['command'] = command
return process
@@ -150,5 +150,5 @@
error_check_func()
# if this function is called from within an ``except`` clause, a re-raise maybe required
if reraise:
- raise # pylint: disable=misplaced-bare-raise
+ raise # pylint: disable=misplaced-bare-raise
return _error
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/client.py b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
index 84d66f1..a569c78 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/client.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/client.py
@@ -32,7 +32,7 @@
class _RequestError(RuntimeError):
def __init__(self, ex_message, ex_type, ex_traceback):
- super(_RequestError, self).__init__(self, '{0}: {1}'.format(ex_type, ex_message))
+ super(_RequestError, self).__init__(self, u'{0}: {1}'.format(ex_type, ex_message))
self.ex_type = ex_type
self.ex_message = ex_message
self.ex_traceback = ex_traceback
@@ -45,7 +45,7 @@
response = opener.open(request, timeout=timeout)
if response.code != 200:
- raise RuntimeError('Request failed: {0}'.format(response))
+ raise RuntimeError(u'Request failed: {0}'.format(response))
return json.loads(response.read())
diff --git a/aria/orchestrator/execution_plugin/ctx_proxy/server.py b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
index 91b95d9..d8aa8fb 100644
--- a/aria/orchestrator/execution_plugin/ctx_proxy/server.py
+++ b/aria/orchestrator/execution_plugin/ctx_proxy/server.py
@@ -37,7 +37,7 @@
self.ctx = ctx
self._ctx_patcher = ctx_patcher
self.port = _get_unused_port()
- self.socket_url = 'http://localhost:{0}'.format(self.port)
+ self.socket_url = 'http://localhost:{0:d}'.format(self.port)
self.server = None
self._started = Queue.Queue(1)
self.thread = self._start_server()
@@ -73,7 +73,7 @@
def address_string(self):
return self.client_address[0]
- def log_request(*args, **kwargs): # pylint: disable=no-method-argument
+ def log_request(*args, **kwargs): # pylint: disable=no-method-argument
if not self.quiet:
return wsgiref.simple_server.WSGIRequestHandler.log_request(*args,
**kwargs)
@@ -110,7 +110,7 @@
self.server.server_close()
def _request_handler(self):
- request = bottle.request.body.read() # pylint: disable=no-member
+ request = bottle.request.body.read() # pylint: disable=no-member
response = self._process(request)
return bottle.LocalResponse(
body=json.dumps(response, cls=modeling.utils.ModelJSONEncoder),
@@ -195,7 +195,7 @@
# Modify object attribute
setattr(obj, modifying_key, modifying_value)
else:
- raise CtxError('Cannot modify `{0}` of `{1!r}`'.format(modifying_key, obj))
+ raise CtxError(u'Cannot modify `{0}` of `{1!r}`'.format(modifying_key, obj))
return obj
@@ -233,7 +233,7 @@
obj[arg] = {}
return obj[arg], args
- raise CtxParsingError('Cannot parse argument: `{0!r}`'.format(arg))
+ raise CtxParsingError(u'Cannot parse argument: `{0!r}`'.format(arg))
def _get_unused_port():
diff --git a/aria/orchestrator/execution_plugin/instantiation.py b/aria/orchestrator/execution_plugin/instantiation.py
index 8b52015..d859043 100644
--- a/aria/orchestrator/execution_plugin/instantiation.py
+++ b/aria/orchestrator/execution_plugin/instantiation.py
@@ -64,8 +64,8 @@
"""
from . import operations
- operation.function = '{0}.{1}'.format(operations.__name__,
- operations.run_script_locally.__name__)
+ operation.function = u'{0}.{1}'.format(operations.__name__,
+ operations.run_script_locally.__name__)
def _configure_remote(operation, reporter):
@@ -105,7 +105,7 @@
# Make sure we have a user
if fabric_env.get('user') is None:
- reporter.report('must configure "ssh.user" for "{0}"'.format(operation.implementation),
+ reporter.report(u'must configure "ssh.user" for "{0}"'.format(operation.implementation),
level=reporter.Issue.BETWEEN_TYPES)
# Make sure we have an authentication value
@@ -120,8 +120,8 @@
operation.arguments['fabric_env'] = Argument.wrap('fabric_env', fabric_env,
'Fabric configuration.')
- operation.function = '{0}.{1}'.format(operations.__name__,
- operations.run_script_with_ssh.__name__)
+ operation.function = u'{0}.{1}'.format(operations.__name__,
+ operations.run_script_with_ssh.__name__)
def _get_process(operation, reporter):
@@ -144,7 +144,7 @@
elif k == 'env':
_validate_type(v, dict, 'process.env', reporter)
else:
- reporter.report('unsupported configuration parameter: "process.{0}"'.format(k),
+ reporter.report(u'unsupported configuration parameter: "process.{0}"'.format(k),
level=reporter.Issue.BETWEEN_TYPES)
return value
@@ -175,7 +175,7 @@
elif k == 'address':
_validate_type(v, basestring, 'ssh.address', reporter)
else:
- reporter.report('unsupported configuration parameter: "ssh.{0}"'.format(k),
+ reporter.report(u'unsupported configuration parameter: "ssh.{0}"'.format(k),
level=reporter.Issue.BETWEEN_TYPES)
return value
@@ -185,7 +185,7 @@
return
if not isinstance(value, the_type):
reporter.report(
- '"{0}" configuration is not a {1}: {2}'.format(
+ u'"{0}" configuration is not a {1}: {2}'.format(
name, utils.type.full_type_name(the_type), utils.formatting.safe_repr(value)),
level=reporter.Issue.BETWEEN_TYPES)
@@ -202,7 +202,7 @@
return False
else:
reporter.report(
- '"{0}" configuration is not "true" or "false": {1}'.format(
+ u'"{0}" configuration is not "true" or "false": {1}'.format(
name, utils.formatting.safe_repr(value)),
level=reporter.Issue.BETWEEN_TYPES)
@@ -212,6 +212,6 @@
value = []
for k in sorted(the_dict):
v = the_dict[k]
- _validate_type(v, basestring, '{0}.{1}'.format(name, k), reporter)
+ _validate_type(v, basestring, u'{0}.{1}'.format(name, k), reporter)
value.append(v)
return value
diff --git a/aria/orchestrator/execution_plugin/local.py b/aria/orchestrator/execution_plugin/local.py
index 04b9ecd..abb5b52 100644
--- a/aria/orchestrator/execution_plugin/local.py
+++ b/aria/orchestrator/execution_plugin/local.py
@@ -78,7 +78,7 @@
command = process['command']
env = os.environ.copy()
env.update(process['env'])
- ctx.logger.info('Executing: {0}'.format(command))
+ ctx.logger.info(u'Executing: {0}'.format(command))
with ctx_proxy.server.CtxProxy(ctx, common.patch_ctx) as proxy:
env[ctx_proxy.client.CTX_SOCKET_URL] = proxy.socket_url
running_process = subprocess.Popen(
@@ -95,7 +95,7 @@
exit_code = running_process.wait()
stdout_consumer.join()
stderr_consumer.join()
- ctx.logger.info('Execution done (exit_code={0}): {1}'.format(exit_code, command))
+ ctx.logger.info(u'Execution done (exit_code={0}): {1}'.format(exit_code, command))
def error_check_func():
if exit_code:
diff --git a/aria/orchestrator/execution_plugin/ssh/operations.py b/aria/orchestrator/execution_plugin/ssh/operations.py
index c40e783..759f1d2 100644
--- a/aria/orchestrator/execution_plugin/ssh/operations.py
+++ b/aria/orchestrator/execution_plugin/ssh/operations.py
@@ -48,7 +48,7 @@
with fabric.api.settings(_hide_output(ctx, groups=hide_output),
**_fabric_env(ctx, fabric_env, warn_only=True)):
for command in commands:
- ctx.logger.info('Running command: {0}'.format(command))
+ ctx.logger.info(u'Running command: {0}'.format(command))
run = fabric.api.sudo if use_sudo else fabric.api.run
result = run(command)
if result.failed:
@@ -70,8 +70,8 @@
# there may be race conditions with other operations that
# may be running in parallel, so we pass -p to make sure
# we get 0 exit code if the directory already exists
- fabric.api.run('mkdir -p {0} && mkdir -p {1}'.format(paths.remote_scripts_dir,
- paths.remote_work_dir))
+ fabric.api.run(u'mkdir -p {0} && mkdir -p {1}'.format(paths.remote_scripts_dir,
+ paths.remote_work_dir))
# this file has to be present before using ctx
fabric.api.put(_PROXY_CLIENT_PATH, paths.remote_ctx_path)
process = common.create_process_config(
@@ -82,7 +82,7 @@
fabric.api.put(paths.local_script_path, paths.remote_script_path)
with ctx_proxy.server.CtxProxy(ctx, _patch_ctx) as proxy:
local_port = proxy.port
- with fabric.context_managers.cd(process.get('cwd', paths.remote_work_dir)): # pylint: disable=not-context-manager
+ with fabric.context_managers.cd(process.get('cwd', paths.remote_work_dir)): # pylint: disable=not-context-manager
with tunnel.remote(ctx, local_port=local_port) as remote_port:
local_socket_url = proxy.socket_url
remote_socket_url = local_socket_url.replace(str(local_port), str(remote_port))
@@ -93,8 +93,8 @@
remote_socket_url=remote_socket_url)
fabric.api.put(env_script, paths.remote_env_script_path)
try:
- command = 'source {0} && {1}'.format(paths.remote_env_script_path,
- process['command'])
+ command = u'source {0} && {1}'.format(paths.remote_env_script_path,
+ process['command'])
run = fabric.api.sudo if use_sudo else fabric.api.run
run(command)
except exceptions.TaskException:
@@ -136,8 +136,8 @@
""" Hides Fabric's output for every 'entity' in `groups` """
groups = set(groups or [])
if not groups.issubset(constants.VALID_FABRIC_GROUPS):
- ctx.task.abort('`hide_output` must be a subset of {0} (Provided: {1})'
- .format(', '.join(constants.VALID_FABRIC_GROUPS), ', '.join(groups)))
+ ctx.task.abort(u'`hide_output` must be a subset of {0} (Provided: {1})'
+ .format(u', '.join(constants.VALID_FABRIC_GROUPS), u', '.join(groups)))
return fabric.api.hide(*groups)
@@ -165,16 +165,16 @@
def _write_environment_script_file(process, paths, local_socket_url, remote_socket_url):
env_script = StringIO.StringIO()
env = process['env']
- env['PATH'] = '{0}:$PATH'.format(paths.remote_ctx_dir)
- env['PYTHONPATH'] = '{0}:$PYTHONPATH'.format(paths.remote_ctx_dir)
- env_script.write('chmod +x {0}\n'.format(paths.remote_script_path))
- env_script.write('chmod +x {0}\n'.format(paths.remote_ctx_path))
+ env['PATH'] = u'{0}:$PATH'.format(paths.remote_ctx_dir)
+ env['PYTHONPATH'] = u'{0}:$PYTHONPATH'.format(paths.remote_ctx_dir)
+ env_script.write(u'chmod +x {0}\n'.format(paths.remote_script_path))
+ env_script.write(u'chmod +x {0}\n'.format(paths.remote_ctx_path))
env.update({
ctx_proxy.client.CTX_SOCKET_URL: remote_socket_url,
- 'LOCAL_{0}'.format(ctx_proxy.client.CTX_SOCKET_URL): local_socket_url
+ u'LOCAL_{0}'.format(ctx_proxy.client.CTX_SOCKET_URL): local_socket_url
})
for key, value in env.iteritems():
- env_script.write('export {0}={1}\n'.format(key, value))
+ env_script.write(u'export {0}={1}\n'.format(key, value))
return env_script
@@ -184,12 +184,12 @@
self.local_script_path = local_script_path
self.remote_ctx_dir = base_dir
self.base_script_path = os.path.basename(self.local_script_path)
- self.remote_ctx_path = '{0}/ctx'.format(self.remote_ctx_dir)
- self.remote_scripts_dir = '{0}/scripts'.format(self.remote_ctx_dir)
- self.remote_work_dir = '{0}/work'.format(self.remote_ctx_dir)
- random_suffix = ''.join(random.choice(string.ascii_lowercase + string.digits)
- for _ in range(8))
- remote_path_suffix = '{0}-{1}'.format(self.base_script_path, random_suffix)
- self.remote_env_script_path = '{0}/env-{1}'.format(self.remote_scripts_dir,
- remote_path_suffix)
- self.remote_script_path = '{0}/{1}'.format(self.remote_scripts_dir, remote_path_suffix)
+ self.remote_ctx_path = u'{0}/ctx'.format(self.remote_ctx_dir)
+ self.remote_scripts_dir = u'{0}/scripts'.format(self.remote_ctx_dir)
+ self.remote_work_dir = u'{0}/work'.format(self.remote_ctx_dir)
+ random_suffix = u''.join(random.choice(string.ascii_lowercase + string.digits)
+ for _ in range(8))
+ remote_path_suffix = u'{0}-{1}'.format(self.base_script_path, random_suffix)
+ self.remote_env_script_path = u'{0}/env-{1}'.format(self.remote_scripts_dir,
+ remote_path_suffix)
+ self.remote_script_path = u'{0}/{1}'.format(self.remote_scripts_dir, remote_path_suffix)
diff --git a/aria/orchestrator/execution_plugin/ssh/tunnel.py b/aria/orchestrator/execution_plugin/ssh/tunnel.py
index e76d525..05ea4ed 100644
--- a/aria/orchestrator/execution_plugin/ssh/tunnel.py
+++ b/aria/orchestrator/execution_plugin/ssh/tunnel.py
@@ -64,10 +64,10 @@
try:
channel.close()
except Exception as ex2:
- close_error = ' (While trying to close channel: {0})'.format(ex2)
+ close_error = u' (While trying to close channel: {0})'.format(ex2)
else:
close_error = ''
- ctx.task.abort('[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'
+ ctx.task.abort(u'[{0}] rtunnel: cannot connect to {1}:{2} ({3}){4}'
.format(fabric.api.env.host_string, local_host, local_port, e,
close_error))
diff --git a/aria/orchestrator/plugin.py b/aria/orchestrator/plugin.py
index 756a28e..4f29e4f 100644
--- a/aria/orchestrator/plugin.py
+++ b/aria/orchestrator/plugin.py
@@ -67,8 +67,8 @@
if len(self._model.plugin.list(filters={'package_name': plugin.package_name,
'package_version': plugin.package_version})):
raise exceptions.PluginAlreadyExistsError(
- 'Plugin {0}, version {1} already exists'.format(plugin.package_name,
- plugin.package_version))
+ u'Plugin {0}, version {1} already exists'.format(plugin.package_name,
+ plugin.package_version))
self._install_wagon(source=source, prefix=self.get_plugin_dir(plugin))
self._model.plugin.put(plugin)
return plugin
@@ -120,8 +120,8 @@
"""
if not zipfile.is_zipfile(source):
raise exceptions.InvalidPluginError(
- 'Archive {0} is of an unsupported type. Only '
- 'zip/wgn is allowed'.format(source))
+ u'Archive {0} is of an unsupported type. Only '
+ u'zip/wgn is allowed'.format(source))
with zipfile.ZipFile(source, 'r') as zip_file:
infos = zip_file.infolist()
try:
@@ -130,8 +130,8 @@
zip_file.getinfo(package_json_path)
except (KeyError, ValueError, IndexError):
raise exceptions.InvalidPluginError(
- 'Failed to validate plugin {0} '
- '(package.json was not found in archive)'.format(source))
+ u'Failed to validate plugin {0} '
+ u'(package.json was not found in archive)'.format(source))
def _install_wagon(self, source, prefix):
pip_freeze_output = self._pip_freeze()
diff --git a/aria/orchestrator/topology/instance_handler.py b/aria/orchestrator/topology/instance_handler.py
index 51f26c6..fad00b9 100644
--- a/aria/orchestrator/topology/instance_handler.py
+++ b/aria/orchestrator/topology/instance_handler.py
@@ -34,18 +34,18 @@
out_stream.write(out_stream.node_style(self._model.name))
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Artifact type: {0}'.format(out_stream.type_style(
self._model.type.name)))
- out_stream.write('Source path: {0}'.format(
+ out_stream.write(u'Source path: {0}'.format(
out_stream.literal_style(self._model.source_path)))
if self._model.target_path is not None:
- out_stream.write('Target path: {0}'.format(
+ out_stream.write(u'Target path: {0}'.format(
out_stream.literal_style(self._model.target_path)))
if self._model.repository_url is not None:
- out_stream.write('Repository URL: {0}'.format(
+ out_stream.write(u'Repository URL: {0}'.format(
out_stream.literal_style(self._model.repository_url)))
if self._model.repository_credential:
- out_stream.write('Repository credential: {0}'.format(
+ out_stream.write(u'Repository credential: {0}'.format(
out_stream.literal_style(self._model.repository_credential)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
@@ -60,11 +60,11 @@
def dump(self, out_stream):
out_stream.write(out_stream.node_style(self._model.name))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
- out_stream.write('Occurrences: {0:d} ({1:d}{2})'.format(
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Occurrences: {0:d} ({1:d}{2})'.format(
self._model.occurrences,
self._model.min_occurrences or 0,
- ' to {0:d}'.format(self._model.max_occurrences)
+ u' to {0:d}'.format(self._model.max_occurrences)
if self._model.max_occurrences is not None
else ' or more'))
self._topology.dump(self._model.properties, out_stream, title='Properties')
@@ -81,9 +81,9 @@
**kwargs)
def dump(self, out_stream):
- out_stream.write('Group: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Group: {0}'.format(out_stream.node_style(self._model.name)))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
if self._model.nodes:
@@ -111,7 +111,7 @@
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Interface type: {0}'.format(
+ out_stream.write(u'Interface type: {0}'.format(
out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.inputs, out_stream, title='Inputs')
self._topology.dump(self._model.operations, out_stream, title='Operations')
@@ -134,7 +134,7 @@
def validate(self, **kwargs):
if len(self._model.name) > context.ID_MAX_LENGTH:
self._topology.report(
- '"{0}" has an ID longer than the limit of {1:d} characters: {2:d}'.format(
+ u'"{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)
@@ -146,10 +146,10 @@
self._model.outbound_relationships)
def dump(self, out_stream):
- out_stream.write('Node: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Node: {0}'.format(out_stream.node_style(self._model.name)))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
- out_stream.write('Template: {0}'.format(
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Template: {0}'.format(
out_stream.node_style(self._model.node_template.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
self._topology.dump(self._model.attributes, out_stream, title='Attributes')
@@ -170,11 +170,11 @@
for capability in self._model.capabilities.itervalues():
if not capability.has_enough_relationships:
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),
+ u'capability "{0}" of node "{1}" requires at least {2:d} '
+ u'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
@@ -197,8 +197,8 @@
satisfied = self._satisfy_capability(
target_node_capability, target_node_template, requirement_template)
else:
- self._topology.report('requirement "{0}" of node "{1}" has no target node template'.
- format(requirement_template.name, self._model.name),
+ self._topology.report(u'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
@@ -237,16 +237,16 @@
return True
else:
self._topology.report(
- 'requirement "{0}" of node "{1}" targets node '
- 'template "{2}" but its instantiated nodes do not '
- 'have enough capacity'.format(
+ u'requirement "{0}" of node "{1}" targets node '
+ u'template "{2}" but its instantiated nodes do not '
+ u'have enough capacity'.format(
requirement_template.name, self._model.name, target_node_template.name),
level=self._topology.Issue.BETWEEN_INSTANCES)
return False
else:
self._topology.report(
- 'requirement "{0}" of node "{1}" targets node template '
- '"{2}" but it has no instantiated nodes'.format(
+ u'requirement "{0}" of node "{1}" targets node template '
+ u'"{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
@@ -258,8 +258,8 @@
if not self._model.node_template.is_target_node_template_valid(
requirement_template.target_node_template):
self._topology.report(
- 'requirement "{0}" of node template "{1}" is for node '
- 'template "{2}" but it does not match constraints'.format(
+ u'requirement "{0}" of node template "{1}" is for node '
+ u'template "{2}" but it does not match constraints'.format(
requirement_template.name,
requirement_template.target_node_template.name,
self._model.node_template.name),
@@ -359,28 +359,28 @@
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
if self._model.implementation is not None:
- out_stream.write('Implementation: {0}'.format(
+ out_stream.write(u'Implementation: {0}'.format(
out_stream.literal_style(self._model.implementation)))
if self._model.dependencies:
out_stream.write(
- 'Dependencies: {0}'.format(', '.join((str(out_stream.literal_style(v))
- for v in self._model.dependencies))))
+ u'Dependencies: {0}'.format(u', '.join((str(out_stream.literal_style(v))
+ for v in self._model.dependencies))))
self._topology.dump(self._model.inputs, out_stream, title='Inputs')
if self._model.executor is not None:
- out_stream.write('Executor: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Executor: {0}'.format(out_stream.literal_style(
self._model.executor)))
if self._model.max_attempts is not None:
- out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Max attempts: {0}'.format(out_stream.literal_style(
self._model.max_attempts)))
if self._model.retry_interval is not None:
- out_stream.write('Retry interval: {0}'.format(
+ out_stream.write(u'Retry interval: {0}'.format(
out_stream.literal_style(self._model.retry_interval)))
if self._model.plugin is not None:
- out_stream.write('Plugin: {0}'.format(
+ out_stream.write(u'Plugin: {0}'.format(
out_stream.literal_style(self._model.plugin.name)))
self._topology.dump(self._model.configurations, out_stream, title='Configuration')
if self._model.function is not None:
- out_stream.write('Function: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Function: {0}'.format(out_stream.literal_style(
self._model.function)))
self._topology.dump(self._model.arguments, out_stream, title='Arguments')
@@ -418,7 +418,7 @@
self._model.arguments.keys())
if used_reserved_names:
self._topology.report(
- 'using reserved arguments in operation "{0}": {1}'.format(
+ u'using reserved arguments in operation "{0}": {1}'.format(
self._model.name, formatting.string_list_as_string(used_reserved_names)),
level=self._topology.Issue.EXTERNAL)
@@ -431,9 +431,9 @@
self._topology.validate(self._model.properties, **kwargs)
def dump(self, out_stream):
- out_stream.write('Policy: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Policy: {0}'.format(out_stream.node_style(self._model.name)))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
if self._model.nodes:
out_stream.write('Target nodes:')
@@ -460,21 +460,21 @@
def dump(self, out_stream):
if self._model.name:
- out_stream.write('{0} ->'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'{0} ->'.format(out_stream.node_style(self._model.name)))
else:
out_stream.write('->')
with out_stream.indent():
- out_stream.write('Node: {0}'.format(out_stream.node_style(
+ out_stream.write(u'Node: {0}'.format(out_stream.node_style(
self._model.target_node.name)))
if self._model.target_capability:
- out_stream.write('Capability: {0}'.format(out_stream.node_style(
+ out_stream.write(u'Capability: {0}'.format(out_stream.node_style(
self._model.target_capability.name)))
if self._model.type is not None:
- out_stream.write('Relationship type: {0}'.format(
+ out_stream.write(u'Relationship type: {0}'.format(
out_stream.type_style(self._model.type.name)))
if (self._model.relationship_template is not None and
self._model.relationship_template.name):
- out_stream.write('Relationship template: {0}'.format(
+ out_stream.write(u'Relationship template: {0}'.format(
out_stream.node_style(self._model.relationship_template.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
@@ -549,7 +549,7 @@
def dump(self, out_stream):
out_stream.write('Substitution:')
with out_stream.indent():
- out_stream.write('Node type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Node type: {0}'.format(out_stream.type_style(
self._model.node_type.name)))
self._topology.dump(self._model.mappings, out_stream, title='Mappings')
@@ -562,19 +562,19 @@
def validate(self, **_):
if (self._model.capability is None) and (self._model.requirement_template is None):
self._topology.report(
- 'mapping "{0}" refers to neither capability nor a requirement'
- ' in node: {1}'.format(
+ u'mapping "{0}" refers to neither capability nor a requirement'
+ u' in node: {1}'.format(
self._model.name, formatting.safe_repr(self._model.node_style.name)),
level=self._topology.Issue.BETWEEN_TYPES)
def dump(self, out_stream):
if self._model.capability is not None:
- out_stream.write('{0} -> {1}.{2}'.format(
+ out_stream.write(u'{0} -> {1}.{2}'.format(
out_stream.node_style(self._model.name),
out_stream.node_style(self._model.capability.node.name),
out_stream.node_style(self._model.capability.name)))
else:
- out_stream.write('{0} -> {1}.{2}'.format(
+ out_stream.write(u'{0} -> {1}.{2}'.format(
out_stream.node_style(self._model.name),
out_stream.node_style(self._model.node.name),
out_stream.node_style(self._model.requirement_template.name)))
@@ -583,7 +583,7 @@
class Metadata(common.InstanceHandlerBase):
def dump(self, out_stream):
- out_stream.write('{0}: {1}'.format(
+ out_stream.write(u'{0}: {1}'.format(
out_stream.property_style(self._model.name),
out_stream.literal_style(self._model.value)))
@@ -601,12 +601,12 @@
def dump(self, out_stream):
if self._model.type_name is not None:
- out_stream.write('{0}: {1} ({2})'.format(
+ out_stream.write(u'{0}: {1} ({2})'.format(
out_stream.property_style(self._model.name),
out_stream.literal_style(formatting.as_raw(self._model.value)),
out_stream.type_style(self._model.type_name)))
else:
- out_stream.write('{0}: {1}'.format(
+ out_stream.write(u'{0}: {1}'.format(
out_stream.property_style(self._model.name),
out_stream.literal_style(formatting.as_raw(self._model.value))))
if self._model.description:
diff --git a/aria/orchestrator/topology/template_handler.py b/aria/orchestrator/topology/template_handler.py
index a84a988..3b1948a 100644
--- a/aria/orchestrator/topology/template_handler.py
+++ b/aria/orchestrator/topology/template_handler.py
@@ -69,7 +69,7 @@
plugin = plugin_specification.plugin
service.plugins[plugin.name] = plugin
else:
- self._topology.report('specified plugin not found: {0}'.format(
+ self._topology.report(u'specified plugin not found: {0}'.format(
plugin_specification.name), level=self._topology.Issue.EXTERNAL)
service.meta_data = self._topology.instantiate(self._model.meta_data)
@@ -108,17 +108,18 @@
def _scaling(self, node_template):
scaling = node_template.scaling
- if any([scaling['min_instances'] < 0,
+ if any((scaling['min_instances'] < 0,
scaling['max_instances'] < scaling['min_instances'],
scaling['max_instances'] < 0,
scaling['default_instances'] < 0,
scaling['default_instances'] < scaling['min_instances'],
scaling['default_instances'] > scaling['max_instances']
- ]):
+ )):
self._topology.report(
- 'invalid scaling parameters for node template "{0}": min={min_instances}, max='
- '{max_instances}, default={default_instances}'.format(self._model.name, **scaling),
+ u'invalid scaling parameters for node template "{0}": min={min_instances}, max='
+ u'{max_instances}, default={default_instances}'.format(node_template.name,
+ **scaling),
level=self._topology.Issue.BETWEEN_TYPES)
return scaling
@@ -150,18 +151,18 @@
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Artifact type: {0}'.format(out_stream.type_style(
self._model.type.name)))
- out_stream.write('Source path: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Source path: {0}'.format(out_stream.literal_style(
self._model.source_path)))
if self._model.target_path is not None:
- out_stream.write('Target path: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Target path: {0}'.format(out_stream.literal_style(
self._model.target_path)))
if self._model.repository_url is not None:
- out_stream.write('Repository URL: {0}'.format(
+ out_stream.write(u'Repository URL: {0}'.format(
out_stream.literal_style(self._model.repository_url)))
if self._model.repository_credential:
- out_stream.write('Repository credential: {0}'.format(
+ out_stream.write(u'Repository credential: {0}'.format(
out_stream.literal_style(self._model.repository_credential)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
@@ -189,17 +190,17 @@
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
out_stream.write(
- 'Occurrences: {0:d}{1}'.format(
+ u'Occurrences: {0:d}{1}'.format(
self._model.min_occurrences or 0,
- ' to {0:d}'.format(self._model.max_occurrences)
+ u' to {0:d}'.format(self._model.max_occurrences)
if self._model.max_occurrences is not None
else ' or more'))
if self._model.valid_source_node_types:
- out_stream.write('Valid source node types: {0}'.format(
- ', '.join((str(out_stream.type_style(v.name))
- for v in self._model.valid_source_node_types))))
+ out_stream.write(u'Valid source node types: {0}'.format(
+ u', '.join((str(out_stream.type_style(v.name))
+ for v in self._model.valid_source_node_types))))
self._topology.dump(self._model.properties, out_stream, title='Properties')
def coerce(self, **kwargs):
@@ -228,19 +229,19 @@
out_stream.write('Requirement:')
with out_stream.indent():
if self._model.target_node_type is not None:
- out_stream.write('Target node type: {0}'.format(
+ out_stream.write(u'Target node type: {0}'.format(
out_stream.type_style(self._model.target_node_type.name)))
elif self._model.target_node_template is not None:
- out_stream.write('Target node template: {0}'.format(
+ out_stream.write(u'Target node template: {0}'.format(
out_stream.node_style(self._model.target_node_template.name)))
if self._model.target_capability_type is not None:
- out_stream.write('Target capability type: {0}'.format(
+ out_stream.write(u'Target capability type: {0}'.format(
out_stream.type_style(self._model.target_capability_type.name)))
elif self._model.target_capability_name is not None:
- out_stream.write('Target capability name: {0}'.format(
+ out_stream.write(u'Target capability name: {0}'.format(
out_stream.node_style(self._model.target_capability_name)))
if self._model.target_node_template_constraints:
- out_stream.write('Target node template constraints:')
+ out_stream.write(u'Target node template constraints:')
with out_stream.indent():
for constraint in self._model.target_node_template_constraints:
out_stream.write(out_stream.literal_style(constraint))
@@ -261,16 +262,16 @@
class GroupTemplate(common.TemplateHandlerBase):
def dump(self, out_stream):
- out_stream.write('Group template: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Group template: {0}'.format(out_stream.node_style(self._model.name)))
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
self._topology.dump(self._model.interface_templates, out_stream,
title='Interface Templates')
if self._model.node_templates:
- out_stream.write('Member node templates: {0}'.format(', '.join(
+ out_stream.write(u'Member node templates: {0}'.format(u', '.join(
(str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
def coerce(self, **kwargs):
@@ -303,7 +304,7 @@
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Interface type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Interface type: {0}'.format(out_stream.type_style(
self._model.type.name)))
self._topology.dump(self._model.inputs, out_stream, title='Inputs')
self._topology.dump(self._model.operation_templates, out_stream,
@@ -332,11 +333,11 @@
class NodeTemplate(common.TemplateHandlerBase):
def dump(self, out_stream):
- out_stream.write('Node template: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Node template: {0}'.format(out_stream.node_style(self._model.name)))
with out_stream.indent():
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
self._topology.dump(self._model.attributes, out_stream, title='Attributes')
self._topology.dump(
@@ -391,17 +392,17 @@
class PolicyTemplate(common.TemplateHandlerBase):
def dump(self, out_stream):
- out_stream.write('Policy template: {0}'.format(out_stream.node_style(self._model.name)))
+ out_stream.write(u'Policy template: {0}'.format(out_stream.node_style(self._model.name)))
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
- out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
+ out_stream.write(u'Type: {0}'.format(out_stream.type_style(self._model.type.name)))
self._topology.dump(self._model.properties, out_stream, title='Properties')
if self._model.node_templates:
- out_stream.write('Target node templates: {0}'.format(', '.join(
+ out_stream.write(u'Target node templates: {0}'.format(u', '.join(
(str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
if self._model.group_templates:
- out_stream.write('Target group templates: {0}'.format(', '.join(
+ out_stream.write(u'Target group templates: {0}'.format(u', '.join(
(str(out_stream.node_style(v.name)) for v in self._model.group_templates))))
def coerce(self, **kwargs):
@@ -432,7 +433,7 @@
def dump(self, out_stream):
out_stream.write('Substitution template:')
with out_stream.indent():
- out_stream.write('Node type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Node type: {0}'.format(out_stream.type_style(
self._model.node_type.name)))
self._topology.dump(self._model.mappings, out_stream, title='Mappings')
@@ -453,7 +454,7 @@
node_template = self._model.capability_template.node_template
else:
node_template = self._model.requirement_template.node_template
- out_stream.write('{0} -> {1}.{2}'.format(
+ out_stream.write(u'{0} -> {1}.{2}'.format(
out_stream.node_style(self._model.name),
out_stream.node_style(node_template.name),
out_stream.node_style(self._model.capability_template.name
@@ -475,7 +476,7 @@
nodes = node_template.nodes
if len(nodes) == 0:
self._topology.report(
- 'mapping "{0}" refers to node template "{1}" but there are no node instances'.
+ u'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
@@ -493,8 +494,8 @@
def validate(self, **_):
if self._model.capability_template is None and self._model.requirement_template is None:
self._topology.report(
- 'mapping "{0}" refers to neither capability nor a requirement '
- 'in node template: {1}'.format(
+ u'mapping "{0}" refers to neither capability nor a requirement '
+ u'in node template: {1}'.format(
self._model.name, formatting.safe_repr(self._model.node_template.name)),
level=self._topology.Issue.BETWEEN_TYPES)
@@ -502,10 +503,10 @@
class RelationshipTemplate(common.TemplateHandlerBase):
def dump(self, out_stream):
if self._model.type is not None:
- out_stream.write('Relationship type: {0}'.format(out_stream.type_style(
+ out_stream.write(u'Relationship type: {0}'.format(out_stream.type_style(
self._model.type.name)))
else:
- out_stream.write('Relationship template: {0}'.format(
+ out_stream.write(u'Relationship template: {0}'.format(
out_stream.node_style(self._model.name)))
if self._model.description:
out_stream.write(out_stream.meta_style(self._model.description))
@@ -539,27 +540,27 @@
out_stream.write(out_stream.meta_style(self._model.description))
with out_stream.indent():
if self._model.implementation is not None:
- out_stream.write('Implementation: {0}'.format(
+ out_stream.write(u'Implementation: {0}'.format(
out_stream.literal_style(self._model.implementation)))
if self._model.dependencies:
- out_stream.write('Dependencies: {0}'.format(', '.join(
+ out_stream.write(u'Dependencies: {0}'.format(u', '.join(
(str(out_stream.literal_style(v)) for v in self._model.dependencies))))
self._topology.dump(self._model.inputs, out_stream, title='Inputs')
if self._model.executor is not None:
- out_stream.write('Executor: {0}'.format(
+ out_stream.write(u'Executor: {0}'.format(
out_stream.literal_style(self._model.executor)))
if self._model.max_attempts is not None:
- out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Max attempts: {0}'.format(out_stream.literal_style(
self._model.max_attempts)))
if self._model.retry_interval is not None:
- out_stream.write('Retry interval: {0}'.format(
+ out_stream.write(u'Retry interval: {0}'.format(
out_stream.literal_style(self._model.retry_interval)))
if self._model.plugin_specification is not None:
- out_stream.write('Plugin specification: {0}'.format(
+ out_stream.write(u'Plugin specification: {0}'.format(
out_stream.literal_style(self._model.plugin_specification.name)))
self._topology.dump(self._model.configurations, out_stream, title='Configuration')
if self._model.function is not None:
- out_stream.write('Function: {0}'.format(out_stream.literal_style(
+ out_stream.write(u'Function: {0}'.format(out_stream.literal_style(
self._model.function)))
def coerce(self, **kwargs):
diff --git a/aria/orchestrator/topology/topology.py b/aria/orchestrator/topology/topology.py
index f86c9dd..ef5322e 100644
--- a/aria/orchestrator/topology/topology.py
+++ b/aria/orchestrator/topology/topology.py
@@ -104,7 +104,7 @@
# if model is empty, no need to print out the section name
if model and title:
- out_stream.write('{0}:'.format(title))
+ out_stream.write(u'{0}:'.format(title))
if isinstance(model, dict):
if str(out_stream):
@@ -133,18 +133,18 @@
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)))
+ out_stream.write(u'{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(
+ out_stream.write(u'-> {0} ({1})'.format(
styled_relationship_name,
out_stream.type_style(relationship_model.type.name)))
else:
- out_stream.write('-> {0}'.format(styled_relationship_name))
+ out_stream.write(u'-> {0}'.format(styled_relationship_name))
with out_stream.indent(3):
self._dump_graph_node(out_stream,
relationship_model.target_node,
diff --git a/aria/orchestrator/workflows/api/task.py b/aria/orchestrator/workflows/api/task.py
index 6ce4a00..67adc0b 100644
--- a/aria/orchestrator/workflows/api/task.py
+++ b/aria/orchestrator/workflows/api/task.py
@@ -78,7 +78,7 @@
:vartype retry_interval: float
"""
- NAME_FORMAT = '{interface}:{operation}@{type}:{name}'
+ NAME_FORMAT = u'{interface}:{operation}@{type}:{name}'
def __init__(self,
actor,
@@ -112,8 +112,8 @@
# interface/operation.
if not has_operation(actor, interface_name, operation_name):
raise exceptions.OperationNotFoundException(
- 'Could not find operation "{operation_name}" on interface '
- '"{interface_name}" for {actor_type} "{actor.name}"'.format(
+ u'Could not find operation "{operation_name}" on interface '
+ u'"{interface_name}" for {actor_type} "{actor.name}"'.format(
operation_name=operation_name,
interface_name=interface_name,
actor_type=type(actor).__name__.lower(),
@@ -149,8 +149,8 @@
elif isinstance(actor, models.Relationship):
self._context_cls = context.operation.RelationshipOperationContext
else:
- raise exceptions.TaskCreationException('Could not create valid context for '
- '{actor.__class__}'.format(actor=actor))
+ raise exceptions.TaskCreationException(u'Could not create valid context for '
+ u'{actor.__class__}'.format(actor=actor))
def __repr__(self):
return self.name
diff --git a/aria/orchestrator/workflows/api/task_graph.py b/aria/orchestrator/workflows/api/task_graph.py
index eb3967b..0debbb7 100644
--- a/aria/orchestrator/workflows/api/task_graph.py
+++ b/aria/orchestrator/workflows/api/task_graph.py
@@ -52,7 +52,7 @@
self._graph = DiGraph()
def __repr__(self):
- return '{name}(id={self._id}, name={self.name}, graph={self._graph!r})'.format(
+ return u'{name}(id={self._id}, name={self.name}, graph={self._graph!r})'.format( # pylint: disable=redundant-keyword-arg
name=self.__class__.__name__, self=self)
@property
@@ -94,7 +94,7 @@
``dependent_task`` is not in the graph
"""
if not self.has_tasks(dependent_task):
- raise TaskNotInGraphError('Task id: {0}'.format(dependent_task.id))
+ raise TaskNotInGraphError(u'Task id: {0}'.format(dependent_task.id))
for _, dependency_id in self._graph.out_edges(dependent_task.id):
yield self.get_task(dependency_id)
@@ -107,7 +107,7 @@
``dependency_task`` is not in the graph
"""
if not self.has_tasks(dependency_task):
- raise TaskNotInGraphError('Task id: {0}'.format(dependency_task.id))
+ raise TaskNotInGraphError(u'Task id: {0}'.format(dependency_task.id))
for dependent_id, _ in self._graph.in_edges(dependency_task.id):
yield self.get_task(dependent_id)
@@ -122,7 +122,7 @@
the graph with the given ID
"""
if not self._graph.has_node(task_id):
- raise TaskNotInGraphError('Task id: {0}'.format(task_id))
+ raise TaskNotInGraphError(u'Task id: {0}'.format(task_id))
data = self._graph.node[task_id]
return data['task']
diff --git a/aria/orchestrator/workflows/builtin/execute_operation.py b/aria/orchestrator/workflows/builtin/execute_operation.py
index 949f864..256927c 100644
--- a/aria/orchestrator/workflows/builtin/execute_operation.py
+++ b/aria/orchestrator/workflows/builtin/execute_operation.py
@@ -60,7 +60,7 @@
for node in ctx.nodes:
if node.id not in filtered_node_ids:
subgraphs[node.id] = ctx.task_graph(
- name='execute_operation_stub_{0}'.format(node.id))
+ name=u'execute_operation_stub_{0}'.format(node.id))
# registering actual tasks to sequences
for node in filtered_nodes:
diff --git a/aria/orchestrator/workflows/core/engine.py b/aria/orchestrator/workflows/core/engine.py
index 0d7d2ae..5099041 100644
--- a/aria/orchestrator/workflows/core/engine.py
+++ b/aria/orchestrator/workflows/core/engine.py
@@ -28,7 +28,7 @@
from .. import exceptions
from ..executor.base import StubTaskExecutor
# Import required so all signals are registered
-from . import events_handler # pylint: disable=unused-import
+from . import events_handler # pylint: disable=unused-import
class Engine(logger.LoggerMixin):
diff --git a/aria/orchestrator/workflows/core/events_handler.py b/aria/orchestrator/workflows/core/events_handler.py
index 473475e..067d0c3 100644
--- a/aria/orchestrator/workflows/core/events_handler.py
+++ b/aria/orchestrator/workflows/core/events_handler.py
@@ -166,5 +166,5 @@
def _log_tried_to_cancel_execution_but_it_already_ended(workflow_context, status):
workflow_context.logger.info(
- "'{workflow_name}' workflow execution {status} before the cancel request"
- "was fully processed".format(workflow_name=workflow_context.workflow_name, status=status))
+ u"'{workflow_name}' workflow execution {status} before the cancel request"
+ u"was fully processed".format(workflow_name=workflow_context.workflow_name, status=status))
diff --git a/aria/orchestrator/workflows/core/graph_compiler.py b/aria/orchestrator/workflows/core/graph_compiler.py
index 81543d5..83fbfea 100644
--- a/aria/orchestrator/workflows/core/graph_compiler.py
+++ b/aria/orchestrator/workflows/core/graph_compiler.py
@@ -90,11 +90,11 @@
@staticmethod
def _start_graph_suffix(api_id):
- return '{0}-Start'.format(api_id)
+ return u'{0}-Start'.format(api_id)
@staticmethod
def _end_graph_suffix(api_id):
- return '{0}-End'.format(api_id)
+ return u'{0}-End'.format(api_id)
@staticmethod
def _get_non_dependent_tasks(execution):
diff --git a/aria/orchestrator/workflows/events_logging.py b/aria/orchestrator/workflows/events_logging.py
index 9eee1e1..1099091 100644
--- a/aria/orchestrator/workflows/events_logging.py
+++ b/aria/orchestrator/workflows/events_logging.py
@@ -24,7 +24,7 @@
def _get_task_name(task):
if isinstance(task.actor, modeling.model_bases.service_instance.RelationshipBase):
- return '{source_node.name}->{target_node.name}'.format(
+ return u'{source_node.name}->{target_node.name}'.format(
source_node=task.actor.source_node, target_node=task.actor.target_node)
else:
return task.actor.name
@@ -40,7 +40,7 @@
suffix = 'has no implementation'
logger = ctx.logger.debug
- logger('{name} {task.interface_name}.{task.operation_name} {suffix}'.format(
+ logger(u'{name} {task.interface_name}.{task.operation_name} {suffix}'.format(
name=_get_task_name(ctx.task), task=ctx.task, suffix=suffix))
@@ -48,38 +48,38 @@
def _success_task_handler(ctx, **kwargs):
if not ctx.task.function:
return
- ctx.logger.info('{name} {task.interface_name}.{task.operation_name} successful'
+ ctx.logger.info(u'{name} {task.interface_name}.{task.operation_name} successful'
.format(name=_get_task_name(ctx.task), task=ctx.task))
@events.on_failure_task_signal.connect
def _failure_operation_handler(ctx, traceback, **kwargs):
ctx.logger.error(
- '{name} {task.interface_name}.{task.operation_name} failed'
+ u'{name} {task.interface_name}.{task.operation_name} failed'
.format(name=_get_task_name(ctx.task), task=ctx.task), extra=dict(traceback=traceback)
)
@events.start_workflow_signal.connect
def _start_workflow_handler(context, **kwargs):
- context.logger.info("Starting '{ctx.workflow_name}' workflow execution".format(ctx=context))
+ context.logger.info(u"Starting '{ctx.workflow_name}' workflow execution".format(ctx=context))
@events.on_failure_workflow_signal.connect
def _failure_workflow_handler(context, **kwargs):
- context.logger.info("'{ctx.workflow_name}' workflow execution failed".format(ctx=context))
+ context.logger.info(u"'{ctx.workflow_name}' workflow execution failed".format(ctx=context))
@events.on_success_workflow_signal.connect
def _success_workflow_handler(context, **kwargs):
- context.logger.info("'{ctx.workflow_name}' workflow execution succeeded".format(ctx=context))
+ context.logger.info(u"'{ctx.workflow_name}' workflow execution succeeded".format(ctx=context))
@events.on_cancelled_workflow_signal.connect
def _cancel_workflow_handler(context, **kwargs):
- context.logger.info("'{ctx.workflow_name}' workflow execution canceled".format(ctx=context))
+ context.logger.info(u"'{ctx.workflow_name}' workflow execution canceled".format(ctx=context))
@events.on_cancelling_workflow_signal.connect
def _cancelling_workflow_handler(context, **kwargs):
- context.logger.info("Cancelling '{ctx.workflow_name}' workflow execution".format(ctx=context))
+ context.logger.info(u"Cancelling '{ctx.workflow_name}' workflow execution".format(ctx=context))
diff --git a/aria/orchestrator/workflows/exceptions.py b/aria/orchestrator/workflows/exceptions.py
index 2a1d6b1..6fce81c 100644
--- a/aria/orchestrator/workflows/exceptions.py
+++ b/aria/orchestrator/workflows/exceptions.py
@@ -55,10 +55,10 @@
Describes the error in detail
"""
return (
- 'Command "{error.command}" executed with an error.{0}'
- 'code: {error.return_code}{0}'
- 'error: {error.stderr}{0}'
- 'output: {error.stdout}'.format(os.linesep, error=self))
+ u'Command "{error.command}" executed with an error.{0}'
+ u'code: {error.return_code}{0}'
+ u'error: {error.stderr}{0}'
+ u'output: {error.stdout}'.format(os.linesep, error=self))
class AriaEngineError(exceptions.AriaError):
diff --git a/aria/orchestrator/workflows/executor/celery.py b/aria/orchestrator/workflows/executor/celery.py
index a2b3513..aab84ec 100644
--- a/aria/orchestrator/workflows/executor/celery.py
+++ b/aria/orchestrator/workflows/executor/celery.py
@@ -89,7 +89,7 @@
exception = async_result.result
except BaseException as e:
exception = RuntimeError(
- 'Could not de-serialize exception of task {0} --> {1}: {2}'
+ u'Could not de-serialize exception of task {0} --> {1}: {2}'
.format(task.name, type(e).__name__, str(e)))
self._task_failed(task, exception=exception)
diff --git a/aria/orchestrator/workflows/executor/dry.py b/aria/orchestrator/workflows/executor/dry.py
index 9314e5d..bdb0eaf 100644
--- a/aria/orchestrator/workflows/executor/dry.py
+++ b/aria/orchestrator/workflows/executor/dry.py
@@ -22,7 +22,7 @@
from . import base
-class DryExecutor(base.BaseExecutor): # pylint: disable=abstract-method
+class DryExecutor(base.BaseExecutor): # pylint: disable=abstract-method
"""
Dry task executor: prints task information without causing any side effects.
"""
@@ -33,11 +33,11 @@
ctx.task.started_at = datetime.utcnow()
ctx.task.status = ctx.task.STARTED
- dry_msg = '<dry> {name} {task.interface_name}.{task.operation_name} {suffix}'
+ dry_msg = u'<dry> {name} {task.interface_name}.{task.operation_name} {suffix}'
logger = ctx.logger.info if ctx.task.function else ctx.logger.debug
if hasattr(ctx.task.actor, 'source_node'):
- name = '{source_node.name}->{target_node.name}'.format(
+ name = u'{source_node.name}->{target_node.name}'.format(
source_node=ctx.task.actor.source_node, target_node=ctx.task.actor.target_node)
else:
name = ctx.task.actor.name
diff --git a/aria/orchestrator/workflows/executor/process.py b/aria/orchestrator/workflows/executor/process.py
index 185f15f..4143127 100644
--- a/aria/orchestrator/workflows/executor/process.py
+++ b/aria/orchestrator/workflows/executor/process.py
@@ -23,8 +23,8 @@
import sys
# As part of the process executor implementation, subprocess are started with this module as their
-# entry point. We thus remove this module's directory from the python path if it happens to be
-# there
+# entry point. We thus remove this module's directory from the Python path if it happens to be
+# there.
from collections import namedtuple
@@ -201,11 +201,11 @@
break
request_handler = self._request_handlers.get(request_type)
if not request_handler:
- raise RuntimeError('Invalid request type: {0}'.format(request_type))
+ raise RuntimeError(u'Invalid request type: {0}'.format(request_type))
task_id = request['task_id']
request_handler(task_id=task_id, request=request, response=response)
except BaseException as e:
- self.logger.debug('Error in process executor listener: {0}'.format(e))
+ self.logger.debug(u'Error in process executor listener: {0}'.format(e))
@contextlib.contextmanager
def _accept_request(self):
diff --git a/aria/orchestrator/workflows/executor/thread.py b/aria/orchestrator/workflows/executor/thread.py
index 170620e..5786a04 100644
--- a/aria/orchestrator/workflows/executor/thread.py
+++ b/aria/orchestrator/workflows/executor/thread.py
@@ -43,7 +43,7 @@
self._queue = Queue.Queue()
self._pool = []
for i in range(pool_size):
- name = 'ThreadExecutor-{index}'.format(index=i+1)
+ name = 'ThreadExecutor-{0:d}'.format(i+1)
thread = threading.Thread(target=self._processor, name=name)
thread.daemon = True
thread.start()
diff --git a/aria/parser/consumption/context.py b/aria/parser/consumption/context.py
index 9164984..91807a4 100644
--- a/aria/parser/consumption/context.py
+++ b/aria/parser/consumption/context.py
@@ -83,14 +83,14 @@
try:
self.out.write(string)
except UnicodeEncodeError:
- self.out.write(string.encode('utf8'))
+ self.out.write(string.encode('utf-8'))
def has_arg_switch(self, name):
- name = '--%s' % name
+ name = '--{0}'.format(name)
return name in self.args
def get_arg_value(self, name, default=None):
- name = '--%s=' % name
+ name = '--{0}='.format(name)
for arg in self.args:
if arg.startswith(name):
return arg[len(name):]
diff --git a/aria/parser/consumption/inputs.py b/aria/parser/consumption/inputs.py
index fe7e192..55f6e92 100644
--- a/aria/parser/consumption/inputs.py
+++ b/aria/parser/consumption/inputs.py
@@ -46,7 +46,7 @@
if not isinstance(inputs, dict):
self.context.validation.report(
- 'Inputs consumer: inputs are not a dict: %s' % safe_repr(inputs))
+ u'Inputs consumer: inputs are not a dict: {0}'.format(safe_repr(inputs)))
return
for name, value in inputs.iteritems():
diff --git a/aria/parser/consumption/presentation.py b/aria/parser/consumption/presentation.py
index 542b3f0..4c4e014 100644
--- a/aria/parser/consumption/presentation.py
+++ b/aria/parser/consumption/presentation.py
@@ -13,15 +13,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
-from ...utils.threading import FixedThreadPoolExecutor
-from ...utils.formatting import json_dumps, yaml_dumps
+from ...utils.formatting import (json_dumps, yaml_dumps)
from ..loading import UriLocation
-from ..reading import AlreadyReadException
from ..presentation import PresenterNotFoundError
from .consumer import Consumer
+PRESENTATION_CACHE = {}
+CANONICAL_LOCATION_CACHE = {}
+
+
class Read(Consumer):
"""
Reads the presentation, handling imports recursively.
@@ -31,7 +32,7 @@
instances.
It supports agnostic raw data composition for presenters that have
- ``_get_import_locations`` and ``_merge_import``.
+ ``_get_import_locations``, ``_validate_import``, and ``_merge_import``.
To improve performance, loaders are called asynchronously on separate threads.
@@ -39,39 +40,25 @@
cycle, for example if the agnostic raw data has dependencies that must also be parsed.
"""
+ def __init__(self, context):
+ super(Read, self).__init__(context)
+ self._cache = {}
+
def consume(self):
- if self.context.presentation.location is None:
- self.context.validation.report('Presentation consumer: missing location')
- return
+ # Present the main location and all imports recursively
+ main_result, all_results = self._present_all()
- presenter = None
- imported_presentations = None
+ # Merge presentations
+ main_result.merge(all_results, self.context)
- executor = FixedThreadPoolExecutor(size=self.context.presentation.threads,
- timeout=self.context.presentation.timeout)
- executor.print_exceptions = self.context.presentation.print_exceptions
- try:
- presenter = self._present(self.context.presentation.location, None, None, executor)
- executor.drain()
+ # Cache merged presentations
+ if self.context.presentation.cache:
+ for result in all_results:
+ result.cache()
- # Handle exceptions
- for e in executor.exceptions:
- self._handle_exception(e)
-
- imported_presentations = executor.returns
- finally:
- executor.close()
-
- # Merge imports
- if (imported_presentations is not None) and hasattr(presenter, '_merge_import'):
- for imported_presentation in imported_presentations:
- okay = True
- if hasattr(presenter, '_validate_import'):
- okay = presenter._validate_import(self.context, imported_presentation)
- if okay:
- presenter._merge_import(imported_presentation)
-
- self.context.presentation.presenter = presenter
+ self.context.presentation.presenter = main_result.presentation
+ if main_result.canonical_location is not None:
+ self.context.presentation.location = main_result.canonical_location
def dump(self):
if self.context.has_arg_switch('yaml'):
@@ -86,52 +73,216 @@
self.context.presentation.presenter._dump(self.context)
def _handle_exception(self, e):
- if isinstance(e, AlreadyReadException):
+ if isinstance(e, _CancelPresentation):
return
super(Read, self)._handle_exception(e)
- def _present(self, location, origin_location, presenter_class, executor):
+ def _present_all(self):
+ """
+ Presents all locations, including all nested imports, from the main location. Uses a thread
+ pool executor for best performance.
+
+ The main presentation is returned separately for easier access.
+ """
+
+ location = self.context.presentation.location
+
+ if location is None:
+ self.context.validation.report('Read consumer: missing location')
+ return
+
+ executor = self.context.presentation.create_executor()
+ try:
+ # This call may recursively submit tasks to the executor if there are imports
+ main_result = self._present(location, None, None, executor)
+
+ # Wait for all tasks to complete
+ executor.drain()
+
+ # Handle exceptions
+ for e in executor.exceptions:
+ self._handle_exception(e)
+
+ all_results = executor.returns or []
+ finally:
+ executor.close()
+
+ all_results.insert(0, main_result)
+
+ return main_result, all_results
+
+ def _present(self, location, origin_canonical_location, origin_presenter_class, executor):
+ """
+ Presents a single location. If the location has imports, those are submitted to the thread
+ pool executor.
+
+ Supports a presentation cache based on the canonical location as cache key.
+ """
+
# Link the context to this thread
self.context.set_thread_local()
- raw = self._read(location, origin_location)
-
- if self.context.presentation.presenter_class is not None:
- # The presenter class we specified in the context overrides everything
- presenter_class = self.context.presentation.presenter_class
+ # Canonicalize the location
+ if self.context.reading.reader is None:
+ loader, canonical_location = self._create_loader(location, origin_canonical_location)
else:
+ # If a reader is specified in the context then we skip loading
+ loader = None
+ canonical_location = location
+
+ # Skip self imports
+ if canonical_location == origin_canonical_location:
+ raise _CancelPresentation()
+
+ if self.context.presentation.cache:
+ # Is the presentation in the global cache?
try:
- presenter_class = self.context.presentation.presenter_source.get_presenter(raw)
- except PresenterNotFoundError:
- if presenter_class is None:
- raise
- # We'll use the presenter class we were given (from the presenter that imported us)
- if presenter_class is None:
- raise PresenterNotFoundError('presenter not found')
+ presentation = PRESENTATION_CACHE[canonical_location]
+ return _Result(presentation, canonical_location, origin_canonical_location)
+ except KeyError:
+ pass
- presentation = presenter_class(raw=raw)
+ try:
+ # Is the presentation in the local cache?
+ presentation = self._cache[canonical_location]
+ return _Result(presentation, canonical_location, origin_canonical_location)
+ except KeyError:
+ pass
- if presentation is not None and hasattr(presentation, '_link_locators'):
- presentation._link_locators()
+ # Create and cache new presentation
+ presentation = self._create_presentation(canonical_location, loader, origin_presenter_class)
+ self._cache[canonical_location] = presentation
# Submit imports to executor
if hasattr(presentation, '_get_import_locations'):
import_locations = presentation._get_import_locations(self.context)
if import_locations:
for import_location in import_locations:
- # The imports inherit the parent presenter class and use the current location as
- # their origin location
import_location = UriLocation(import_location)
- executor.submit(self._present, import_location, location, presenter_class,
- executor)
+ executor.submit(self._present, import_location, canonical_location,
+ presentation.__class__, executor)
+
+ return _Result(presentation, canonical_location, origin_canonical_location)
+
+ def _create_loader(self, location, origin_canonical_location):
+ loader = self.context.loading.loader_source.get_loader(self.context.loading, location,
+ origin_canonical_location)
+
+ if origin_canonical_location is not None:
+ # The cache key is is a combination of the canonical location of the origin, which is
+ # globally absolute and never changes, and our location, which might be relative to
+ # the origin's location
+ cache_key = (origin_canonical_location, location)
+ try:
+ canonical_location = CANONICAL_LOCATION_CACHE[cache_key]
+ return loader, canonical_location
+ except KeyError:
+ pass
+ else:
+ cache_key = None
+
+ try:
+ canonical_location = loader.get_canonical_location()
+ except NotImplementedError:
+ canonical_location = None
+
+ # Because retrieving the canonical location can be costly, we will try to cache it
+ if cache_key is not None:
+ CANONICAL_LOCATION_CACHE[cache_key] = canonical_location
+
+ return loader, canonical_location
+
+ def _create_presentation(self, canonical_location, loader, default_presenter_class):
+ # The reader we specified in the context will override
+ reader = self.context.reading.reader
+
+ if reader is None:
+ # Read raw data from loader
+ reader = self.context.reading.reader_source.get_reader(self.context.reading,
+ canonical_location, loader)
+
+ raw = reader.read()
+
+ # Wrap raw data in presenter class
+ if self.context.presentation.presenter_class is not None:
+ # The presenter class we specified in the context will override
+ presenter_class = self.context.presentation.presenter_class
+ else:
+ try:
+ presenter_class = self.context.presentation.presenter_source.get_presenter(raw)
+ except PresenterNotFoundError:
+ if default_presenter_class is None:
+ raise
+ else:
+ presenter_class = default_presenter_class
+
+ if presenter_class is None:
+ raise PresenterNotFoundError(u'presenter not found: {0}'.format(canonical_location))
+
+ presentation = presenter_class(raw=raw)
+
+ if hasattr(presentation, '_link_locators'):
+ presentation._link_locators()
return presentation
- def _read(self, location, origin_location):
- if self.context.reading.reader is not None:
- return self.context.reading.reader.read()
- loader = self.context.loading.loader_source.get_loader(self.context.loading, location,
- origin_location)
- reader = self.context.reading.reader_source.get_reader(self.context.reading, location,
- loader)
- return reader.read()
+
+class _Result(object):
+ """
+ The result of a :meth:`Read._present` call. Contains the read presentation itself, as well as
+ extra fields to help caching and keep track of merging.
+ """
+
+ def __init__(self, presentation, canonical_location, origin_canonical_location):
+ self.presentation = presentation
+ self.canonical_location = canonical_location
+ self.origin_canonical_location = origin_canonical_location
+ self.merged = False
+
+ def get_imports(self, results):
+ imports = []
+
+ def has_import(result):
+ for i in imports:
+ if i.canonical_location == result.canonical_location:
+ return True
+ return False
+
+ for result in results:
+ if result.origin_canonical_location == self.canonical_location:
+ if not has_import(result):
+ imports.append(result)
+ return imports
+
+ def merge(self, results, context):
+ # Make sure to only merge each presentation once
+ if self.merged:
+ return
+ self.merged = True
+ for result in results:
+ if result.presentation == self.presentation:
+ result.merged = True
+
+ for result in self.get_imports(results):
+ # Make sure import is merged
+ result.merge(results, context)
+
+ # Validate import
+ if hasattr(self.presentation, '_validate_import'):
+ if not self.presentation._validate_import(context, result.presentation):
+ # _validate_import will report an issue if invalid
+ continue
+
+ # Merge import
+ if hasattr(self.presentation, '_merge_import'):
+ self.presentation._merge_import(result.presentation)
+
+ def cache(self):
+ if not self.merged:
+ # Only merged presentations can be cached
+ return
+ PRESENTATION_CACHE[self.canonical_location] = self.presentation
+
+
+class _CancelPresentation(Exception):
+ pass
diff --git a/aria/parser/consumption/validation.py b/aria/parser/consumption/validation.py
index a7bc3b8..dd145ce 100644
--- a/aria/parser/consumption/validation.py
+++ b/aria/parser/consumption/validation.py
@@ -24,7 +24,7 @@
def consume(self):
if self.context.presentation.presenter is None:
- self.context.validation.report('Validation consumer: missing presenter')
+ self.context.validation.report('Validate consumer: missing presenter')
return
self.context.presentation.presenter._validate(self.context)
diff --git a/aria/parser/loading/file.py b/aria/parser/loading/file.py
index a02bd69..719e770 100644
--- a/aria/parser/loading/file.py
+++ b/aria/parser/loading/file.py
@@ -38,27 +38,31 @@
self._file = codecs.open(self.path, mode='r', encoding=self.encoding, buffering=1)
except IOError as e:
if e.errno == 2:
- raise DocumentNotFoundException('file not found: "%s"' % self.path, cause=e)
+ raise DocumentNotFoundException(u'file not found: "{0}"'.format(self.path), cause=e)
else:
- raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+ raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
except Exception as e:
- raise LoaderException('file error: "%s"' % self.path, cause=e)
+ raise LoaderException(u'file error: "{0}"'.format(self.path), cause=e)
def close(self):
if self._file is not None:
try:
self._file.close()
except IOError as e:
- raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+ raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
except Exception as e:
- raise LoaderException('file error: "%s"' % self.path, cause=e)
+ raise LoaderException(u'file error: "{0}"'.format(self.path), cause=e)
+ self._file = None
def load(self):
if self._file is not None:
try:
return self._file.read()
except IOError as e:
- raise LoaderException('file I/O error: "%s"' % self.path, cause=e)
+ raise LoaderException(u'file I/O error: "{0}"'.format(self.path), cause=e)
except Exception as e:
- raise LoaderException('file error %s' % self.path, cause=e)
+ raise LoaderException(u'file error {0}'.format(self.path), cause=e)
return None
+
+ def get_canonical_location(self):
+ raise NotImplementedError
diff --git a/aria/parser/loading/literal.py b/aria/parser/loading/literal.py
index 7865008..208ab53 100644
--- a/aria/parser/loading/literal.py
+++ b/aria/parser/loading/literal.py
@@ -29,3 +29,6 @@
def load(self):
return self.location.content
+
+ def get_canonical_location(self):
+ return self.location
diff --git a/aria/parser/loading/loader.py b/aria/parser/loading/loader.py
index e1abfbf..92b44ee 100644
--- a/aria/parser/loading/loader.py
+++ b/aria/parser/loading/loader.py
@@ -20,8 +20,14 @@
Loaders extract a document by consuming a document source.
- Though the extracted document is often textual (a string or string-like
- data), loaders may provide any format.
+ Though the extracted document is often textual (a string or string-like data), loaders may
+ provide any format.
+
+ Loaders can also calculate the "canonical location" of what they are loading, which is a
+ globally unique reference that can be used as a cache key. Examples include absolute file paths
+ and de-relativized URIs. Note that calculating the canonical location can be very costly
+ (for example, hitting various URLs until finding an existing one), and thus it's a good idea
+ to cache the canonical location per loader context.
"""
def open(self):
@@ -32,3 +38,6 @@
def load(self):
raise NotImplementedError
+
+ def get_canonical_location(self):
+ raise NotImplementedError
diff --git a/aria/parser/loading/location.py b/aria/parser/loading/location.py
index 902e856..255f650 100644
--- a/aria/parser/loading/location.py
+++ b/aria/parser/loading/location.py
@@ -27,9 +27,6 @@
an appropriate :class:`~aria.parser.loading.Loader`.
"""
- def is_equivalent(self, location):
- raise NotImplementedError
-
@property
def prefix(self):
return None
@@ -47,15 +44,12 @@
def __init__(self, uri):
self.uri = uri
- def is_equivalent(self, location):
- return isinstance(location, UriLocation) and (location.uri == self.uri)
-
@property
def prefix(self):
prefix = os.path.dirname(self.uri)
if prefix and (as_file(prefix) is None):
- # Yes, it's weird, but dirname handles URIs,
- # too: http://stackoverflow.com/a/35616478/849021
+ # Yes, it's weird, but dirname handles URIs, too:
+ # http://stackoverflow.com/a/35616478/849021
# We just need to massage it with a trailing slash
prefix += '/'
return prefix
@@ -63,6 +57,12 @@
def __str__(self):
return self.uri
+ def __eq__(self, other):
+ return isinstance(other, UriLocation) and (other.uri == self.uri)
+
+ def __hash__(self):
+ return hash(self.uri)
+
class LiteralLocation(Location):
"""
@@ -75,8 +75,11 @@
self.content = content
self.name = name
- def is_equivalent(self, location):
- return isinstance(location, LiteralLocation) and (location.content == self.content)
-
def __str__(self):
- return '<%s>' % self.name
+ return u'<{0}>'.format(self.name)
+
+ def __eq__(self, other):
+ return isinstance(other, LiteralLocation) and (other.content == self.content)
+
+ def __hash__(self):
+ return hash(self.content)
diff --git a/aria/parser/loading/request.py b/aria/parser/loading/request.py
index a809347..199353a 100644
--- a/aria/parser/loading/request.py
+++ b/aria/parser/loading/request.py
@@ -57,19 +57,22 @@
try:
self._response = SESSION.get(self.uri, headers=self.headers)
except InvalidSchema as e:
- raise DocumentNotFoundException('document not found: "%s"' % self.uri, cause=e)
+ raise DocumentNotFoundException(u'document not found: "{0}"'.format(self.uri), cause=e)
except ConnectionError as e:
- raise LoaderException('request connection error: "%s"' % self.uri, cause=e)
+ raise LoaderException(u'request connection error: "{0}"'.format(self.uri), cause=e)
except Exception as e:
- raise LoaderException('request error: "%s"' % self.uri, cause=e)
+ raise LoaderException(u'request error: "{0}"'.format(self.uri), cause=e)
status = self._response.status_code
if status == 404:
self._response = None
- raise DocumentNotFoundException('document not found: "%s"' % self.uri)
+ raise DocumentNotFoundException(u'document not found: "{0}"'.format(self.uri))
elif status != 200:
self._response = None
- raise LoaderException('request error %d: "%s"' % (status, self.uri))
+ raise LoaderException(u'request error {0:d}: "{1}"'.format(status, self.uri))
+
+ def get_canonical_location(self):
+ raise NotImplementedError
class RequestTextLoader(RequestLoader):
@@ -81,8 +84,11 @@
if self._response is not None:
try:
if self._response.encoding is None:
- self._response.encoding = 'utf8'
+ self._response.encoding = 'utf-8'
return self._response.text
except Exception as e:
- raise LoaderException('request error: %s' % self.uri, cause=e)
+ raise LoaderException(u'request error: {0}'.format(self.uri), cause=e)
return None
+
+ def get_canonical_location(self):
+ raise NotImplementedError
diff --git a/aria/parser/loading/uri.py b/aria/parser/loading/uri.py
index a5a18e6..83f5ced 100644
--- a/aria/parser/loading/uri.py
+++ b/aria/parser/loading/uri.py
@@ -20,6 +20,7 @@
from ...utils.collections import StrictList
from ...utils.uris import as_file
from .loader import Loader
+from .location import UriLocation
from .file import FileTextLoader
from .request import RequestTextLoader
from .exceptions import DocumentNotFoundException
@@ -44,6 +45,7 @@
self.location = location
self._prefixes = StrictList(value_class=basestring)
self._loader = None
+ self._canonical_location = None
def add_prefix(prefix):
if prefix and (prefix not in self._prefixes):
@@ -60,23 +62,27 @@
add_prefixes(parser.uri_loader_prefix())
def open(self):
- try:
- self._open(self.location.uri)
- return
- except DocumentNotFoundException:
- # Try prefixes in order
- for prefix in self._prefixes:
- prefix_as_file = as_file(prefix)
- if prefix_as_file is not None:
- uri = os.path.join(prefix_as_file, self.location.uri)
- else:
- uri = urljoin(prefix, self.location.uri)
- try:
- self._open(uri)
- return
- except DocumentNotFoundException:
- pass
- raise DocumentNotFoundException('document not found at URI: "%s"' % self.location)
+ if self._loader is not None:
+ self._loader.open()
+ else:
+ try:
+ self._open(self.location.uri)
+ return
+ except DocumentNotFoundException:
+ # Try prefixes in order
+ for prefix in self._prefixes:
+ prefix_as_file = as_file(prefix)
+ if prefix_as_file is not None:
+ uri = os.path.join(prefix_as_file, self.location.uri)
+ else:
+ uri = urljoin(prefix, self.location.uri)
+ try:
+ self._open(uri)
+ return
+ except DocumentNotFoundException:
+ pass
+ raise DocumentNotFoundException(u'document not found at URI: "{0}"'
+ .format(self.location))
def close(self):
if self._loader is not None:
@@ -85,6 +91,11 @@
def load(self):
return self._loader.load() if self._loader is not None else None
+ def get_canonical_location(self):
+ self.open()
+ self.close()
+ return self._canonical_location
+
def _open(self, uri):
the_file = as_file(uri)
if the_file is not None:
@@ -94,4 +105,4 @@
loader = RequestTextLoader(self.context, uri)
loader.open() # might raise an exception
self._loader = loader
- self.location.uri = uri
+ self._canonical_location = UriLocation(uri)
diff --git a/aria/parser/presentation/__init__.py b/aria/parser/presentation/__init__.py
index 5633e7b..aa5439c 100644
--- a/aria/parser/presentation/__init__.py
+++ b/aria/parser/presentation/__init__.py
@@ -69,6 +69,7 @@
.. autosummary::
:nosignatures:
+ aria.parser.presentation.not_negative_validator
aria.parser.presentation.type_validator
aria.parser.presentation.list_type_validator
aria.parser.presentation.list_length_validator
@@ -93,19 +94,19 @@
aria.parser.presentation.report_issue_for_circular_type_hierarchy
"""
-from .exceptions import PresenterException, PresenterNotFoundError
+from .exceptions import (PresenterException, PresenterNotFoundError)
from .context import PresentationContext
from .presenter import Presenter
-from .presentation import Value, PresentationBase, Presentation, AsIsPresentation
-from .source import PresenterSource, DefaultPresenterSource
-from .null import NULL, none_to_null, null_to_none
+from .presentation import (Value, PresentationBase, Presentation, AsIsPresentation)
+from .source import (PresenterSource, DefaultPresenterSource)
+from .null import (NULL, none_to_null, null_to_none)
from .fields import (Field, has_fields, short_form_field, allow_unknown_fields, primitive_field,
primitive_list_field, primitive_dict_field, primitive_dict_unknown_fields,
object_field, object_list_field, object_dict_field,
object_sequenced_list_field, object_dict_unknown_fields, field_getter,
field_setter, field_validator)
-from .field_validators import (type_validator, list_type_validator, list_length_validator,
- derived_from_validator)
+from .field_validators import (not_negative_validator, type_validator, list_type_validator,
+ list_length_validator, derived_from_validator)
from .utils import (get_locator, parse_types_dict_names, validate_primitive, validate_no_short_form,
validate_no_unknown_fields, validate_known_fields, get_parent_presentation,
report_issue_for_unknown_type, report_issue_for_unknown_parent_type,
@@ -141,6 +142,7 @@
'field_getter',
'field_setter',
'field_validator',
+ 'not_negative_validator',
'type_validator',
'list_type_validator',
'list_length_validator',
diff --git a/aria/parser/presentation/context.py b/aria/parser/presentation/context.py
index 44a6f82..bf7123a 100644
--- a/aria/parser/presentation/context.py
+++ b/aria/parser/presentation/context.py
@@ -15,6 +15,7 @@
from .source import DefaultPresenterSource
+from ...utils.threading import (BlockingExecutor, FixedThreadPoolExecutor)
class PresentationContext(object):
@@ -29,11 +30,13 @@
:vartype presenter_source: ~aria.parser.presentation.PresenterSource
:ivar presenter_class: overrides ``presenter_source`` with a specific class
:vartype presenter_class: type
- :ivar import_profile: whether to import the profile by default (defaults to ``True``)
- :vartype import_profile: bool
- :ivar threads: number of threads to use when reading data
+ :ivar configuration: custom configurations for the presenter
+ :vartype configuration: {}
+ :ivar cache: whether to cache presentations (defaults to ``True``)
+ :vartype cache: bool
+ :ivar threads: number of threads to use when reading data (defaults to 8)
:vartype threads: int
- :ivar timeout: timeout in seconds for loading data
+ :ivar timeout: timeout in seconds for loading data (defaults to 10)
:vartype timeout: float
:ivar print_exceptions: whether to print exceptions while reading data
:vartype print_exceptions: bool
@@ -44,7 +47,8 @@
self.location = None
self.presenter_source = DefaultPresenterSource()
self.presenter_class = None # overrides
- self.import_profile = True
+ self.configuration = {}
+ self.cache = True
self.threads = 8 # reasonable default for networking multithreading
self.timeout = 10 # in seconds
self.print_exceptions = False
@@ -63,3 +67,12 @@
"""
return self.presenter._get_from_dict(*names) if self.presenter is not None else None
+
+ def create_executor(self):
+ if self.threads == 1:
+ # BlockingExecutor is much faster for the single-threaded case
+ return BlockingExecutor(print_exceptions=self.print_exceptions)
+
+ return FixedThreadPoolExecutor(size=self.threads,
+ timeout=self.timeout,
+ print_exceptions=self.print_exceptions)
diff --git a/aria/parser/presentation/field_validators.py b/aria/parser/presentation/field_validators.py
index aa04913..bd7bfdd 100644
--- a/aria/parser/presentation/field_validators.py
+++ b/aria/parser/presentation/field_validators.py
@@ -14,12 +14,29 @@
# limitations under the License.
+from ...utils.formatting import safe_repr
from ..validation import Issue
from .utils import (parse_types_dict_names, report_issue_for_unknown_type,
report_issue_for_parent_is_self, report_issue_for_unknown_parent_type,
report_issue_for_circular_type_hierarchy)
+def not_negative_validator(field, presentation, context):
+ """
+ Makes sure that the field is not negative.
+
+ Can be used with the :func:`field_validator` decorator.
+ """
+
+ field.default_validate(presentation, context)
+ value = getattr(presentation, field.name)
+ if (value is not None) and (value < 0):
+ context.validation.report(u'field "{0}" is negative: {1}'
+ .format(field.name, safe_repr(value)),
+ locator=presentation._get_child_locator(field.name),
+ level=Issue.FIELD)
+
+
def type_validator(type_name, *types_dict_names):
"""
Makes sure that the field refers to an existing type defined in the root presenter.
@@ -101,8 +118,10 @@
values = getattr(presentation, field.name)
if isinstance(values, list):
if len(values) != length:
- context.validation.report('field "%s" does not have exactly %d elements in "%s"'
- % (field.name, length, presentation._fullname),
+ context.validation.report(u'field "{0}" does not have exactly {1:d} elements in '
+ u'"{2}": {3}'
+ .format(field.name, length, presentation._fullname,
+ safe_repr(values)),
locator=presentation._get_child_locator(field.name),
level=Issue.FIELD)
diff --git a/aria/parser/presentation/fields.py b/aria/parser/presentation/fields.py
index 5c3e074..27fb236 100644
--- a/aria/parser/presentation/fields.py
+++ b/aria/parser/presentation/fields.py
@@ -338,7 +338,7 @@
if not isinstance(key, basestring):
raise TypeError('key must be a string')
if key not in self.__class__.FIELDS:
- raise KeyError('no \'%s\' property' % key)
+ raise KeyError('no \'{0}\' property'.format(key))
return getattr(self, key)
@@ -346,7 +346,7 @@
if not isinstance(key, basestring):
raise TypeError('key must be a string')
if key not in self.__class__.FIELDS:
- raise KeyError('no \'%s\' property' % key)
+ raise KeyError('no \'{0}\' property'.format(key))
return setattr(self, key, value)
@@ -354,7 +354,7 @@
if not isinstance(key, basestring):
raise TypeError('key must be a string')
if key not in self.__class__.FIELDS:
- raise KeyError('no \'%s\' property' % key)
+ raise KeyError('no \'{0}\' property'.format(key))
return setattr(self, key, None)
@@ -389,7 +389,7 @@
@property
def full_name(self):
- return 'field "%s" in "%s"' % (self.name, full_type_name(self.container_cls))
+ return u'field "{0}" in "{1}"'.format(self.name, full_type_name(self.container_cls))
@property
def full_cls_name(self):
@@ -420,7 +420,7 @@
if value is None:
return
- dumper = getattr(self, '_dump_%s' % self.field_variant)
+ dumper = getattr(self, '_dump_{0}'.format(self.field_variant))
dumper(context, value)
def default_get(self, presentation, context):
@@ -457,8 +457,8 @@
if value is None:
if self.required:
- raise InvalidValueError('required %s does not have a value' % self.full_name,
- locator=self.get_locator(raw))
+ raise InvalidValueError(u'required {0} does not have a value'
+ .format(self.full_name, locator=self.get_locator(raw)))
else:
return None
@@ -466,21 +466,20 @@
if self.allowed is not None:
if value not in self.allowed:
- raise InvalidValueError('%s is not %s'
- % (self.full_name, ' or '.join([safe_repr(v)
- for v in self.allowed])),
+ raise InvalidValueError(u'{0} is not {1}'
+ .format(self.full_name,
+ u' or '.join([safe_repr(v) for v in self.allowed])),
locator=self.get_locator(raw))
# Handle get according to variant
- getter = getattr(self, '_get_{field_variant}'.format(field_variant=self.field_variant),
- None)
+ getter = getattr(self, '_get_{0}'.format(self.field_variant), None)
if getter is None:
locator = self.get_locator(raw)
- location = (' @%s' % locator) if locator is not None else ''
- raise AttributeError('%s has unsupported field variant: "%s"%s'
- % (self.full_name, self.field_variant, location))
+ location = (u' @{0}'.format(locator)) if locator is not None else ''
+ raise AttributeError(u'{0} has unsupported field variant: "{1}"{2}'
+ .format(self.full_name, self.field_variant, location))
return getter(presentation, raw, value, context)
@@ -489,6 +488,9 @@
if is_short_form_field and not is_dict:
# Handle short form
value = raw
+ if value is None:
+ # An explicit null
+ value = NULL
elif is_dict:
if self.name in raw:
value = raw[self.name]
@@ -559,26 +561,28 @@
# primitive
def _get_primitive(self, presentation, raw, value, context):
- if (self.cls is not None and not isinstance(value, self.cls)
- and value is not None and value is not NULL):
+ if (self.cls is not None) and (not isinstance(value, self.cls)) \
+ and (value is not None):
try:
return self._coerce_primitive(value, context)
except ValueError as e:
- raise InvalidValueError('%s is not a valid "%s": %s' %
- (self.full_name, self.full_cls_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a valid "{1}": {2}'
+ .format(self.full_name, self.full_cls_name,
+ safe_repr(value)),
locator=self.get_locator(raw), cause=e)
return value
def _dump_primitive(self, context, value):
if hasattr(value, 'as_raw'):
value = as_raw(value)
- puts('%s: %s' % (self.name, context.style.literal_style(value)))
+ puts(u'{0}: {1}'.format(self.name, context.style.literal_style(value)))
# primitive list
def _get_primitive_list(self, presentation, raw, value, context):
if not isinstance(value, list):
- raise InvalidValueError('%s is not a list: %s' % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a list: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
primitive_list = value
if self.cls is not None:
@@ -587,26 +591,28 @@
primitive_list = []
for i, _ in enumerate(value):
primitive = value[i]
+ if primitive is None:
+ primitive = NULL
try:
primitive = self._coerce_primitive(primitive, context)
except ValueError as e:
- raise InvalidValueError('%s is not a list of "%s": element %d is %s'
- % (self.full_name,
- self.full_cls_name,
- i,
- safe_repr(primitive)),
+ raise InvalidValueError(u'{0} is not a list of "{1}": element {2:d} is {3}'
+ .format(self.full_name,
+ self.full_cls_name,
+ i,
+ safe_repr(primitive)),
locator=self.get_locator(raw), cause=e)
if primitive in primitive_list:
- raise InvalidValueError('%s has a duplicate "%s": %s'
- % (self.full_name,
- self.full_cls_name,
- safe_repr(primitive)),
+ raise InvalidValueError(u'{0} has a duplicate "{1}": {2}'
+ .format(self.full_name,
+ self.full_cls_name,
+ safe_repr(primitive)),
locator=self.get_locator(raw))
primitive_list.append(primitive)
return FrozenList(primitive_list)
def _dump_primitive_list(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
with context.style.indent():
for primitive in value:
if hasattr(primitive, 'as_raw'):
@@ -617,7 +623,8 @@
def _get_primitive_dict(self, presentation, raw, value, context):
if not isinstance(value, dict):
- raise InvalidValueError('%s is not a dict: %s' % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a dict: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
primitive_dict = value
if self.cls is not None:
@@ -625,17 +632,21 @@
context = Field._get_context()
primitive_dict = OrderedDict()
for k, v in value.iteritems():
+ if v is None:
+ v = NULL
try:
primitive_dict[k] = self._coerce_primitive(v, context)
except ValueError as e:
- raise InvalidValueError('%s is not a dict of "%s" values: entry "%d" is %s'
- % (self.full_name, self.full_cls_name, k, safe_repr(v)),
+ raise InvalidValueError(u'{0} is not a dict of "{1}" values: entry "{2:d}" '
+ u'is {3}'
+ .format(self.full_name, self.full_cls_name, k,
+ safe_repr(v)),
locator=self.get_locator(raw),
cause=e)
return FrozenDict(primitive_dict)
def _dump_primitive_dict(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
with context.style.indent():
for v in value.itervalues():
if hasattr(v, 'as_raw'):
@@ -648,13 +659,13 @@
try:
return self.cls(name=self.name, raw=value, container=presentation)
except TypeError as e:
- raise InvalidValueError('%s cannot not be initialized to an instance of "%s": %s'
- % (self.full_name, self.full_cls_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} cannot not be initialized to an instance of "{1}": {2}'
+ .format(self.full_name, self.full_cls_name, safe_repr(value)),
cause=e,
locator=self.get_locator(raw))
def _dump_object(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
with context.style.indent():
if hasattr(value, '_dump'):
value._dump(context)
@@ -663,13 +674,13 @@
def _get_object_list(self, presentation, raw, value, context):
if not isinstance(value, list):
- raise InvalidValueError('%s is not a list: %s'
- % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a list: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
return FrozenList((self.cls(name=self.name, raw=v, container=presentation) for v in value))
def _dump_object_list(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
with context.style.indent():
for v in value:
if hasattr(v, '_dump'):
@@ -679,13 +690,14 @@
def _get_object_dict(self, presentation, raw, value, context):
if not isinstance(value, dict):
- raise InvalidValueError('%s is not a dict: %s' % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a dict: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
return FrozenDict(((k, self.cls(name=k, raw=v, container=presentation))
for k, v in value.iteritems()))
def _dump_object_dict(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
with context.style.indent():
for v in value.itervalues():
if hasattr(v, '_dump'):
@@ -695,26 +707,27 @@
def _get_sequenced_object_list(self, presentation, raw, value, context):
if not isinstance(value, list):
- raise InvalidValueError('%s is not a sequenced list (a list of dicts, '
- 'each with exactly one key): %s'
- % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} is not a sequenced list (a list of dicts, '
+ u'each with exactly one key): {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
sequence = []
for v in value:
if not isinstance(v, dict):
- raise InvalidValueError('%s list elements are not all dicts with '
- 'exactly one key: %s' % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} list elements are not all dicts with '
+ u'exactly one key: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
if len(v) != 1:
- raise InvalidValueError('%s list elements do not all have exactly one key: %s'
- % (self.full_name, safe_repr(value)),
+ raise InvalidValueError(u'{0} list elements do not all have exactly one key: {1}'
+ .format(self.full_name, safe_repr(value)),
locator=self.get_locator(raw))
key, value = v.items()[0]
sequence.append((key, self.cls(name=key, raw=value, container=presentation)))
return FrozenList(sequence)
def _dump_sequenced_object_list(self, context, value):
- puts('%s:' % self.name)
+ puts(u'{0}:'.format(self.name))
for _, v in value:
if hasattr(v, '_dump'):
v._dump(context)
@@ -730,13 +743,15 @@
primitive_dict = OrderedDict()
for k, v in raw.iteritems():
if k not in presentation.FIELDS:
+ if v is None:
+ v = NULL
try:
primitive_dict[k] = self._coerce_primitive(v, context)
except ValueError as e:
- raise InvalidValueError('%s is not a dict of "%s" values:'
- ' entry "%d" is %s'
- % (self.full_name, self.full_cls_name,
- k, safe_repr(v)),
+ raise InvalidValueError(u'{0} is not a dict of "{1}" values:'
+ u' entry "{2}" is {3}'
+ .format(self.full_name, self.full_cls_name,
+ k, safe_repr(v)),
locator=self.get_locator(raw),
cause=e)
return FrozenDict(primitive_dict)
diff --git a/aria/parser/presentation/presentation.py b/aria/parser/presentation/presentation.py
index 3f9f86d..762ecba 100644
--- a/aria/parser/presentation/presentation.py
+++ b/aria/parser/presentation/presentation.py
@@ -115,7 +115,7 @@
if names:
obj = self._get(*names[:-1])
if isinstance(obj, dict):
- return obj.get(names[-1]) # pylint: disable=no-member
+ return obj.get(names[-1]) # pylint: disable=no-member
return None
def _get_child_locator(self, *names):
@@ -159,7 +159,7 @@
for field_name in field_names:
self._dump_field(context, field_name)
elif hasattr(self, '_iter_field_names'):
- for field_name in self._iter_field_names(): # pylint: disable=no-member
+ for field_name in self._iter_field_names(): # pylint: disable=no-member
self._dump_field(context, field_name)
else:
puts(context.style.literal_style(self._raw))
@@ -172,7 +172,7 @@
delegate to their ``_dump`` methods.
"""
- field = self.FIELDS[field_name] # pylint: disable=no-member
+ field = self.FIELDS[field_name] # pylint: disable=no-member
field.dump(self, context)
def _clone(self, container=None):
@@ -199,6 +199,10 @@
"""
def _validate(self, context):
+ # Allow the skipping of normative type validation (for improved performance)
+ if (not context.presentation.configuration.get('validate_normative', True)) \
+ and self._get_extension('normative'):
+ return
validate_no_short_form(context, self)
validate_no_unknown_fields(context, self)
validate_known_fields(context, self)
@@ -233,8 +237,9 @@
try:
validate_primitive(self._raw, self.cls, context.validation.allow_primitive_coersion)
except ValueError as e:
- context.validation.report('"%s" is not a valid "%s": %s'
- % (self._fullname, self._full_cls_name, safe_repr(self._raw)),
+ context.validation.report(u'"{0}" is not a valid "{1}": {2}'
+ .format(self._fullname, self._full_cls_name,
+ safe_repr(self._raw)),
locator=self._locator,
level=Issue.FIELD,
exception=e)
diff --git a/aria/parser/presentation/presenter.py b/aria/parser/presentation/presenter.py
index 9fd296f..d2f3292 100644
--- a/aria/parser/presentation/presenter.py
+++ b/aria/parser/presentation/presenter.py
@@ -41,10 +41,10 @@
if tosca_definitions_version is not None \
and tosca_definitions_version not in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS:
context.validation.report(
- 'import "tosca_definitions_version" is not one of %s: %s'
- % (' or '.join([safe_repr(v)
- for v in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS]),
- presentation.service_template.tosca_definitions_version),
+ u'import "tosca_definitions_version" is not one of {0}: {1}'
+ .format(u' or '.join([safe_repr(v)
+ for v in self.__class__.ALLOWED_IMPORTED_DSL_VERSIONS]),
+ presentation.service_template.tosca_definitions_version),
locator=presentation._get_child_locator('inputs'),
level=Issue.BETWEEN_TYPES)
return False
diff --git a/aria/parser/presentation/source.py b/aria/parser/presentation/source.py
index 4bfb8e1..0bee5d1 100644
--- a/aria/parser/presentation/source.py
+++ b/aria/parser/presentation/source.py
@@ -26,7 +26,7 @@
Presenter sources provide appropriate :class:`Presenter` classes for agnostic raw data.
"""
- def get_presenter(self, raw): # pylint: disable=unused-argument,no-self-use
+ def get_presenter(self, raw): # pylint: disable=unused-argument,no-self-use
raise PresenterNotFoundError('presenter not found')
diff --git a/aria/parser/presentation/utils.py b/aria/parser/presentation/utils.py
index f0fd390..b805299 100644
--- a/aria/parser/presentation/utils.py
+++ b/aria/parser/presentation/utils.py
@@ -56,7 +56,7 @@
:raises ValueError: if not a primitive type or if coercion failed.
"""
- if (cls is not None) and (value is not None) and (value is not NULL):
+ if (cls is not None) and (value is not None):
if (cls is unicode) or (cls is str): # These two types are interchangeable
valid = isinstance(value, basestring)
elif cls is int:
@@ -66,9 +66,11 @@
valid = isinstance(value, cls)
if not valid:
if coerce:
+ if value is NULL:
+ value = None
value = cls(value)
else:
- raise ValueError('not a "%s": %s' % (full_type_name(cls), safe_repr(value)))
+ raise ValueError(u'not a "{0}": {1}'.format(full_type_name(cls), safe_repr(value)))
return value
@@ -78,7 +80,8 @@
"""
if not hasattr(presentation, 'SHORT_FORM_FIELD') and not isinstance(presentation._raw, dict):
- context.validation.report('short form not allowed for field "%s"' % presentation._fullname,
+ context.validation.report(u'short form not allowed for field "{0}"'
+ .format(presentation._fullname),
locator=presentation._locator,
level=Issue.BETWEEN_FIELDS)
@@ -94,8 +97,8 @@
and hasattr(presentation, 'FIELDS'):
for k in presentation._raw:
if k not in presentation.FIELDS:
- context.validation.report('field "%s" is not supported in "%s"'
- % (k, presentation._fullname),
+ context.validation.report(u'field "{0}" is not supported in "{1}"'
+ .format(k, presentation._fullname),
locator=presentation._get_child_locator(k),
level=Issue.BETWEEN_FIELDS)
@@ -161,27 +164,28 @@
def report_issue_for_unknown_type(context, presentation, type_name, field_name, value=None):
if value is None:
value = getattr(presentation, field_name)
- context.validation.report('"%s" refers to an unknown %s in "%s": %s'
- % (field_name, type_name, presentation._fullname, safe_repr(value)),
+ context.validation.report(u'"{0}" refers to an unknown {1} in "{2}": {3}'
+ .format(field_name, type_name, presentation._fullname,
+ safe_repr(value)),
locator=presentation._get_child_locator(field_name),
level=Issue.BETWEEN_TYPES)
def report_issue_for_parent_is_self(context, presentation, field_name):
- context.validation.report('parent type of "%s" is self' % presentation._fullname,
+ context.validation.report(u'parent type of "{0}" is self'.format(presentation._fullname),
locator=presentation._get_child_locator(field_name),
level=Issue.BETWEEN_TYPES)
def report_issue_for_unknown_parent_type(context, presentation, field_name):
- context.validation.report('unknown parent type "%s" in "%s"'
- % (getattr(presentation, field_name), presentation._fullname),
+ context.validation.report(u'unknown parent type "{0}" in "{1}"'
+ .format(getattr(presentation, field_name), presentation._fullname),
locator=presentation._get_child_locator(field_name),
level=Issue.BETWEEN_TYPES)
def report_issue_for_circular_type_hierarchy(context, presentation, field_name):
- context.validation.report('"%s" of "%s" creates a circular type hierarchy'
- % (getattr(presentation, field_name), presentation._fullname),
+ context.validation.report(u'"{0}" of "{1}" creates a circular type hierarchy'
+ .format(getattr(presentation, field_name), presentation._fullname),
locator=presentation._get_child_locator(field_name),
level=Issue.BETWEEN_TYPES)
diff --git a/aria/parser/reading/__init__.py b/aria/parser/reading/__init__.py
index c110585..ddd9a3b 100644
--- a/aria/parser/reading/__init__.py
+++ b/aria/parser/reading/__init__.py
@@ -20,7 +20,6 @@
ReaderException
ReaderNotFoundError
ReaderSyntaxError
- AlreadyReadException
JinjaReader
JsonReader
Locator
@@ -41,14 +40,12 @@
from .source import ReaderSource, DefaultReaderSource
from .exceptions import (ReaderException,
ReaderNotFoundError,
- ReaderSyntaxError,
- AlreadyReadException)
+ ReaderSyntaxError)
__all__ = (
'ReaderException',
'ReaderNotFoundError',
'ReaderSyntaxError',
- 'AlreadyReadException',
'Reader',
'ReaderSource',
'DefaultReaderSource',
diff --git a/aria/parser/reading/context.py b/aria/parser/reading/context.py
index 233e407..1eb05a4 100644
--- a/aria/parser/reading/context.py
+++ b/aria/parser/reading/context.py
@@ -10,7 +10,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ...utils.threading import LockedList
from .source import DefaultReaderSource
@@ -27,5 +26,3 @@
def __init__(self):
self.reader_source = DefaultReaderSource()
self.reader = None
-
- self._locations = LockedList() # for keeping track of locations already read
diff --git a/aria/parser/reading/exceptions.py b/aria/parser/reading/exceptions.py
index 3699729..13bec92 100644
--- a/aria/parser/reading/exceptions.py
+++ b/aria/parser/reading/exceptions.py
@@ -36,9 +36,3 @@
super(ReaderSyntaxError, self).__init__(message, cause, cause_tb)
self.issue = Issue(message, location=location, line=line, column=column,
locator=locator, snippet=snippet, level=level)
-
-
-class AlreadyReadException(ReaderException):
- """
- ARIA reader exception: already read.
- """
diff --git a/aria/parser/reading/jinja.py b/aria/parser/reading/jinja.py
index 687317a..623103a 100644
--- a/aria/parser/reading/jinja.py
+++ b/aria/parser/reading/jinja.py
@@ -36,7 +36,7 @@
def read(self):
data = self.load()
try:
- data = str(data)
+ data = unicode(data)
template = Template(data)
literal = template.render(CONTEXT)
# TODO: might be useful to write the literal result to a file for debugging
@@ -52,4 +52,4 @@
self.context, LiteralLocation(literal), LiteralLoader(literal))
return next_reader.read()
except Exception as e:
- raise ReaderSyntaxError('Jinja: %s' % e, cause=e)
+ raise ReaderSyntaxError(u'Jinja: {0}'.format(e), cause=e)
diff --git a/aria/parser/reading/json.py b/aria/parser/reading/json.py
index d144f80..d02aeee 100644
--- a/aria/parser/reading/json.py
+++ b/aria/parser/reading/json.py
@@ -30,4 +30,4 @@
data = unicode(data)
return json.loads(data, object_pairs_hook=OrderedDict)
except Exception as e:
- raise ReaderSyntaxError('JSON: %s' % e, cause=e)
+ raise ReaderSyntaxError(u'JSON: {0}'.format(e), cause=e)
diff --git a/aria/parser/reading/locator.py b/aria/parser/reading/locator.py
index 57b4d50..63f4735 100644
--- a/aria/parser/reading/locator.py
+++ b/aria/parser/reading/locator.py
@@ -73,23 +73,23 @@
wrapped, raw_element = wrap(raw_element)
if wrapped:
raw[i] = raw_element
- child_path = '%s.%d' % (path, i) if path else str(i)
+ child_path = u'{0}.{1:d}'.format(path, i) if path else unicode(i)
try:
self.children[i].link(raw_element, child_path)
except KeyError:
- raise ValueError('location map does not match agnostic raw data: %s' %
- child_path)
+ raise ValueError('location map does not match agnostic raw data: {0}'
+ .format(child_path))
elif isinstance(raw, dict):
for k, raw_element in raw.iteritems():
wrapped, raw_element = wrap(raw_element)
if wrapped:
raw[k] = raw_element
- child_path = '%s.%s' % (path, k) if path else k
+ child_path = u'{0}.{1}'.format(path, k) if path else k
try:
self.children[k].link(raw_element, child_path)
except KeyError:
- raise ValueError('location map does not match agnostic raw data: %s' %
- child_path)
+ raise ValueError('location map does not match agnostic raw data: {0}'
+ .format(child_path))
def merge(self, locator):
if isinstance(self.children, dict) and isinstance(locator.children, dict):
@@ -101,10 +101,10 @@
def dump(self, key=None):
if key:
- puts('%s "%s":%d:%d' %
- (Colored.red(key), Colored.blue(self.location), self.line, self.column))
+ puts(u'{0} "{1}":{2:d}:{3:d}'
+ .format(Colored.red(key), Colored.blue(self.location), self.line, self.column))
else:
- puts('"%s":%d:%d' % (Colored.blue(self.location), self.line, self.column))
+ puts(u'"{0}":{1:d}:{2:d}'.format(Colored.blue(self.location), self.line, self.column))
if isinstance(self.children, list):
with indent(2):
for loc in self.children:
@@ -116,4 +116,4 @@
def __str__(self):
# Should be in same format as Issue.locator_as_str
- return '"%s":%d:%d' % (self.location, self.line, self.column)
+ return u'"{0}":{1:d}:{2:d}'.format(self.location, self.line, self.column)
diff --git a/aria/parser/reading/reader.py b/aria/parser/reading/reader.py
index 1a29f11..4cba0b5 100644
--- a/aria/parser/reading/reader.py
+++ b/aria/parser/reading/reader.py
@@ -11,7 +11,7 @@
# limitations under the License.
from ...utils.openclose import OpenClose
-from .exceptions import ReaderException, AlreadyReadException
+from .exceptions import ReaderException
class Reader(object):
@@ -28,16 +28,9 @@
def load(self):
with OpenClose(self.loader) as loader:
- if self.context is not None:
- with self.context._locations:
- for location in self.context._locations:
- if location.is_equivalent(loader.location):
- raise AlreadyReadException('already read: %s' % loader.location)
- self.context._locations.append(loader.location)
-
data = loader.load()
if data is None:
- raise ReaderException('loader did not provide data: %s' % loader)
+ raise ReaderException(u'loader did not provide data: {0}'.format(loader))
return data
def read(self):
diff --git a/aria/parser/reading/source.py b/aria/parser/reading/source.py
index 6fff2f6..93260d2 100644
--- a/aria/parser/reading/source.py
+++ b/aria/parser/reading/source.py
@@ -31,8 +31,8 @@
"""
@staticmethod
- def get_reader(context, location, loader): # pylint: disable=unused-argument
- raise ReaderNotFoundError('location: %s' % location)
+ def get_reader(context, location, loader): # pylint: disable=unused-argument
+ raise ReaderNotFoundError(u'location: {0}'.format(location))
class DefaultReaderSource(ReaderSource):
diff --git a/aria/parser/reading/yaml.py b/aria/parser/reading/yaml.py
index d396ade..3735d5e 100644
--- a/aria/parser/reading/yaml.py
+++ b/aria/parser/reading/yaml.py
@@ -10,15 +10,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ...utils.yaml import yaml # @UnresolvedImport
-
+from ...utils.yaml import yaml
from ...utils.collections import OrderedDict
from .reader import Reader
from .locator import Locator
from .exceptions import ReaderSyntaxError
-from .locator import LocatableString, LocatableInt, LocatableFloat
+from .locator import (LocatableString, LocatableInt, LocatableFloat)
-# Add our types to ruamel.yaml
+
+# YAML mapping tag
+MAP_TAG = u'tag:yaml.org,2002:map'
+
+# This is an internal tag used by ruamel.yaml for merging nodes
+MERGE_TAG = u'tag:yaml.org,2002:merge'
+
+
+# Add our types to RoundTripRepresenter
yaml.representer.RoundTripRepresenter.add_representer(
LocatableString, yaml.representer.RoundTripRepresenter.represent_unicode)
yaml.representer.RoundTripRepresenter.add_representer(
@@ -26,8 +33,18 @@
yaml.representer.RoundTripRepresenter.add_representer(
LocatableFloat, yaml.representer.RoundTripRepresenter.represent_float)
-MERGE_TAG = u'tag:yaml.org,2002:merge'
-MAP_TAG = u'tag:yaml.org,2002:map'
+
+def construct_yaml_map(self, node):
+ """
+ Replacement for ruamel.yaml's constructor that uses OrderedDict instead of dict.
+ """
+ data = OrderedDict()
+ yield data
+ value = self.construct_mapping(node)
+ data.update(value)
+
+
+yaml.constructor.SafeConstructor.add_constructor(MAP_TAG, construct_yaml_map)
class YamlLocator(Locator):
@@ -60,16 +77,6 @@
locator.add_children(node)
-def construct_yaml_map(self, node):
- data = OrderedDict()
- yield data
- value = self.construct_mapping(node)
- data.update(value)
-
-
-yaml.constructor.SafeConstructor.add_constructor(MAP_TAG, construct_yaml_map)
-
-
class YamlReader(Reader):
"""
ARIA YAML reader.
@@ -82,7 +89,11 @@
# see issue here:
# https://bitbucket.org/ruamel/yaml/issues/61/roundtriploader-causes-exceptions-with
#yaml_loader = yaml.RoundTripLoader(data)
- yaml_loader = yaml.SafeLoader(data)
+ try:
+ # Faster C-based loader, might not be available on all platforms
+ yaml_loader = yaml.CSafeLoader(data)
+ except Exception:
+ yaml_loader = yaml.SafeLoader(data)
try:
node = yaml_loader.get_single_node()
locator = YamlLocator(self.loader.location, 0, 0)
@@ -102,12 +113,12 @@
line = e.problem_mark.line
column = e.problem_mark.column
snippet = e.problem_mark.get_snippet()
- raise ReaderSyntaxError('YAML %s: %s %s' %
- (e.__class__.__name__, problem, context),
+ raise ReaderSyntaxError(u'YAML {0}: {1} {2}'
+ .format(e.__class__.__name__, problem, context),
location=self.loader.location,
line=line,
column=column,
snippet=snippet,
cause=e)
except Exception as e:
- raise ReaderSyntaxError('YAML: %s' % e, cause=e)
+ raise ReaderSyntaxError(u'YAML: {0}'.format(e), cause=e)
diff --git a/aria/parser/specification.py b/aria/parser/specification.py
index 4f452b8..682cea3 100644
--- a/aria/parser/specification.py
+++ b/aria/parser/specification.py
@@ -21,7 +21,7 @@
from ..extension import parser
from ..utils.collections import OrderedDict
-from ..utils.specification import (DSL_SPECIFICATIONS, implements_specification) # pylint: disable=unused-import
+from ..utils.specification import (DSL_SPECIFICATIONS, implements_specification) # pylint: disable=unused-import
def iter_specifications():
diff --git a/aria/parser/validation/issue.py b/aria/parser/validation/issue.py
index 42fc580..e26408e 100644
--- a/aria/parser/validation/issue.py
+++ b/aria/parser/validation/issue.py
@@ -66,9 +66,9 @@
def __init__(self, message=None, exception=None, location=None, line=None,
column=None, locator=None, snippet=None, level=0):
if message is not None:
- self.message = str(message)
+ self.message = unicode(message)
elif exception is not None:
- self.message = str(exception)
+ self.message = unicode(exception)
else:
self.message = 'unknown issue'
@@ -102,33 +102,33 @@
if self.location is not None:
if self.line is not None:
if self.column is not None:
- return '"%s":%d:%d' % (self.location, self.line, self.column)
+ return u'"{0}":{1:d}:{2:d}'.format(self.location, self.line, self.column)
else:
- return '"%s":%d' % (self.location, self.line)
+ return u'"{0}":{1:d}'.format(self.location, self.line)
else:
- return '"%s"' % self.location
+ return u'"{0}"'.format(self.location)
else:
return None
@property
def heading_as_str(self):
- return '%d: %s' % (self.level, self.message)
+ return u'{0:d}: {1}'.format(self.level, self.message)
@property
def details_as_str(self):
- details_str = ''
+ details_str = u''
locator = self.locator_as_str
if locator is not None:
- details_str += '@%s' % locator
+ details_str += u'@{0}'.format(locator)
if self.snippet is not None:
- details_str += '\n%s' % self.snippet
+ details_str += u'\n{0}'.format(self.snippet)
return details_str
def __str__(self):
heading_str = self.heading_as_str
details = self.details_as_str
if details:
- heading_str += ', ' + details
+ heading_str += u', ' + details
return heading_str
@@ -149,7 +149,7 @@
# Avoid duplicate issues
with self._issues:
for i in self._issues:
- if str(i) == str(issue):
+ if unicode(i) == unicode(issue):
return
self._issues.append(issue)
diff --git a/aria/storage/filesystem_rapi.py b/aria/storage/filesystem_rapi.py
index b425fa2..d0703e6 100644
--- a/aria/storage/filesystem_rapi.py
+++ b/aria/storage/filesystem_rapi.py
@@ -129,7 +129,7 @@
if os.path.isfile(resource):
shutil.copy2(resource, destination)
else:
- dir_util.copy_tree(resource, destination) # pylint: disable=no-member
+ dir_util.copy_tree(resource, destination) # pylint: disable=no-member
def upload(self, entry_id, source, path=None, **_):
"""
@@ -146,7 +146,7 @@
if os.path.isfile(source):
shutil.copy2(source, destination)
else:
- dir_util.copy_tree(source, destination) # pylint: disable=no-member
+ dir_util.copy_tree(source, destination) # pylint: disable=no-member
def delete(self, entry_id, path=None, **_):
"""
diff --git a/aria/utils/caching.py b/aria/utils/caching.py
index 5f8cd88..350a9e8 100644
--- a/aria/utils/caching.py
+++ b/aria/utils/caching.py
@@ -25,7 +25,7 @@
from .collections import OrderedDict
-class cachedmethod(object): # pylint: disable=invalid-name
+class cachedmethod(object): # pylint: disable=invalid-name
"""
Decorator for caching method return values.
diff --git a/aria/utils/collections.py b/aria/utils/collections.py
index ccc37a1..2126d59 100644
--- a/aria/utils/collections.py
+++ b/aria/utils/collections.py
@@ -19,6 +19,11 @@
from __future__ import absolute_import # so we can import standard 'collections'
+try:
+ import cPickle as pickle
+except ImportError:
+ import pickle
+
from copy import deepcopy
try:
from collections import OrderedDict
@@ -29,7 +34,7 @@
def cls_name(cls):
module = str(cls.__module__)
name = str(cls.__name__)
- return name if module == '__builtin__' else '%s.%s' % (module, name)
+ return name if module == '__builtin__' else '{0}.{1}'.format(module, name)
class FrozenList(list):
@@ -140,7 +145,8 @@
def _wrap(self, value):
if (self.value_class is not None) and (not isinstance(value, self.value_class)):
- raise TypeError('value must be a "%s": %s' % (cls_name(self.value_class), repr(value)))
+ raise TypeError('value must be a "{0}": {1}'
+ .format(cls_name(self.value_class), repr(value)))
if self.wrapper_function is not None:
value = self.wrapper_function(value)
return value
@@ -204,7 +210,8 @@
def __getitem__(self, key):
if (self.key_class is not None) and (not isinstance(key, self.key_class)):
- raise TypeError('key must be a "%s": %s' % (cls_name(self.key_class), repr(key)))
+ raise TypeError('key must be a "{0}": {1}'
+ .format(cls_name(self.key_class), repr(key)))
value = super(StrictDict, self).__getitem__(key)
if self.unwrapper_function is not None:
value = self.unwrapper_function(value)
@@ -212,35 +219,49 @@
def __setitem__(self, key, value, **_):
if (self.key_class is not None) and (not isinstance(key, self.key_class)):
- raise TypeError('key must be a "%s": %s' % (cls_name(self.key_class), repr(key)))
+ raise TypeError('key must be a "{0}": {1}'
+ .format(cls_name(self.key_class), repr(key)))
if (self.value_class is not None) and (not isinstance(value, self.value_class)):
- raise TypeError('value must be a "%s": %s' % (cls_name(self.value_class), repr(value)))
+ raise TypeError('value must be a "{0}": {1}'
+ .format(cls_name(self.value_class), repr(value)))
if self.wrapper_function is not None:
value = self.wrapper_function(value)
return super(StrictDict, self).__setitem__(key, value)
-def merge(dict_a, dict_b, path=None, strict=False):
+def merge(dict_a, dict_b, copy=True, strict=False, path=None):
"""
Merges dicts, recursively.
+
+ :param dict_a: target dict (will be modified)
+ :param dict_b: source dict (will not be modified)
+ :param copy: if True, will use :func:`deepcopy_fast` on each merged element
+ :param strict: if True, will raise a ValueError if there are key conflicts, otherwise will
+ override exiting values
+ :param path: for internal use in strict mode
+ :return: dict_a, after the merge
"""
- # TODO: a.add_yaml_merge(b), see https://bitbucket.org/ruamel/yaml/src/
- # TODO: 86622a1408e0f171a12e140d53c4ffac4b6caaa3/comments.py?fileviewer=file-view-default
+ # TODO: a.add_yaml_merge(b),
+ # see https://bitbucket.org/ruamel/yaml/src/86622a1408e0f171a12e140d53c4ffac4b6caaa3/
+ # comments.py?fileviewer=file-view-default
path = path or []
for key, value_b in dict_b.iteritems():
if key in dict_a:
value_a = dict_a[key]
if isinstance(value_a, dict) and isinstance(value_b, dict):
- merge(value_a, value_b, path + [str(key)], strict)
+ if strict:
+ path = path + [str(key)]
+ merge(value_a, value_b, copy, strict, path)
elif value_a != value_b:
if strict:
- raise ValueError('dict merge conflict at %s' % '.'.join(path + [str(key)]))
+ raise ValueError('dict merge conflict at {0}'
+ .format('.'.join(path + [str(key)])))
else:
- dict_a[key] = value_b
+ dict_a[key] = deepcopy_fast(value_b) if copy else value_b
else:
- dict_a[key] = value_b
+ dict_a[key] = deepcopy_fast(value_b) if copy else value_b
return dict_a
@@ -269,6 +290,15 @@
return value
+def deepcopy_fast(obj):
+ """
+ The builtin ``deepcopy`` is very slow due to detection of loops and other errors.
+
+ This version is surprisingly much faster.
+ """
+ return pickle.loads(pickle.dumps(obj))
+
+
# TODO: Move following two methods to some place parser specific
def deepcopy_with_locators(value):
@@ -276,7 +306,7 @@
Like :func:`~copy.deepcopy`, but also copies over locators.
"""
- res = deepcopy(value)
+ res = deepcopy_fast(value)
copy_locators(res, value)
return res
diff --git a/aria/utils/formatting.py b/aria/utils/formatting.py
index fac3221..c9e7988 100644
--- a/aria/utils/formatting.py
+++ b/aria/utils/formatting.py
@@ -61,7 +61,7 @@
super(JsonAsRawEncoder, self).__init__(*args, **kwargs)
-class YamlAsRawDumper(yaml.dumper.RoundTripDumper): # pylint: disable=too-many-ancestors
+class YamlAsRawDumper(yaml.dumper.RoundTripDumper): # pylint: disable=too-many-ancestors
"""
A :class:`RoundTripDumper` that will use the ``as_raw`` property of objects if available.
"""
diff --git a/aria/utils/threading.py b/aria/utils/threading.py
index f5ca302..a32136d 100644
--- a/aria/utils/threading.py
+++ b/aria/utils/threading.py
@@ -59,10 +59,9 @@
pass
-# https://gist.github.com/tliron/81dd915166b0bfc64be08b4f8e22c835
-class FixedThreadPoolExecutor(object):
+class Executor(object):
"""
- Executes tasks in a fixed thread pool.
+ Executes tasks.
Makes sure to gather all returned results and thrown exceptions in one place, in order of task
submission.
@@ -93,7 +92,104 @@
print executor.returns
"""
- _CYANIDE = object() # Special task marker used to kill worker threads.
+ def __init__(self, print_exceptions=False):
+ self.print_exceptions = print_exceptions
+
+ def submit(self, func, *args, **kwargs):
+ """
+ Submit a task for execution.
+
+ The task will be called ASAP on the next available worker thread in the pool.
+
+ :raises ExecutorException: if cannot be submitted
+ """
+ raise NotImplementedError
+
+ def close(self):
+ """
+ Blocks until all current tasks finish execution and all worker threads are dead.
+
+ You cannot submit tasks anymore after calling this.
+
+ This is called automatically upon exit if you are using the ``with`` keyword.
+ """
+ pass
+
+ def drain(self):
+ """
+ Blocks until all current tasks finish execution, but leaves the worker threads alive.
+ """
+ pass
+
+ @property
+ def returns(self):
+ """
+ The returned values from all tasks, in order of submission.
+ """
+ return ()
+
+ @property
+ def exceptions(self):
+ """
+ The raised exceptions from all tasks, in order of submission.
+ """
+ return ()
+
+ def raise_first(self):
+ """
+ If exceptions were thrown by any task, then the first one will be raised.
+
+ This is rather arbitrary: proper handling would involve iterating all the exceptions.
+ However, if you want to use the "raise" mechanism, you are limited to raising only one of
+ them.
+ """
+
+ exceptions = self.exceptions
+ if exceptions:
+ raise exceptions[0]
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, the_type, value, traceback):
+ pass
+
+
+class BlockingExecutor(Executor):
+ """
+ Executes tasks in the current thread.
+ """
+
+ def __init__(self, print_exceptions=False):
+ super(BlockingExecutor, self).__init__(print_exceptions=print_exceptions)
+ self._returns = []
+ self._exceptions = []
+
+ def submit(self, func, *args, **kwargs):
+ try:
+ result = func(*args, **kwargs)
+ self._returns.append(result)
+ except Exception as e:
+ self._exceptions.append(e)
+ if self.print_exceptions:
+ print_exception(e)
+
+ @property
+ def returns(self):
+ return self._returns
+
+ @property
+ def exceptions(self):
+ return self._exceptions
+
+
+# https://gist.github.com/tliron/81dd915166b0bfc64be08b4f8e22c835
+class FixedThreadPoolExecutor(Executor):
+ """
+ Executes tasks in a fixed thread pool.
+ """
+
+ _CYANIDE = object() # special task marker used to kill worker threads
def __init__(self,
size=None,
@@ -105,6 +201,8 @@
:param timeout: timeout in seconds for all blocking operations (``None`` means no timeout)
:param print_exceptions: set to ``True`` in order to print exceptions from tasks
"""
+ super(FixedThreadPoolExecutor, self).__init__(print_exceptions=print_exceptions)
+
if not size:
try:
size = multiprocessing.cpu_count() * 2 + 1
@@ -113,7 +211,6 @@
self.size = size
self.timeout = timeout
- self.print_exceptions = print_exceptions
self._tasks = Queue()
self._returns = {}
@@ -124,34 +221,18 @@
self._workers = []
for index in range(size):
worker = DaemonThread(
- name='%s%d' % (self.__class__.__name__, index),
+ name='{0}{1:d}'.format(self.__class__.__name__, index),
target=self._thread_worker)
worker.start()
self._workers.append(worker)
def submit(self, func, *args, **kwargs):
- """
- Submit a task for execution.
-
- The task will be called ASAP on the next available worker thread in the pool.
-
- :raises ExecutorException: if cannot be submitted
- """
-
try:
self._tasks.put((self._id_creator.next(), func, args, kwargs), timeout=self.timeout)
except Full:
raise ExecutorException('cannot submit task: queue is full')
def close(self):
- """
- Blocks until all current tasks finish execution and all worker threads are dead.
-
- You cannot submit tasks anymore after calling this.
-
- This is called automatically upon exit if you are using the ``with`` keyword.
- """
-
self.drain()
while self.is_alive:
try:
@@ -161,11 +242,7 @@
self._workers = None
def drain(self):
- """
- Blocks until all current tasks finish execution, but leaves the worker threads alive.
- """
-
- self._tasks.join() # oddly, the API does not support a timeout parameter
+ self._tasks.join() # oddly, the API does not support a timeout parameter
@property
def is_alive(self):
@@ -180,33 +257,12 @@
@property
def returns(self):
- """
- The returned values from all tasks, in order of submission.
- """
-
return [self._returns[k] for k in sorted(self._returns)]
@property
def exceptions(self):
- """
- The raised exceptions from all tasks, in order of submission.
- """
-
return [self._exceptions[k] for k in sorted(self._exceptions)]
- def raise_first(self):
- """
- If exceptions were thrown by any task, then the first one will be raised.
-
- This is rather arbitrary: proper handling would involve iterating all the exceptions.
- However, if you want to use the "raise" mechanism, you are limited to raising only one of
- them.
- """
-
- exceptions = self.exceptions
- if exceptions:
- raise exceptions[0]
-
def _thread_worker(self):
while True:
if not self._execute_next_task():
@@ -240,7 +296,6 @@
def __exit__(self, the_type, value, traceback):
self.close()
- return False
class LockedList(list):
diff --git a/aria/utils/uris.py b/aria/utils/uris.py
index 49881f2..bb5b6ce 100644
--- a/aria/utils/uris.py
+++ b/aria/utils/uris.py
@@ -43,6 +43,6 @@
path = url.path
if _IS_WINDOWS:
path = path.replace('/', '\\')
- return os.path.normpath(path)
+ return os.path.realpath(path)
return None
diff --git a/aria/utils/versions.py b/aria/utils/versions.py
index 521004c..83f37d1 100644
--- a/aria/utils/versions.py
+++ b/aria/utils/versions.py
@@ -24,7 +24,7 @@
_NULL = (), _INF
-_DIGITS_RE = re.compile(r'^\d+$')
+_DIGITS_RE = re.compile(r'^\d+$', flags=re.UNICODE)
_PREFIXES = {
'dev': 0.0001,
@@ -81,7 +81,7 @@
return self.key.__hash__()
-def parse_version_string(version): # pylint: disable=too-many-branches
+def parse_version_string(version): # pylint: disable=too-many-branches
"""
Parses a version string.
diff --git a/examples/clearwater/clearwater-single-existing.yaml b/examples/clearwater/clearwater-single-existing.yaml
index 72b882a..4be3c6f 100644
--- a/examples/clearwater/clearwater-single-existing.yaml
+++ b/examples/clearwater/clearwater-single-existing.yaml
@@ -139,9 +139,9 @@
substitution_mappings:
node_type: ims.nodes.IMS
capabilities:
- p-cscf: [ bono, p-cscf ]
- i-cscf: [ i-cscf, i-cscf ]
- s-cscf: [ s-cscf, s-cscf ]
- hss: [ homestead, hss ]
- ctf: [ ralf, ctf ]
- xdms: [ homer, xdms ]
+ p-cscf: [ bono, p-cscf ]
+ i-cscf: [ i-cscf, i-cscf ]
+ s-cscf: [ s-cscf, s-cscf ]
+ hss: [ homestead, hss ]
+ ctf: [ ralf, ctf ]
+ xdms: [ homer, xdms ]
diff --git a/examples/hello-world/hello-world.yaml b/examples/hello-world/hello-world.yaml
index 86e2ad0..3d36049 100644
--- a/examples/hello-world/hello-world.yaml
+++ b/examples/hello-world/hello-world.yaml
@@ -2,30 +2,24 @@
node_types:
- WebServer:
- derived_from: tosca:Root
- capabilities:
- host:
- type: tosca:Container
-
- WebApp:
+ HelloWorld:
derived_from: tosca:WebApplication
- properties:
- port:
- type: integer
+ requirements:
+ - host:
+ # Override to allow for 0 occurrences
+ capability: tosca:Container
+ occurrences: [ 0, UNBOUNDED ]
topology_template:
node_templates:
- web_server:
- type: WebServer
-
- web_app:
- type: WebApp
- properties:
- port: 9090
- requirements:
- - host: web_server
+ hello_world:
+ type: HelloWorld
+ capabilities:
+ app_endpoint:
+ properties:
+ protocol: http
+ port: 9090
interfaces:
Standard:
configure: scripts/configure.sh
@@ -35,4 +29,4 @@
outputs:
port:
type: integer
- value: { get_property: [ web_app, port ] }
+ value: { get_property: [ hello_world, app_endpoint, port ] }
diff --git a/examples/hello-world/scripts/start.sh b/examples/hello-world/scripts/start.sh
index 1525f30..8a1f2fc 100755
--- a/examples/hello-world/scripts/start.sh
+++ b/examples/hello-world/scripts/start.sh
@@ -19,7 +19,7 @@
TEMP_DIR=/tmp
PYTHON_FILE_SERVER_ROOT="$TEMP_DIR/python-simple-http-webserver"
PID_FILE=server.pid
-PORT=$(ctx node properties port)
+PORT=$(ctx node capabilities app_endpoint properties port)
URL="http://localhost:$PORT"
ctx logger info [ "Starting web server at: $PYTHON_FILE_SERVER_ROOT." ]
diff --git a/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml b/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
index da89dcb..29a61f1 100644
--- a/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
+++ b/examples/tosca-simple-1.0/use-cases/non-normative-types.yaml
@@ -161,9 +161,11 @@
default: https://github.com/mmm/testnode.git
interfaces:
Standard:
+ type: tosca.interfaces.node.lifecycle.Standard # ARIA NOTE: missing in spec
inputs:
github_url:
type: string
+ required: false # ARIA NOTE: missing in spec
tosca.nodes.Container.Application.Docker:
_extensions:
diff --git a/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml b/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
index e421150..82c456b 100644
--- a/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
+++ b/extensions/aria_extension_tosca/profiles/aria-1.0/aria-1.0.yaml
@@ -17,6 +17,7 @@
aria.Plugin:
_extensions:
+ normative: true
shorthand_name: Plugin
type_qualified_name: aria:Plugin
role: plugin
@@ -40,6 +41,7 @@
aria.Workflow:
_extensions:
+ normative: true
shorthand_name: Workflow
type_qualified_name: aria:Workflow
role: workflow
@@ -61,6 +63,7 @@
aria.Scaling:
_extensions:
+ normative: true
type_qualified_name: aria:Scaling
role: scaling
description: >-
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
index 945622f..963361f 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/artifacts.yaml
@@ -17,6 +17,7 @@
tosca.artifacts.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
tosca.artifacts.File:
_extensions:
+ normative: true
shorthand_name: File
type_qualified_name: tosca:File
specification: tosca-simple-1.0
@@ -41,6 +43,7 @@
tosca.artifacts.Deployment:
_extensions:
+ normative: true
shorthand_name: Deployment # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Deployment
specification: tosca-simple-1.0
@@ -54,6 +57,7 @@
tosca.artifacts.Deployment.Image:
_extensions:
+ normative: true
shorthand_name: Deployment.Image
type_qualified_name: tosca:Deployment.Image
specification: tosca-simple-1.0
@@ -67,6 +71,7 @@
tosca.artifacts.Deployment.Image.VM:
_extensions:
+ normative: true
shorthand_name: Deployment.VM # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Deployment.VM
specification: tosca-simple-1.0
@@ -85,6 +90,7 @@
tosca.artifacts.Implementation:
_extensions:
+ normative: true
shorthand_name: Implementation # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Implementation
specification: tosca-simple-1.0
@@ -97,6 +103,7 @@
tosca.artifacts.Implementation.Bash:
_extensions:
+ normative: true
shorthand_name: Implementation.Bash # ARIA NOTE: mistake in spec? shouldn't we have "Implementation." as prefix?
type_qualified_name: tosca:Implementation.Bash
specification: tosca-simple-1.0
@@ -109,6 +116,7 @@
tosca.artifacts.Implementation.Python:
_extensions:
+ normative: true
shorthand_name: Implementation.Python # ARIA NOTE: mistake in spec? shouldn't we have "Implementation." as prefix?
type_qualified_name: tosca:Implementation.Python
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
index 66a4046..784279b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/capabilities.yaml
@@ -17,6 +17,7 @@
tosca.capabilities.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
tosca.capabilities.Node:
_extensions:
+ normative: true
shorthand_name: Node
type_qualified_name: tosca:Node
specification: tosca-simple-1.0
@@ -39,6 +41,7 @@
tosca.capabilities.Container:
_extensions:
+ normative: true
shorthand_name: Container
type_qualified_name: tosca:Container
specification: tosca-simple-1.0
@@ -82,6 +85,7 @@
tosca.capabilities.Attachment:
_extensions:
+ normative: true
shorthand_name: Attachment
type_qualified_name: tosca:Attachment
specification: tosca-simple-1.0
@@ -94,6 +98,7 @@
tosca.capabilities.OperatingSystem:
_extensions:
+ normative: true
shorthand_name: OperatingSystem
type_qualified_name: tosca:OperatingSystem
specification: tosca-simple-1.0
@@ -127,6 +132,7 @@
tosca.capabilities.Scalable:
_extensions:
+ normative: true
shorthand_name: Scalable
type_qualified_name: tosca:Scalable
specification: tosca-simple-1.0
@@ -163,6 +169,7 @@
tosca.capabilities.Endpoint:
_extensions:
+ normative: true
shorthand_name: Endpoint
type_qualified_name: tosca:Endpoint
specification: tosca-simple-1.0
@@ -232,6 +239,7 @@
tosca.capabilities.Endpoint.Public:
_extensions:
+ normative: true
shorthand_name: Endpoint.Public
type_qualified_name: tosca:Endpoint.Public
specification: tosca-simple-1.0
@@ -265,6 +273,7 @@
tosca.capabilities.Endpoint.Admin:
_extensions:
+ normative: true
shorthand_name: Endpoint.Admin
type_qualified_name: tosca:Endpoint.Admin
specification: tosca-simple-1.0
@@ -284,6 +293,7 @@
tosca.capabilities.Endpoint.Database:
_extensions:
+ normative: true
shorthand_name: Endpoint.Database
type_qualified_name: tosca:Endpoint.Database
specification: tosca-simple-1.0
@@ -299,6 +309,7 @@
tosca.capabilities.network.Bindable:
_extensions:
+ normative: true
shorthand_name: Bindable # ARIA NOTE: mistake in spec? has "network." as a prefix
type_qualified_name: tosca:Bindable
specification: tosca-simple-1.0
@@ -311,6 +322,7 @@
tosca.capabilities.network.Linkable:
_extensions:
+ normative: true
shorthand_name: Linkable
type_qualified_name: tosca:Linkable
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
index 61d4186..7a65cbd 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/data.yaml
@@ -21,10 +21,12 @@
timestamp:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_timestamp
version:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_version
type_qualified_name: tosca:version
specification: tosca-simple-1.0
@@ -33,6 +35,7 @@
range:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_range
type_qualified_name: tosca:range
specification: tosca-simple-1.0
@@ -45,6 +48,7 @@
list:
_extensions:
+ normative: true
use_entry_schema: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_list
type_qualified_name: tosca:list
@@ -54,6 +58,7 @@
map:
_extensions:
+ normative: true
use_entry_schema: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_map_value
type_qualified_name: tosca:map
@@ -67,6 +72,7 @@
scalar-unit.size:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_size
type_qualified_name: tosca:scalar-unit.size
specification: tosca-simple-1.0
@@ -75,6 +81,7 @@
scalar-unit.time:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_time
type_qualified_name: tosca:scalar-unit.time
specification: tosca-simple-1.0
@@ -83,6 +90,7 @@
scalar-unit.frequency:
_extensions:
+ normative: true
coerce_value: aria_extension_tosca.simple_v1_0.data_types.coerce_scalar_unit_frequency
type_qualified_name: tosca:scalar-unit.frequency
specification: tosca-simple-1.0
@@ -95,6 +103,7 @@
tosca.datatypes.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -105,6 +114,7 @@
tosca.datatypes.Credential:
_extensions:
+ normative: true
shorthand_name: Credential
type_qualified_name: tosca:Credential
specification: tosca-simple-1.0
@@ -145,6 +155,7 @@
tosca.datatypes.network.NetworkInfo:
_extensions:
+ normative: true
shorthand_name: NetworkInfo
type_qualified_name: tosca:NetworkInfo
specification: tosca-simple-1.0
@@ -174,6 +185,7 @@
tosca.datatypes.network.PortInfo:
_extensions:
+ normative: true
shorthand_name: PortInfo
type_qualified_name: tosca:PortInfo
specification: tosca-simple-1.0
@@ -213,6 +225,7 @@
tosca.datatypes.network.PortDef:
_extensions:
+ normative: true
shorthand_name: PortDef
type_qualified_name: tosca:PortDef
specification: tosca-simple-1.0
@@ -226,6 +239,7 @@
tosca.datatypes.network.PortSpec:
_extensions:
+ normative: true
shorthand_name: PortSpec
type_qualified_name: tosca:PortSpec
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
index 66cc25f..9b9aa23 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/groups.yaml
@@ -17,6 +17,7 @@
tosca.groups.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
index 29cc8dd..25e8993 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/interfaces.yaml
@@ -17,6 +17,7 @@
tosca.interfaces.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
tosca.interfaces.node.lifecycle.Standard:
_extensions:
+ normative: true
shorthand_name: Standard
type_qualified_name: tosca:Standard
specification: tosca-simple-1.0
@@ -52,6 +54,7 @@
tosca.interfaces.relationship.Configure:
_extensions:
+ normative: true
shorthand_name: Configure
type_qualified_name: tosca:Configure
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
index 05963b7..576b41b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/nodes.yaml
@@ -17,6 +17,7 @@
tosca.nodes.Root:
_extensions:
+ normative: true
shorthand_name: Root
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -55,6 +56,7 @@
tosca.nodes.Compute:
_extensions:
+ normative: true
shorthand_name: Compute
type_qualified_name: tosca:Compute
specification: tosca-simple-1.0
@@ -106,6 +108,7 @@
tosca.nodes.LoadBalancer:
_extensions:
+ normative: true
shorthand_name: LoadBalancer
type_qualified_name: tosca:LoadBalancer
specification: tosca-simple-1.0
@@ -140,6 +143,7 @@
tosca.nodes.SoftwareComponent:
_extensions:
+ normative: true
shorthand_name: SoftwareComponent
type_qualified_name: tosca:SoftwareComponent
specification: tosca-simple-1.0
@@ -168,6 +172,7 @@
tosca.nodes.WebServer:
_extensions:
+ normative: true
shorthand_name: WebServer
type_qualified_name: tosca:WebServer
specification: tosca-simple-1.0
@@ -188,6 +193,7 @@
tosca.nodes.WebApplication:
_extensions:
+ normative: true
shorthand_name: WebApplication
type_qualified_name: tosca:WebApplication
specification: tosca-simple-1.0
@@ -214,6 +220,7 @@
tosca.nodes.DBMS:
_extensions:
+ normative: true
shorthand_name: DBMS # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:DBMS
specification: tosca-simple-1.0
@@ -240,6 +247,7 @@
tosca.nodes.Database:
_extensions:
+ normative: true
shorthand_name: Database
type_qualified_name: tosca:Database
specification: tosca-simple-1.0
@@ -283,6 +291,7 @@
tosca.nodes.Container.Runtime:
_extensions:
+ normative: true
shorthand_name: Container.Runtime
type_qualified_name: tosca:Container.Runtime
specification: tosca-simple-1.0
@@ -300,6 +309,7 @@
tosca.nodes.Container.Application:
_extensions:
+ normative: true
shorthand_name: Container.Application
type_qualified_name: tosca:Container.Application
specification: tosca-simple-1.0
@@ -321,6 +331,7 @@
tosca.nodes.ObjectStorage:
_extensions:
+ normative: true
shorthand_name: ObjectStorage
type_qualified_name: tosca:ObjectStorage
specification: tosca-simple-1.0
@@ -354,6 +365,7 @@
tosca.nodes.BlockStorage:
_extensions:
+ normative: true
shorthand_name: BlockStorage
type_qualified_name: tosca:BlockStorage
specification: tosca-simple-1.0
@@ -388,6 +400,7 @@
tosca.nodes.network.Network:
_extensions:
+ normative: true
shorthand_name: Network
type_qualified_name: tosca:Network
specification: tosca-simple-1.0
@@ -466,6 +479,7 @@
tosca.nodes.network.Port:
_extensions:
+ normative: true
shorthand_name: Port
type_qualified_name: tosca:Port
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
index 7b35bb9..2ff672e 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/policies.yaml
@@ -17,6 +17,7 @@
tosca.policies.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -27,6 +28,7 @@
tosca.policies.Placement:
_extensions:
+ normative: true
shorthand_name: Placement # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Placement
specification: tosca-simple-1.0
@@ -38,6 +40,7 @@
tosca.policies.Scaling:
_extensions:
+ normative: true
shorthand_name: Scaling # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Scaling
specification: tosca-simple-1.0
@@ -49,6 +52,7 @@
tosca.policies.Update:
_extensions:
+ normative: true
shorthand_name: Update # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Update
specification: tosca-simple-1.0
@@ -60,6 +64,7 @@
tosca.policies.Performance:
_extensions:
+ normative: true
shorthand_name: Performance # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Performance
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
index 9f2c32c..b45da96 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-1.0/relationships.yaml
@@ -17,6 +17,7 @@
tosca.relationships.Root:
_extensions:
+ normative: true
shorthand_name: Root # ARIA NOTE: omitted in the spec
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -46,6 +47,7 @@
tosca.relationships.DependsOn:
_extensions:
+ normative: true
shorthand_name: DependsOn
type_qualified_name: tosca:DependsOn
specification: tosca-simple-1.0
@@ -58,6 +60,7 @@
tosca.relationships.HostedOn:
_extensions:
+ normative: true
shorthand_name: HostedOn
type_qualified_name: tosca:HostedOn
specification: tosca-simple-1.0
@@ -70,6 +73,7 @@
tosca.relationships.ConnectsTo:
_extensions:
+ normative: true
shorthand_name: ConnectsTo
type_qualified_name: tosca:ConnectsTo
specification: tosca-simple-1.0
@@ -86,6 +90,7 @@
tosca.relationships.AttachesTo:
_extensions:
+ normative: true
shorthand_name: AttachesTo
type_qualified_name: tosca:AttachesTo
specification: tosca-simple-1.0
@@ -119,6 +124,7 @@
tosca.relationships.RoutesTo:
_extensions:
+ normative: true
shorthand_name: RoutesTo
type_qualified_name: tosca:RoutesTo
specification: tosca-simple-1.0
@@ -135,6 +141,7 @@
tosca.relationships.network.LinksTo:
_extensions:
+ normative: true
shorthand_name: LinksTo
type_qualified_name: tosca:LinksTo
specification: tosca-simple-1.0
@@ -147,6 +154,7 @@
tosca.relationships.network.BindsTo:
_extensions:
+ normative: true
shorthand_name: BindsTo # ARIA NOTE: the spec says "network.BindsTo" which seems wrong
type_qualified_name: tosca:BindsTo
specification: tosca-simple-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
index 2427d9f..4a8e3c6 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/artifacts.yaml
@@ -17,6 +17,7 @@
tosca.artifacts.nfv.SwImage:
_extensions:
+ normative: true
shorthand_name: SwImage
type_qualified_name: tosca:SwImage
specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
index 7b6363f..0e71a3b 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/capabilities.yaml
@@ -17,6 +17,7 @@
tosca.capabilities.nfv.VirtualBindable:
_extensions:
+ normative: true
shorthand_name: VirtualBindable
type_qualified_name: tosca:VirtualBindable
specification: tosca-simple-nfv-1.0
@@ -29,6 +30,7 @@
tosca.capabilities.nfv.Metric:
_extensions:
+ normative: true
shorthand_name: Metric
type_qualified_name: tosca:Metric
specification: tosca-simple-nfv-1.0
@@ -41,6 +43,7 @@
tosca.capabilities.nfv.VirtualCompute:
_extensions:
+ normative: true
shorthand_name: VirtualCompute
type_qualified_name: tosca:VirtualCompute
specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
index 889dcf7..09f173d 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/data.yaml
@@ -18,6 +18,7 @@
tosca.datatypes.nfv.L2AddressData:
# TBD
_extensions:
+ normative: true
shorthand_name: L2AddressData
type_qualified_name: tosca:L2AddressData
specification: tosca-simple-nfv-1.0
@@ -26,6 +27,7 @@
tosca.datatypes.nfv.L3AddressData:
_extensions:
+ normative: true
shorthand_name: L3AddressData
type_qualified_name: tosca:L3AddressData
specification: tosca-simple-nfv-1.0
@@ -65,6 +67,7 @@
tosca.datatypes.nfv.AddressData:
_extensions:
+ normative: true
shorthand_name: AddressData
type_qualified_name: tosca:AddressData
specification: tosca-simple-nfv-1.0
@@ -102,6 +105,7 @@
tosca.datatypes.nfv.VirtualNetworkInterfaceRequirements:
_extensions:
+ normative: true
shorthand_name: VirtualNetworkInterfaceRequirements
type_qualified_name: tosca:VirtualNetworkInterfaceRequirements
specification: tosca-simple-nfv-1.0
@@ -139,6 +143,7 @@
tosca.datatypes.nfv.ConnectivityType:
_extensions:
+ normative: true
shorthand_name: ConnectivityType
type_qualified_name: tosca:ConnectivityType
specification: tosca-simple-nfv-1.0
@@ -165,6 +170,7 @@
tosca.datatypes.nfv.RequestedAdditionalCapability:
_extensions:
+ normative: true
shorthand_name: RequestedAdditionalCapability
type_qualified_name: tosca:RequestedAdditionalCapability
specification: tosca-simple-nfv-1.0
@@ -205,6 +211,7 @@
tosca.datatypes.nfv.VirtualMemory:
_extensions:
+ normative: true
shorthand_name: VirtualMemory
type_qualified_name: tosca:VirtualMemory
specification: tosca-simple-nfv-1.0
@@ -235,6 +242,7 @@
tosca.datatypes.nfv.VirtualCpu:
_extensions:
+ normative: true
shorthand_name: VirtualCpu
type_qualified_name: tosca:VirtualCpu
specification: tosca-simple-nfv-1.0
@@ -272,6 +280,7 @@
tosca.datatypes.nfv.VirtualCpuPinning:
_extensions:
+ normative: true
shorthand_name: VirtualCpuPinning
type_qualified_name: tosca:VirtualCpuPinning
specification: tosca-simple-nfv-1.0
@@ -299,6 +308,7 @@
tosca.datatypes.nfv.VnfcConfigurableProperties:
_extensions:
+ normative: true
shorthand_name: VnfcconfigurableProperties
type_qualified_name: tosca:VnfcconfigurableProperties
specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
index 8d1f0a2..4d7f337 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/nodes.yaml
@@ -17,6 +17,7 @@
tosca.nodes.nfv.VDU.Compute:
_extensions:
+ normative: true
shorthand_name: VDU.Compute
type_qualified_name: tosca:VDU.Compute
specification: tosca-simple-nfv-1.0
@@ -115,6 +116,7 @@
tosca.nodes.nfv.VDU.VirtualStorage:
_extensions:
+ normative: true
shorthand_name: VirtualStorage # ARIA NOTE: seems wrong in spec
type_qualified_name: tosca:VirtualStorage # ARIA NOTE: seems wrong in spec
specification: tosca-simple-nfv-1.0
@@ -151,6 +153,7 @@
tosca.nodes.nfv.Cpd:
_extensions:
+ normative: true
shorthand_name: Cpd
type_qualified_name: tosca:Cpd
specification: tosca-simple-nfv-1.0
@@ -194,11 +197,12 @@
tosca.nodes.nfv.VduCpd:
_extensions:
- shorthand_name: VduCpd
- type_qualified_name: tosca:VduCpd
- specification: tosca-simple-nfv-1.0
- specification_section: 5.9.5
- specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896082'
+ normative: true
+ shorthand_name: VduCpd
+ type_qualified_name: tosca:VduCpd
+ specification: tosca-simple-nfv-1.0
+ specification_section: 5.9.5
+ specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896082'
description: >-
The TOSCA nfv.VduCpd node type represents a type of TOSCA Cpd node and describes network
connectivity between a VNFC instance (based on this VDU) and an internal VL as defined by
@@ -232,11 +236,12 @@
tosca.nodes.nfv.VnfVirtualLinkDesc:
_extensions:
- shorthand_name: VnfVirtualLinkDesc
- type_qualified_name: tosca:VnfVirtualLinkDesc
- specification: tosca-simple-nfv-1.0
- specification_section: 5.9.6
- specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896083'
+ normative: true
+ shorthand_name: VnfVirtualLinkDesc
+ type_qualified_name: tosca:VnfVirtualLinkDesc
+ specification: tosca-simple-nfv-1.0
+ specification_section: 5.9.6
+ specification_url: 'http://docs.oasis-open.org/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html#_Toc482896083'
description: >-
The TOSCA nfv.VnfVirtualLinkDesc node type represents a logical internal virtual link as
defined by [ETSI GS NFV-IFA 011].
diff --git a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
index 4cf99a2..84c6a87 100644
--- a/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
+++ b/extensions/aria_extension_tosca/profiles/tosca-simple-nfv-1.0/relationships.yaml
@@ -17,6 +17,7 @@
tosca.relationships.nfv.VirtualBindsTo:
_extensions:
+ normative: true
shorthand_name: VirtualBindsTo
type_qualified_name: tosca:VirtualBindsTo
specification: tosca-simple-nfv-1.0
@@ -31,6 +32,7 @@
# valid_target_types), so we are using the definition in csd03 section 8.4.2.
tosca.relationships.nfv.Monitor:
_extensions:
+ normative: true
shorthand_name: Monitor
type_qualified_name: tosca:Monitor
specification: tosca-simple-nfv-1.0
diff --git a/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
index 64178aa..386a659 100644
--- a/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
+++ b/extensions/aria_extension_tosca/simple_nfv_v1_0/presenter.py
@@ -19,7 +19,7 @@
from ..simple_v1_0 import ToscaSimplePresenter1_0
-class ToscaSimpleNfvPresenter1_0(ToscaSimplePresenter1_0): # pylint: disable=invalid-name,abstract-method
+class ToscaSimpleNfvPresenter1_0(ToscaSimplePresenter1_0): # pylint: disable=invalid-name,abstract-method
"""
ARIA presenter for the `TOSCA Simple Profile for NFV v1.0 csd04 <http://docs.oasis-open.org
/tosca/tosca-nfv/v1.0/csd04/tosca-nfv-v1.0-csd04.html>`__.
@@ -38,6 +38,6 @@
@cachedmethod
def _get_import_locations(self, context):
import_locations = super(ToscaSimpleNfvPresenter1_0, self)._get_import_locations(context)
- if context.presentation.import_profile:
+ if context.presentation.configuration.get('tosca.import_profile', True):
return FrozenList([self.SIMPLE_PROFILE_FOR_NFV_LOCATION] + import_locations)
return import_locations
diff --git a/extensions/aria_extension_tosca/simple_v1_0/__init__.py b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
index 61995db..da323ea 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/__init__.py
@@ -45,7 +45,8 @@
aria_extension_tosca.simple_v1_0.PropertyDefinition
aria_extension_tosca.simple_v1_0.AttributeDefinition
- aria_extension_tosca.simple_v1_0.ParameterDefinition
+ aria_extension_tosca.simple_v1_0.InputDefinition
+ aria_extension_tosca.simple_v1_0.OutputDefinition
aria_extension_tosca.simple_v1_0.OperationDefinition
aria_extension_tosca.simple_v1_0.InterfaceDefinition
aria_extension_tosca.simple_v1_0.RelationshipDefinition
@@ -126,9 +127,9 @@
from .assignments import (PropertyAssignment, OperationAssignment, InterfaceAssignment,
RelationshipAssignment, RequirementAssignment, AttributeAssignment,
CapabilityAssignment, ArtifactAssignment)
-from .definitions import (PropertyDefinition, AttributeDefinition, ParameterDefinition,
- OperationDefinition, InterfaceDefinition, RelationshipDefinition,
- RequirementDefinition, CapabilityDefinition)
+from .definitions import (PropertyDefinition, AttributeDefinition, InputDefinition,
+ OutputDefinition, OperationDefinition, InterfaceDefinition,
+ RelationshipDefinition, RequirementDefinition, CapabilityDefinition)
from .filters import CapabilityFilter, NodeFilter
from .misc import (Description, MetaData, Repository, Import, ConstraintClause, EntrySchema,
OperationImplementation, SubstitutionMappingsRequirement,
@@ -157,7 +158,8 @@
'ArtifactAssignment',
'PropertyDefinition',
'AttributeDefinition',
- 'ParameterDefinition',
+ 'InputDefinition',
+ 'OutputDefinition',
'OperationDefinition',
'InterfaceDefinition',
'RelationshipDefinition',
diff --git a/extensions/aria_extension_tosca/simple_v1_0/assignments.py b/extensions/aria_extension_tosca/simple_v1_0/assignments.py
index 7b48ed0..6248483 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/assignments.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/assignments.py
@@ -144,6 +144,15 @@
# In RelationshipAssignment
the_type = the_type[0] # This could be a RelationshipTemplate
+ if isinstance(self._container._container, RequirementAssignment):
+ # In RequirementAssignment
+ relationship_definition = \
+ self._container._container._get_relationship_definition(context)
+ interface_definitions = relationship_definition.interfaces \
+ if relationship_definition is not None else None
+ if (interface_definitions is not None) and (self._name in interface_definitions):
+ return interface_definitions[self._name]._get_type(context)
+
interface_definitions = the_type._get_interfaces(context) \
if the_type is not None else None
interface_definition = interface_definitions.get(self._name) \
@@ -154,7 +163,7 @@
def _validate(self, context):
super(InterfaceAssignment, self)._validate(context)
if self.operations:
- for operation in self.operations.itervalues(): # pylint: disable=no-member
+ for operation in self.operations.itervalues(): # pylint: disable=no-member
operation._validate(context)
@@ -172,6 +181,8 @@
The optional reserved keyname used to provide the name of the Relationship Type for the
requirement assignment's relationship keyname.
+ ARIA NOTE: this can also be a relationship template name.
+
:type: :obj:`basestring`
"""
@@ -290,6 +301,20 @@
return None, None
@cachedmethod
+ def _get_definition(self, context):
+ node_type = self._container._get_type(context)
+ if (node_type is not None) and (node_type.requirements is not None):
+ for name, requirement in node_type.requirements:
+ if name == self._name:
+ return requirement
+ return None
+
+ @cachedmethod
+ def _get_relationship_definition(self, context):
+ requirement_definition = self._get_definition(context)
+ return requirement_definition.relationship if requirement_definition is not None else None
+
+ @cachedmethod
def _get_capability(self, context):
capability = self.capability
@@ -369,6 +394,9 @@
Template and used by orchestration engine to facilitate deployment and implementation of
interface operations.
+ ARIA NOTE: section 3.5.6.2.1 in the spec refers to a short notation for "file", but that
+ notation would be impossible because the "type" field is required.
+
See the `TOSCA Simple Profile v1.0 cos01 specification <http://docs.oasis-open.org/tosca
/TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html
#DEFN_ENTITY_ARTIFACT_DEF>`__
diff --git a/extensions/aria_extension_tosca/simple_v1_0/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/data_types.py
index 216f1e4..b85caa1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/data_types.py
@@ -37,13 +37,13 @@
super(Timezone, self).__init__()
self._offset = timedelta(hours=hours, minutes=minutes)
- def utcoffset(self, dt): # pylint: disable=unused-argument
+ def utcoffset(self, dt): # pylint: disable=unused-argument
return self._offset
- def tzname(self, dt): # pylint: disable=unused-argument
- return str(self._offset)
+ def tzname(self, dt): # pylint: disable=unused-argument
+ return unicode(self._offset)
- def dst(self, dt): # pylint: disable=unused-argument
+ def dst(self, dt): # pylint: disable=unused-argument
return Timezone._ZERO
_ZERO = timedelta(0)
@@ -74,8 +74,8 @@
r'(([ \t]*)Z|(?P<tzhour>[-+][0-9][0-9])?(:(?P<tzminute>[0-9][0-9])?)?)?$'
CANONICAL = '%Y-%m-%dT%H:%M:%S'
- def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
- value = str(value)
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ value = unicode(value)
match = re.match(Timestamp.REGULAR_SHORT, value)
if match is not None:
# Parse short form
@@ -116,8 +116,8 @@
Timezone(tzhour, tzminute))
else:
raise ValueError(
- 'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": %s'
- % safe_repr(value))
+ u'timestamp must be formatted as YAML ISO8601 variant or "YYYY-MM-DD": {0}'
+ .format(safe_repr(value)))
@property
def as_datetime_utc(self):
@@ -129,8 +129,8 @@
def __str__(self):
the_datetime = self.as_datetime_utc
- return '%s%sZ' \
- % (the_datetime.strftime(Timestamp.CANONICAL), Timestamp._fraction_as_str(the_datetime))
+ return u'{0}{1}Z'.format(the_datetime.strftime(Timestamp.CANONICAL),
+ Timestamp._fraction_as_str(the_datetime))
def __repr__(self):
return repr(self.__str__())
@@ -145,7 +145,7 @@
@staticmethod
def _fraction_as_str(the_datetime):
- return '{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0')
+ return u'{0:g}'.format(the_datetime.microsecond / 1000000.0).lstrip('0')
@total_ordering
@@ -165,7 +165,7 @@
REGEX = \
r'^(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<fix>\d+)' + \
- r'((\.(?P<qualifier>\d+))(\-(?P<build>\d+))?)?)?$'
+ r'((\.(?P<qualifier>\w+))(\-(?P<build>\d+))?)?)?$'
@staticmethod
def key(version):
@@ -174,14 +174,13 @@
"""
return (version.major, version.minor, version.fix, version.qualifier, version.build)
- def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
- str_value = str(value)
- match = re.match(Version.REGEX, str_value)
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ str_value = unicode(value)
+ match = re.match(Version.REGEX, str_value, flags=re.UNICODE)
if match is None:
raise ValueError(
- 'version must be formatted as <major_version>.<minor_version>'
- '[.<fix_version>[.<qualifier>[-<build_version]]]: %s'
- % safe_repr(value))
+ u'version must be formatted as <major_version>.<minor_version>'
+ u'[.<fix_version>[.<qualifier>[-<build_version]]]: {0}'.format(safe_repr(value)))
self.value = str_value
@@ -193,8 +192,6 @@
if self.fix is not None:
self.fix = int(self.fix)
self.qualifier = match.group('qualifier')
- if self.qualifier is not None:
- self.qualifier = int(self.qualifier)
self.build = match.group('build')
if self.build is not None:
self.build = int(self.build)
@@ -215,6 +212,7 @@
return (self.major, self.minor, self.fix, self.qualifier, self.build) == \
(version.major, version.minor, version.fix, version.qualifier, version.build)
+ @implements_specification('3.2.2.1', 'tosca-simple-1.0')
def __lt__(self, version):
if self.major < version.major:
return True
@@ -225,9 +223,7 @@
if self.fix < version.fix:
return True
elif self.fix == version.fix:
- if self.qualifier < version.qualifier:
- return True
- elif self.qualifier == version.qualifier:
+ if self.qualifier == version.qualifier:
if self.build < version.build:
return True
return False
@@ -244,28 +240,29 @@
#TYPE_TOSCA_RANGE>`__
"""
- def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
if not isinstance(value, list):
- raise ValueError('range value is not a list: %s' % safe_repr(value))
+ raise ValueError(u'range value is not a list: {0}'.format(safe_repr(value)))
if len(value) != 2:
- raise ValueError('range value does not have exactly 2 elements: %s' % safe_repr(value))
+ raise ValueError(u'range value does not have exactly 2 elements: {0}'
+ .format(safe_repr(value)))
def is_int(v):
return isinstance(v, int) and (not isinstance(v, bool)) # In Python bool is an int
if not is_int(value[0]):
- raise ValueError('lower bound of range is not a valid integer: %s'
- % safe_repr(value[0]))
+ raise ValueError(u'lower bound of range is not a valid integer: {0}'
+ .format(safe_repr(value[0])))
if value[1] != 'UNBOUNDED':
if not is_int(value[1]):
- raise ValueError('upper bound of range is not a valid integer or "UNBOUNDED": %s'
- % safe_repr(value[0]))
+ raise ValueError(u'upper bound of range is not a valid integer or "UNBOUNDED": {0}'
+ .format(safe_repr(value[0])))
if value[0] >= value[1]:
raise ValueError(
- 'upper bound of range is not greater than the lower bound: %s >= %s'
- % (safe_repr(value[0]), safe_repr(value[1])))
+ u'upper bound of range is not greater than the lower bound: {0} >= {1}'
+ .format(safe_repr(value[0]), safe_repr(value[1])))
self.value = value
@@ -294,9 +291,9 @@
"""
@staticmethod
- def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
if not isinstance(value, list):
- raise ValueError('"list" data type value is not a list: %s' % safe_repr(value))
+ raise ValueError(u'"list" data type value is not a list: {0}'.format(safe_repr(value)))
entry_schema_type = entry_schema._get_type(context)
entry_schema_constraints = entry_schema.constraints
@@ -328,12 +325,12 @@
"""
@staticmethod
- def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ def _create(context, presentation, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
if not isinstance(value, dict):
- raise ValueError('"map" data type value is not a dict: %s' % safe_repr(value))
+ raise ValueError(u'"map" data type value is not a dict: {0}'.format(safe_repr(value)))
if entry_schema is None:
- raise ValueError('"map" data type does not define "entry_schema"')
+ raise ValueError(u'"map" data type does not define "entry_schema"')
entry_schema_type = entry_schema._get_type(context)
entry_schema_constraints = entry_schema.constraints
@@ -348,7 +345,7 @@
return the_map
def __init__(self, items=None):
- super(Map, self).__init__(items, key_class=str)
+ super(Map, self).__init__(items, key_class=basestring)
# Can't define as property because it's old-style Python class
def as_raw(self):
@@ -374,29 +371,31 @@
"""
return scalar.value
- def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
- str_value = str(value)
- match = re.match(self.REGEX, str_value) # pylint: disable=no-member
+ def __init__(self, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+ str_value = unicode(value)
+ match = re.match(self.REGEX, str_value, flags=re.UNICODE) # pylint: disable=no-member
if match is None:
- raise ValueError('scalar must be formatted as <scalar> <unit>: %s' % safe_repr(value))
+ raise ValueError(u'scalar must be formatted as <scalar> <unit>: {0}'
+ .format(safe_repr(value)))
self.factor = float(match.group('scalar'))
if self.factor < 0:
- raise ValueError('scalar is negative: %s' % safe_repr(self.factor))
+ raise ValueError('scalar is negative: {0}'.format(safe_repr(self.factor)))
self.unit = match.group('unit')
unit_lower = self.unit.lower()
unit_size = None
- for k, v in self.UNITS.iteritems(): # pylint: disable=no-member
+ for k, v in self.UNITS.iteritems(): # pylint: disable=no-member
if k.lower() == unit_lower:
self.unit = k
unit_size = v
break
if unit_size is None:
- raise ValueError('scalar specified with unsupported unit: %s' % safe_repr(self.unit))
+ raise ValueError(u'scalar specified with unsupported unit: {0}'
+ .format(safe_repr(self.unit)))
- self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member
+ self.value = self.TYPE(self.factor * unit_size) # pylint: disable=no-member
@property
def as_raw(self):
@@ -404,10 +403,10 @@
('value', self.value),
('factor', self.factor),
('unit', self.unit),
- ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member
+ ('unit_size', self.UNITS[self.unit]))) # pylint: disable=no-member
def __str__(self):
- return '%s %s' % (self.value, self.UNIT) # pylint: disable=no-member
+ return u'{0} {1}'.format(self.value, self.UNIT) # pylint: disable=no-member
def __repr__(self):
return repr(self.__str__())
@@ -416,14 +415,14 @@
if isinstance(scalar, Scalar):
value = scalar.value
else:
- value = self.TYPE(scalar) # pylint: disable=no-member
+ value = self.TYPE(scalar) # pylint: disable=no-member
return self.value == value
def __lt__(self, scalar):
if isinstance(scalar, Scalar):
value = scalar.value
else:
- value = self.TYPE(scalar) # pylint: disable=no-member
+ value = self.TYPE(scalar) # pylint: disable=no-member
return self.value < value
@@ -509,12 +508,12 @@
# The following are hooked in the YAML as 'coerce_value' extensions
#
-def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_timestamp(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
return coerce_to_data_type_class(context, presentation, Timestamp, entry_schema, constraints,
value, aspect)
-def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_version(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
return coerce_to_data_type_class(context, presentation, Version, entry_schema, constraints,
value, aspect)
@@ -533,23 +532,23 @@
value, aspect)
-def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_list(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
return coerce_to_data_type_class(context, presentation, List, entry_schema, constraints,
value, aspect)
-def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
+def coerce_map_value(context, presentation, the_type, entry_schema, constraints, value, aspect): # pylint: disable=unused-argument
return coerce_to_data_type_class(context, presentation, Map, entry_schema, constraints, value,
aspect)
-def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_scalar_unit_size(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
aspect):
return coerce_to_data_type_class(context, presentation, ScalarSize, entry_schema, constraints,
value, aspect)
-def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_scalar_unit_time(context, presentation, the_type, entry_schema, constraints, value, # pylint: disable=unused-argument
aspect):
return coerce_to_data_type_class(context, presentation, ScalarTime, entry_schema, constraints,
value, aspect)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/definitions.py b/extensions/aria_extension_tosca/simple_v1_0/definitions.py
index 9158776..a7e2806 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/definitions.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/definitions.py
@@ -15,7 +15,9 @@
from aria.utils.collections import FrozenDict
from aria.utils.caching import cachedmethod
+from aria.utils.formatting import safe_repr
from aria.parser import implements_specification
+from aria.parser.validation import Issue
from aria.parser.presentation import (has_fields, short_form_field, allow_unknown_fields,
primitive_field, primitive_list_field, object_field,
object_list_field, object_dict_field,
@@ -192,8 +194,8 @@
@has_fields
-@implements_specification('3.5.12', 'tosca-simple-1.0')
-class ParameterDefinition(PropertyDefinition):
+@implements_specification('3.5.12-1', 'tosca-simple-1.0')
+class InputDefinition(PropertyDefinition):
"""
A parameter definition is essentially a TOSCA property definition; however, it also allows a
value to be assigned to it (as for a TOSCA property assignment). In addition, in the case of
@@ -205,6 +207,18 @@
#DEFN_ELEMENT_PARAMETER_DEF>`__
"""
+ @field_validator(data_value_validator)
+ @primitive_field()
+ def value(self):
+ """
+ The type-compatible value to assign to the named parameter. Parameter values may be provided
+ as the result from the evaluation of an expression or a function.
+ """
+
+
+@has_fields
+@implements_specification('3.5.12-2', 'tosca-simple-1.0')
+class OutputDefinition(InputDefinition):
@field_validator(data_type_validator())
@primitive_field(str)
def type(self):
@@ -214,15 +228,10 @@
Note: This keyname is required for a TOSCA Property definition, but is not for a TOSCA
Parameter definition.
- :type: :obj:`basestring`
- """
+ ARIA NOTE: the spec must be mistaken: inputs should have this field requires, only outputs
+ have it as optional.
- @field_validator(data_value_validator)
- @primitive_field()
- def value(self):
- """
- The type-compatible value to assign to the named parameter. Parameter values may be provided
- as the result from the evaluation of an expression or a function.
+ :type: :obj:`basestring`
"""
@@ -283,7 +292,7 @@
@field_validator(type_validator('interface type', convert_name_to_full_type_name,
'interface_types'))
- @primitive_field(str)
+ @primitive_field(str, required=True)
def type(self):
"""
ARIA NOTE: This field is not mentioned in the spec, but is implied.
@@ -322,9 +331,8 @@
def _validate(self, context):
super(InterfaceDefinition, self)._validate(context)
- if self.operations:
- for operation in self.operations.itervalues(): # pylint: disable=no-member
- operation._validate(context)
+ self._get_inputs(context)
+ self._get_operations(context)
@short_form_field('type')
@@ -342,6 +350,8 @@
The optional reserved keyname used to provide the name of the Relationship Type for the
requirement definition's relationship keyname.
+ ARIA NOTE: the spec shows this as a required field.
+
:type: :obj:`basestring`
"""
@@ -428,6 +438,16 @@
def _get_node_type(self, context):
return context.presentation.get_from_dict('service_template', 'node_types', self.node)
+ def _validate(self, context):
+ super(RequirementDefinition, self)._validate(context)
+ occurrences = self.occurrences
+ if (occurrences is not None) and ((occurrences.value[0] < 0) or \
+ ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))):
+ context.validation.report(
+ 'requirements definition "{0}" occurrences range includes negative integers: {1}'
+ .format(self._name, safe_repr(occurrences)),
+ locator=self._locator, level=Issue.BETWEEN_TYPES)
+
@short_form_field('type')
@has_fields
@@ -516,3 +536,13 @@
if container_parent is not None else None
return container_parent_capabilities.get(self._name) \
if container_parent_capabilities is not None else None
+
+ def _validate(self, context):
+ super(CapabilityDefinition, self)._validate(context)
+ occurrences = self.occurrences
+ if (occurrences is not None) and ((occurrences.value[0] < 0) or \
+ ((occurrences.value[1] != 'UNBOUNDED') and (occurrences.value[1] < 0))):
+ context.validation.report(
+ 'capability definition "{0}" occurrences range includes negative integers: {1}'
+ .format(self._name, safe_repr(occurrences)),
+ locator=self._locator, level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/misc.py b/extensions/aria_extension_tosca/simple_v1_0/misc.py
index 221163c..914691e 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/misc.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/misc.py
@@ -20,14 +20,16 @@
from aria.parser.presentation import (AsIsPresentation, has_fields, allow_unknown_fields,
short_form_field, primitive_field, primitive_list_field,
primitive_dict_unknown_fields, object_field,
- object_list_field, object_dict_field, field_validator,
- type_validator)
+ object_list_field, object_dict_field, field_getter,
+ field_validator, type_validator, not_negative_validator)
+from .data_types import Version
from .modeling.data_types import (get_data_type, get_data_type_value, get_property_constraints,
apply_constraint_to_value)
from .modeling.substitution_mappings import (validate_substitution_mappings_requirement,
validate_substitution_mappings_capability)
from .presentation.extensible import ExtensiblePresentation
+from .presentation.field_getters import data_type_class_getter
from .presentation.field_validators import (constraint_clause_field_validator,
constraint_clause_in_range_validator,
constraint_clause_valid_values_validator,
@@ -47,7 +49,7 @@
#DEFN_ELEMENT_DESCRIPTION>`__
"""
- def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument
+ def __init__(self, name=None, raw=None, container=None, cls=None): # pylint: disable=unused-argument
super(Description, self).__init__(name, raw, container, cls=unicode)
def _dump(self, context):
@@ -79,6 +81,7 @@
as a single-line string value.
"""
+ @field_getter(data_type_class_getter(Version, allow_null=True))
@primitive_field(str)
@implements_specification('3.9.3.5', 'tosca-simple-1.0')
def template_version(self):
@@ -87,7 +90,7 @@
service template as a single-line string value.
"""
- @primitive_dict_unknown_fields()
+ @primitive_dict_unknown_fields(str)
def custom(self):
"""
:type: dict
@@ -135,6 +138,10 @@
def _get_credential(self, context):
return get_data_type_value(context, self, 'credential', 'tosca.datatypes.Credential')
+ def _validate(self, context):
+ super(Repository, self)._validate(context)
+ self._get_credential(context)
+
@short_form_field('file')
@has_fields
@@ -255,18 +262,21 @@
Constrains a property or parameter to a value that is in the list of declared values.
"""
+ @field_validator(not_negative_validator)
@primitive_field(int)
def length(self):
"""
Constrains the property or parameter to a value of a given length.
"""
+ @field_validator(not_negative_validator)
@primitive_field(int)
def min_length(self):
"""
Constrains the property or parameter to a value to a minimum length.
"""
+ @field_validator(not_negative_validator)
@primitive_field(int)
def max_length(self):
"""
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 0c621a0..6c305c3 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/__init__.py
@@ -27,7 +27,7 @@
from aria.parser.validation import Issue
-from aria.utils.formatting import string_list_as_string
+from aria.utils.formatting import (string_list_as_string, safe_repr)
from aria.utils.collections import (StrictDict, OrderedDict)
from aria.utils.yaml import yaml
from aria.orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS
@@ -49,7 +49,7 @@
IMPLEMENTATION_PREFIX_REGEX = re.compile(r'(?<!\\)(?:\\\\)*>')
-def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches
+def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches
model = ServiceTemplate(created_at=datetime.now(),
main_file_name=os.path.basename(str(context.presentation.location)))
@@ -315,6 +315,10 @@
create_parameter_models_from_assignments(model.properties,
relationship.properties,
model_cls=Property)
+ # TODO: does not exist in models, but should
+ #create_parameter_models_from_assignments(model.attributes,
+ # relationship.attributes,
+ # model_cls=Attribute)
create_interface_template_models(context, service_template, model.interface_templates,
relationship.interfaces)
@@ -346,6 +350,10 @@
create_parameter_models_from_assignments(model.properties,
capability.properties,
model_cls=Property)
+ # TODO: does not exist in models, but should
+ #create_parameter_models_from_assignments(model.attributes,
+ # capability.attributes,
+ # model_cls=Attribute)
return model
@@ -399,7 +407,7 @@
value = yaml.load(value, Loader=yaml.SafeLoader)
except yaml.parser.MarkedYAMLError as e:
context.validation.report(
- 'YAML parser {0} in operation configuration: {1}'
+ u'YAML parser {0} in operation configuration: {1}'
.format(e.problem, value),
locator=implementation._locator,
level=Issue.FIELD)
@@ -518,7 +526,7 @@
used_reserved_names = WORKFLOW_DECORATOR_RESERVED_ARGUMENTS.intersection(model.inputs.keys())
if used_reserved_names:
- context.validation.report('using reserved arguments in workflow policy "{0}": {1}'
+ context.validation.report(u'using reserved arguments in workflow policy "{0}": {1}'
.format(
policy._name,
string_list_as_string(used_reserved_names)),
@@ -630,7 +638,8 @@
for property_name, constraint_clause in properties:
constraint = create_constraint(context, node_filter, constraint_clause, property_name,
None)
- target_node_template_constraints.append(constraint)
+ if constraint is not None:
+ target_node_template_constraints.append(constraint)
capabilities = node_filter.capabilities
if capabilities is not None:
@@ -640,24 +649,33 @@
for property_name, constraint_clause in properties:
constraint = create_constraint(context, node_filter, constraint_clause,
property_name, capability_name)
- target_node_template_constraints.append(constraint)
+ if constraint is not None:
+ target_node_template_constraints.append(constraint)
-def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements
+def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements
+ if (not isinstance(constraint_clause._raw, dict)) or (len(constraint_clause._raw) != 1):
+ context.validation.report(
+ u'node_filter constraint is not a dict with one key: {0}'
+ .format(safe_repr(constraint_clause._raw)),
+ locator=node_filter._locator,
+ level=Issue.FIELD)
+ return None
+
constraint_key = constraint_clause._raw.keys()[0]
- the_type = constraint_clause._get_type(context)
+ value_type = constraint_clause._get_type(context)
- def coerce_constraint(constraint):
- if the_type is not None:
- return coerce_value(context, node_filter, the_type, None, None, constraint,
+ def coerce_constraint(constraint, value_type=value_type):
+ if value_type is not None:
+ return coerce_value(context, node_filter, value_type, None, None, constraint,
constraint_key)
else:
return constraint
- def coerce_constraints(constraints):
- if the_type is not None:
- return tuple(coerce_constraint(constraint) for constraint in constraints)
+ def coerce_constraints(constraints, value_type=value_type):
+ if value_type is not None:
+ return tuple(coerce_constraint(constraint, value_type) for constraint in constraints)
else:
return constraints
@@ -684,18 +702,22 @@
coerce_constraints(constraint_clause.valid_values))
elif constraint_key == 'length':
return Length(property_name, capability_name,
- coerce_constraint(constraint_clause.length))
+ coerce_constraint(constraint_clause.length, int))
elif constraint_key == 'min_length':
return MinLength(property_name, capability_name,
- coerce_constraint(constraint_clause.min_length))
+ coerce_constraint(constraint_clause.min_length, int))
elif constraint_key == 'max_length':
return MaxLength(property_name, capability_name,
- coerce_constraint(constraint_clause.max_length))
+ coerce_constraint(constraint_clause.max_length, int))
elif constraint_key == 'pattern':
return Pattern(property_name, capability_name,
- coerce_constraint(constraint_clause.pattern))
+ coerce_constraint(constraint_clause.pattern, unicode))
else:
- raise ValueError('malformed node_filter: {0}'.format(constraint_key))
+ context.validation.report(
+ u'unsupported node_filter constraint: {0}'.format(constraint_key),
+ locator=node_filter._locator,
+ level=Issue.FIELD)
+ return None
def split_prefix(string):
@@ -703,7 +725,7 @@
Splits the prefix on the first non-escaped ">".
"""
- split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1)
+ split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1) if string is not None else ()
if len(split) < 2:
return None, None
return split[0].strip(), split[1].strip()
@@ -740,7 +762,7 @@
model.function = postfix
if model.plugin_specification is None:
context.validation.report(
- 'no policy for plugin "{0}" specified in operation implementation: {1}'
+ u'no policy for plugin "{0}" specified in operation implementation: {1}'
.format(prefix, primary),
locator=presentation._get_child_locator('properties', 'implementation'),
level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
index 1b95bec..bf6636b 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/capabilities.py
@@ -73,8 +73,8 @@
if not type1._is_descendant(context, type2):
context.validation.report(
- 'capability definition type "{0}" is not a descendant of overridden '
- 'capability definition type "{1}"' \
+ u'capability definition type "{0}" is not a descendant of overridden '
+ u'capability definition type "{1}"' \
.format(type1._name, type2._name),
locator=our_capability_definition._locator, level=Issue.BETWEEN_TYPES)
@@ -139,9 +139,18 @@
if values:
capability_assignment._raw['properties'] = values
capability_assignment._reset_method_cache()
+
+ # Assign attributes
+ values = get_assigned_and_defined_parameter_values(context,
+ our_capability_assignment,
+ 'attribute')
+
+ if values:
+ capability_assignment._raw['attributes'] = values
+ capability_assignment._reset_method_cache()
else:
context.validation.report(
- 'capability "{0}" not declared at node type "{1}" in "{2}"'
+ u'capability "{0}" not declared at node type "{1}" in "{2}"'
.format(capability_name, presentation.type, presentation._fullname),
locator=our_capability_assignment._locator, level=Issue.BETWEEN_TYPES)
@@ -161,29 +170,38 @@
if properties is not None:
raw['properties'] = convert_parameter_definitions_to_values(context, properties)
- # TODO attributes
+ attributes = presentation.attributes
+ if attributes is not None:
+ raw['attributes'] = convert_parameter_definitions_to_values(context, attributes)
return CapabilityAssignment(name=presentation._name, raw=raw, container=container)
def merge_capability_definition(context, presentation, capability_definition,
from_capability_definition):
- raw_properties = OrderedDict()
-
capability_definition._raw['type'] = from_capability_definition.type
- # Merge properties from type
- from_property_defintions = from_capability_definition.properties
- merge_raw_parameter_definitions(context, presentation, raw_properties, from_property_defintions,
- 'properties')
+ raw_properties = OrderedDict()
+ raw_attributes = OrderedDict()
- # Merge our properties
+ # Merge parameters from type
merge_raw_parameter_definitions(context, presentation, raw_properties,
capability_definition.properties, 'properties')
+ merge_raw_parameter_definitions(context, presentation, raw_attributes,
+ capability_definition.attributes, 'attributes')
+
+ # Merge our parameters
+ merge_raw_parameter_definitions(context, presentation, raw_properties,
+ from_capability_definition.properties, 'properties')
+ merge_raw_parameter_definitions(context, presentation, raw_attributes,
+ from_capability_definition.attributes, 'attributes')
if raw_properties:
capability_definition._raw['properties'] = raw_properties
capability_definition._reset_method_cache()
+ if raw_attributes:
+ capability_definition._raw['attributes'] = raw_attributes
+ capability_definition._reset_method_cache()
# Merge occurrences
occurrences = from_capability_definition._raw.get('occurrences')
@@ -194,23 +212,31 @@
def merge_capability_definition_from_type(context, presentation, capability_definition):
"""
- Merge ``properties`` and ``valid_source_types`` from the node type's capability definition
- over those taken from the parent node type.
+ Merge ``properties``, ``attributes``, and ``valid_source_types`` from the node type's capability
+ definition over those taken from the parent node type.
"""
raw_properties = OrderedDict()
+ raw_attributes = OrderedDict()
- # Merge properties from parent
+ # Merge parameters from parent
the_type = capability_definition._get_type(context)
type_property_defintions = the_type._get_properties(context)
+ type_attribute_defintions = the_type._get_attributes(context)
merge_raw_parameter_definitions(context, presentation, raw_properties, type_property_defintions,
'properties')
+ merge_raw_parameter_definitions(context, presentation, raw_attributes,
+ type_attribute_defintions, 'attributes')
- # Merge our properties (might override definitions in parent)
+ # Merge our parameters (might override definitions in parent)
merge_raw_parameter_definitions(context, presentation, raw_properties,
capability_definition.properties, 'properties')
+ merge_raw_parameter_definitions(context, presentation, raw_attributes,
+ capability_definition.attributes, 'attributes')
if raw_properties:
capability_definition._raw['properties'] = raw_properties
+ if raw_attributes:
+ capability_definition._raw['attributes'] = raw_attributes
# Override valid_source_types
if capability_definition._raw.get('valid_source_types') is None:
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
index bd9037f..b3c2e49 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/copy.py
@@ -23,7 +23,7 @@
"""
copy = presentation._raw.get('copy')
- if copy is not None:
+ if isinstance(copy, basestring):
templates = getattr(presentation._container, field_name)
if templates is not None:
template = templates.get(copy)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
index 13ce9a3..31865b9 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/data_types.py
@@ -52,7 +52,7 @@
return constraints
-def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument
+def coerce_data_type_value(context, presentation, data_type, entry_schema, constraints, value, # pylint: disable=unused-argument
aspect):
"""
Handles the ``_coerce_data()`` hook for complex data types.
@@ -90,8 +90,8 @@
aspect)
else:
context.validation.report(
- 'assignment to undefined property "%s" in type "%s" in "%s"'
- % (name, data_type._fullname, presentation._fullname),
+ u'assignment to undefined property "{0}" in type "{1}" in "{2}"'
+ .format(name, data_type._fullname, presentation._fullname),
locator=get_locator(v, value, presentation), level=Issue.BETWEEN_TYPES)
# Fill in defaults from the definitions, and check if required definitions have not been
@@ -108,15 +108,15 @@
if getattr(definition, 'required', False) and (temp.get(name) is None):
context.validation.report(
- 'required property "%s" in type "%s" is not assigned a value in "%s"'
- % (name, data_type._fullname, presentation._fullname),
+ u'required property "{0}" in type "{1}" is not assigned a value in "{2}"'
+ .format(name, data_type._fullname, presentation._fullname),
locator=presentation._get_child_locator('definitions'),
level=Issue.BETWEEN_TYPES)
value = temp
elif value is not None:
- context.validation.report('value of type "%s" is not a dict in "%s"'
- % (data_type._fullname, presentation._fullname),
+ context.validation.report(u'value of type "{0}" is not a dict in "{1}"'
+ .format(data_type._fullname, presentation._fullname),
locator=get_locator(value, presentation),
level=Issue.BETWEEN_TYPES)
value = None
@@ -131,8 +131,8 @@
name = presentation._name
if get_primitive_data_type(name) is not None:
- context.validation.report('data type name is that of a built-in type: %s'
- % safe_repr(name),
+ context.validation.report(u'data type name is that of a built-in type: {0}'
+ .format(safe_repr(name)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
@@ -201,7 +201,7 @@
# ConstraintClause
#
-def apply_constraint_to_value(context, presentation, constraint_clause, value): # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches
+def apply_constraint_to_value(context, presentation, constraint_clause, value): # pylint: disable=too-many-statements,too-many-return-statements,too-many-branches
"""
Returns false if the value does not conform to the constraint.
"""
@@ -216,10 +216,10 @@
constraint_key)
def report(message, constraint):
- context.validation.report('value %s %s per constraint in "%s": %s'
- % (message, safe_repr(constraint),
- presentation._name or presentation._container._name,
- safe_repr(value)),
+ context.validation.report(u'value {0} {1} per constraint in "{2}": {3}'
+ .format(message, safe_repr(constraint),
+ presentation._name or presentation._container._name,
+ safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
if constraint_key == 'equal':
@@ -318,15 +318,15 @@
#
def get_data_type_value(context, presentation, field_name, type_name):
- the_type = get_type_by_name(context, type_name, 'data_types')
- if the_type is not None:
- value = getattr(presentation, field_name)
- if value is not None:
- return coerce_data_type_value(context, presentation, the_type, None, None, value, None)
- else:
- context.validation.report('field "%s" in "%s" refers to unknown data type "%s"'
- % (field_name, presentation._fullname, type_name),
- locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+ value = getattr(presentation, field_name)
+ if value is not None:
+ data_type = get_type_by_name(context, type_name, 'data_types')
+ if data_type is not None:
+ return coerce_data_type_value(context, presentation, data_type, None, None, value, None)
+ else:
+ context.validation.report(u'field "{0}" in "{1}" refers to unknown data type "{2}"'
+ .format(field_name, presentation._fullname, type_name),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return None
@@ -372,7 +372,7 @@
return the_type._name if hasattr(the_type, '_name') else full_type_name(the_type)
-def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None): # pylint: disable=too-many-return-statements
+def coerce_value(context, presentation, the_type, entry_schema, constraints, value, aspect=None): # pylint: disable=too-many-return-statements
"""
Returns the value after it's coerced to its type, reporting validation errors if it cannot be
coerced.
@@ -394,8 +394,8 @@
if the_type == None.__class__:
if value is not None:
- context.validation.report('field "%s" is of type "null" but has a non-null value: %s'
- % (presentation._name, safe_repr(value)),
+ context.validation.report(u'field "{0}" is of type "null" but has a non-null value: {1}'
+ .format(presentation._name, safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
return None
@@ -498,17 +498,18 @@
if aspect == 'default':
aspect = '"default" value'
elif aspect is not None:
- aspect = '"%s" aspect' % aspect
+ aspect = u'"{0}" aspect'.format(aspect)
if aspect is not None:
- context.validation.report('%s for field "%s" is not a valid "%s": %s'
- % (aspect, presentation._name or presentation._container._name,
- get_data_type_name(the_type), safe_repr(value)),
+ context.validation.report(u'{0} for field "{1}" is not a valid "{2}": {3}'
+ .format(aspect,
+ presentation._name or presentation._container._name,
+ get_data_type_name(the_type), safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
exception=e)
else:
- context.validation.report('field "%s" is not a valid "%s": %s'
- % (presentation._name or presentation._container._name,
- get_data_type_name(the_type), safe_repr(value)),
+ context.validation.report(u'field "{0}" is not a valid "{1}": {2}'
+ .format(presentation._name or presentation._container._name,
+ get_data_type_name(the_type), safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_FIELDS,
exception=e)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
index ecbfde9..16c49fa 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/functions.py
@@ -24,6 +24,7 @@
from aria.parser.validation import Issue
from aria.modeling.exceptions import CannotEvaluateFunctionException
from aria.modeling.models import (Node, NodeTemplate, Relationship, RelationshipTemplate)
+from aria.modeling.mixins import ParameterMixin
from aria.modeling.functions import (Function, Evaluation)
@@ -43,7 +44,7 @@
if not isinstance(argument, list):
raise InvalidValueError(
- 'function "concat" argument must be a list of string expressions: {0}'
+ u'function "concat" argument must be a list of string expressions: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -84,7 +85,7 @@
self.locator = presentation._locator
if (not isinstance(argument, list)) or (len(argument) != 3):
- raise InvalidValueError('function "token" argument must be a list of 3 parameters: {0}'
+ raise InvalidValueError(u'function "token" argument must be a list of 3 parameters: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -95,6 +96,10 @@
argument[1])
self.substring_index = parse_int(context, presentation, 'token', 2,
'the 0-based index of the token to return', argument[2])
+ if self.substring_index < 0:
+ raise invalid_value('token', 2, 'a non-negative integer',
+ 'the 0-based index of the token to return', self.substring_index,
+ presentation._locator)
@property
def as_raw(self):
@@ -143,7 +148,7 @@
'inputs', self.input_property_name)
if the_input is None:
raise InvalidValueError(
- 'function "get_input" argument is not a valid input name: {0}'
+ u'function "get_input" argument is not a valid input name: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -162,7 +167,7 @@
return Evaluation(value, False) # We never return final evaluations!
raise InvalidValueError(
- 'function "get_input" argument is not a valid input name: {0}'
+ u'function "get_input" argument is not a valid input name: {0}'
.format(safe_repr(self.input_property_name)),
locator=self.locator)
@@ -179,8 +184,8 @@
if (not isinstance(argument, list)) or (len(argument) < 2):
raise InvalidValueError(
- 'function "get_property" argument must be a list of at least 2 string expressions: '
- '{0}'.format(safe_repr(argument)),
+ u'function "get_property" argument must be a list of at least 2 string expressions:'
+ u' {0}'.format(safe_repr(argument)),
locator=self.locator)
self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
@@ -224,14 +229,13 @@
properties = modelable_entity.properties
nested_property_name_or_index = self.nested_property_name_or_index
- evaluation = get_modelable_entity_parameter(modelable_entity, properties,
- nested_property_name_or_index)
+ evaluation = get_modelable_entity_parameter(properties, nested_property_name_or_index)
if evaluation is not None:
return evaluation
raise InvalidValueError(
- 'function "get_property" could not find "{0}" in modelable entity "{1}"'
- .format('.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
+ u'function "get_property" could not find "{0}" in modelable entity "{1}"'
+ .format(u'.'.join(self.nested_property_name_or_index), self.modelable_entity_name),
locator=self.locator)
@@ -251,8 +255,8 @@
if (not isinstance(argument, list)) or (len(argument) < 2):
raise InvalidValueError(
- 'function "get_attribute" argument must be a list of at least 2 string expressions:'
- ' {0}'.format(safe_repr(argument)),
+ u'function "get_attribute" argument must be a list of at least 2 string '
+ u'expressions: {0}'.format(safe_repr(argument)),
locator=self.locator)
self.modelable_entity_name = parse_modelable_entity_name(context, presentation,
@@ -270,15 +274,14 @@
for modelable_entity in modelable_entities:
attributes = modelable_entity.attributes
nested_attribute_name_or_index = self.nested_attribute_name_or_index
- evaluation = get_modelable_entity_parameter(modelable_entity, attributes,
- nested_attribute_name_or_index)
+ evaluation = get_modelable_entity_parameter(attributes, nested_attribute_name_or_index)
if evaluation is not None:
evaluation.final = False # We never return final evaluations!
return evaluation
raise InvalidValueError(
- 'function "get_attribute" could not find "{0}" in modelable entity "{1}"'
- .format('.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name),
+ u'function "get_attribute" could not find "{0}" in modelable entity "{1}"'
+ .format(u'.'.join(self.nested_attribute_name_or_index), self.modelable_entity_name),
locator=self.locator)
@@ -286,7 +289,7 @@
# Operation
#
-@implements_specification('4.6.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
+@implements_specification('4.6.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
class GetOperationOutput(Function):
"""
The ``get_operation_output`` function is used to retrieve the values of variables exposed /
@@ -298,7 +301,7 @@
if (not isinstance(argument, list)) or (len(argument) != 4):
raise InvalidValueError(
- 'function "get_operation_output" argument must be a list of 4 parameters: {0}'
+ u'function "get_operation_output" argument must be a list of 4 parameters: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -327,6 +330,9 @@
return {'get_operation_output': [self.modelable_entity_name, interface_name, operation_name,
output_variable_name]}
+ def __evaluate__(self, container):
+ return Evaluation(None)
+
#
# Navigation
@@ -349,7 +355,7 @@
node_types = context.presentation.get('service_template', 'node_types')
if (node_types is None) or (self.node_type_name not in node_types):
raise InvalidValueError(
- 'function "get_nodes_of_type" argument is not a valid node type name: {0}'
+ u'function "get_nodes_of_type" argument is not a valid node type name: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -361,14 +367,14 @@
return {'get_nodes_of_type': node_type_name}
def __evaluate__(self, container):
- pass
+ return Evaluation(None)
#
# Artifact
#
-@implements_specification('4.8.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
+@implements_specification('4.8.1', 'tosca-simple-1.0') # pylint: disable=abstract-method
class GetArtifact(Function):
"""
The ``get_artifact`` function is used to retrieve artifact location between modelable
@@ -380,7 +386,7 @@
if (not isinstance(argument, list)) or (len(argument) < 2) or (len(argument) > 4):
raise InvalidValueError(
- 'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
+ u'function "get_artifact" argument must be a list of 2 to 4 parameters: {0}'
.format(safe_repr(argument)),
locator=self.locator)
@@ -389,10 +395,16 @@
argument[0])
self.artifact_name = parse_string_expression(context, presentation, 'get_artifact', 1,
'the artifact name', argument[1])
- self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
- 'the location or "LOCAL_FILE"', argument[2])
- self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
- argument[3])
+ if len(argument) > 2:
+ self.location = parse_string_expression(context, presentation, 'get_artifact', 2,
+ 'the location or "LOCAL_FILE"', argument[2])
+ else:
+ self.location = None
+ if len(argument) > 3:
+ self.remove = parse_bool(context, presentation, 'get_artifact', 3, 'the removal flag',
+ argument[3])
+ else:
+ self.remove = None
@property
def as_raw(self):
@@ -400,9 +412,22 @@
if hasattr(artifact_name, 'as_raw'):
artifact_name = as_raw(artifact_name)
location = self.location
- if hasattr(location, 'as_raw'):
- location = as_raw(location)
- return {'get_artifacts': [self.modelable_entity_name, artifact_name, location, self.remove]}
+ if location is not None:
+ if hasattr(location, 'as_raw'):
+ location = as_raw(location)
+ remove = self.remove
+ if hasattr(remove, 'as_raw'):
+ remove = as_raw(remove)
+ if remove is not None:
+ return {'get_artifact': [self.modelable_entity_name, artifact_name, location,
+ remove]}
+ else:
+ return {'get_artifact': [self.modelable_entity_name, artifact_name, location]}
+ else:
+ return {'get_artifact': [self.modelable_entity_name, artifact_name]}
+
+ def __evaluate__(self, container):
+ return Evaluation(None)
#
@@ -422,16 +447,16 @@
return False, None
-def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_string_expression(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
is_function, func = get_function(context, presentation, value)
if is_function:
return func
else:
- value = str(value)
+ value = unicode(value)
return value
-def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_int(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
if not isinstance(value, int):
try:
value = int(value)
@@ -441,7 +466,7 @@
return value
-def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
+def parse_bool(context, presentation, name, index, explanation, value): # pylint: disable=unused-argument
if not isinstance(value, bool):
raise invalid_value(name, index, 'a boolean', explanation, value, presentation._locator)
return value
@@ -475,7 +500,7 @@
or {}
if (value not in node_templates) and (value not in relationship_templates):
raise InvalidValueError(
- 'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
+ u'function "{0}" parameter {1:d} is not a valid modelable entity name: {2}'
.format(name, index + 1, safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return value
@@ -548,7 +573,7 @@
return modelable_entities
- raise InvalidValueError('function "{0}" could not find modelable entity "{1}"'
+ raise InvalidValueError(u'function "{0}" could not find modelable entity "{1}"'
.format(name, modelable_entity_name),
locator=locator)
@@ -564,9 +589,9 @@
(not isinstance(container, NodeTemplate)) and \
(not isinstance(container, Relationship)) and \
(not isinstance(container, RelationshipTemplate)):
- raise InvalidValueError('function "{0}" refers to "SELF" but it is not contained in '
- 'a node or a relationship: {1}'.format(name,
- full_type_name(container)),
+ raise InvalidValueError(u'function "{0}" refers to "SELF" but it is not contained in '
+ u'a node or a relationship: {1}'.format(name,
+ full_type_name(container)),
locator=locator)
return [container]
@@ -586,8 +611,8 @@
container = container_holder.container
if (not isinstance(container, Node)) and (not isinstance(container, NodeTemplate)):
- raise InvalidValueError('function "{0}" refers to "HOST" but it is not contained in '
- 'a node: {1}'.format(name, full_type_name(container)),
+ raise InvalidValueError(u'function "{0}" refers to "HOST" but it is not contained in '
+ u'a node: {1}'.format(name, full_type_name(container)),
locator=locator)
if not isinstance(container, Node):
@@ -611,8 +636,8 @@
container = container_holder.container
if (not isinstance(container, Relationship)) and \
(not isinstance(container, RelationshipTemplate)):
- raise InvalidValueError('function "{0}" refers to "SOURCE" but it is not contained in '
- 'a relationship: {1}'.format(name, full_type_name(container)),
+ raise InvalidValueError(u'function "{0}" refers to "SOURCE" but it is not contained in '
+ u'a relationship: {1}'.format(name, full_type_name(container)),
locator=locator)
if not isinstance(container, RelationshipTemplate):
@@ -631,8 +656,8 @@
container = container_holder.container
if (not isinstance(container, Relationship)) and \
(not isinstance(container, RelationshipTemplate)):
- raise InvalidValueError('function "{0}" refers to "TARGET" but it is not contained in '
- 'a relationship: {1}'.format(name, full_type_name(container)),
+ raise InvalidValueError(u'function "{0}" refers to "TARGET" but it is not contained in '
+ u'a relationship: {1}'.format(name, full_type_name(container)),
locator=locator)
if not isinstance(container, RelationshipTemplate):
@@ -642,21 +667,25 @@
return [container.target_node]
-def get_modelable_entity_parameter(modelable_entity, parameters, nested_parameter_name_or_index):
+def get_modelable_entity_parameter(parameters, nested_parameter_name_or_index):
if not parameters:
return Evaluation(None, True)
found = True
final = True
value = parameters
+ last_container = parameters
for name_or_index in nested_parameter_name_or_index:
if (isinstance(value, dict) and (name_or_index in value)) \
or ((isinstance(value, list) and (name_or_index < len(value)))):
- value = value[name_or_index] # Parameter
- # We are not using Parameter.value, but rather Parameter._value, because we want to make
- # sure to get "final" (it is swallowed by Parameter.value)
- value, final = evaluate(value._value, final, value)
+ value = value[name_or_index]
+ if isinstance(value, ParameterMixin):
+ last_container = value
+ # We are not using Parameter.value, but rather Parameter._value, because we want to
+ # make sure to get "final" (it is swallowed by Parameter.value)
+ value = value._value
+ value, final = evaluate(value, final, last_container)
else:
found = False
break
@@ -665,17 +694,17 @@
def invalid_modelable_entity_name(name, index, value, locator, contexts):
- return InvalidValueError('function "{0}" parameter {1:d} can be "{2}" only in {3}'
+ return InvalidValueError(u'function "{0}" parameter {1:d} can be "{2}" only in {3}'
.format(name, index + 1, value, contexts),
locator=locator, level=Issue.FIELD)
def invalid_value(name, index, the_type, explanation, value, locator):
return InvalidValueError(
- 'function "{0}" {1} is not {2}{3}: {4}'
+ u'function "{0}" {1} is not {2}{3}: {4}'
.format(name,
- 'parameter {0:d}'.format(index + 1) if index is not None else 'argument',
+ u'parameter {0:d}'.format(index + 1) if index is not None else 'argument',
the_type,
- ', {0}'.format(explanation) if explanation is not None else '',
+ u', {0}'.format(explanation) if explanation is not None else '',
safe_repr(value)),
locator=locator, level=Issue.FIELD)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py
new file mode 100644
index 0000000..1d8013b
--- /dev/null
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/groups.py
@@ -0,0 +1,43 @@
+# 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 ..presentation.types import convert_name_to_full_type_name
+
+
+#
+# GroupType
+#
+
+def get_inherited_members(context, presentation):
+ """
+ Returns our target node types if we have them or those of our parent, if we have one
+ (recursively).
+ """
+
+ parent = presentation._get_parent(context)
+
+ node_types = get_inherited_members(context, parent) if parent is not None else []
+
+ our_members = presentation.members
+ if our_members:
+ all_node_types = context.presentation.get('service_template', 'node_types') or {}
+ node_types = []
+
+ for our_member in our_members:
+ if our_member in all_node_types:
+ our_member = convert_name_to_full_type_name(context, our_member, all_node_types)
+ node_types.append(all_node_types[our_member])
+
+ return node_types
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
index 23a03b7..4450fc2 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/interfaces.py
@@ -18,6 +18,7 @@
from aria.parser.validation import Issue
from .parameters import (coerce_parameter_value, convert_parameter_definitions_to_values)
+from .data_types import (get_type_by_name, get_primitive_data_type)
#
@@ -156,7 +157,7 @@
interface_definitions = the_type._get_interfaces(context) if the_type is not None else None
# Copy over interfaces from the type (will initialize inputs with default values)
- if interface_definitions is not None:
+ if interface_definitions:
for interface_name, interface_definition in interface_definitions.iteritems():
# Note that in the case of a RelationshipTemplate, we will already have the values as
# InterfaceAssignment. It will not be converted, just cloned.
@@ -177,8 +178,8 @@
our_interface_assignment, interface_definition, interface_name)
else:
context.validation.report(
- 'interface definition "%s" not declared at %s "%s" in "%s"'
- % (interface_name, type_name, presentation.type, presentation._fullname),
+ u'interface definition "{0}" not declared at {1} "{2}" in "{3}"'
+ .format(interface_name, type_name, presentation.type, presentation._fullname),
locator=our_interface_assignment._locator, level=Issue.BETWEEN_TYPES)
# Check that there are no required inputs that we haven't assigned
@@ -209,7 +210,7 @@
return InterfaceAssignment(name=presentation._name, raw=raw, container=container)
-def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name
+def convert_interface_definition_from_type_to_raw_template(context, presentation): # pylint: disable=invalid-name
raw = OrderedDict()
# Copy default values for inputs
@@ -236,7 +237,7 @@
return raw
-def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name
+def convert_requirement_interface_definitions_from_type_to_raw_template(context, raw_requirement, # pylint: disable=invalid-name
interface_definitions):
if not interface_definitions:
return
@@ -257,62 +258,71 @@
assign_raw_inputs(context, interface_assignment._raw, our_interface_assignment.inputs,
interface_definition._get_inputs(context), interface_name, None, presentation)
- # Assign operation implementations and inputs
our_operation_templates = our_interface_assignment.operations # OperationAssignment
+ if our_operation_templates is None:
+ our_operation_templates = {}
+
# OperationDefinition or OperationAssignment:
operation_definitions = interface_definition._get_operations(context) \
if hasattr(interface_definition, '_get_operations') else interface_definition.operations
- if our_operation_templates:
- # OperationAssignment:
- for operation_name, our_operation_template in our_operation_templates.iteritems():
- operation_definition = operation_definitions.get(operation_name) # OperationDefinition
+ if operation_definitions is None:
+ operation_definitions = {}
- our_input_assignments = our_operation_template.inputs
- our_implementation = our_operation_template.implementation
+ # OperationAssignment:
+ for operation_name, our_operation_template in our_operation_templates.iteritems():
+ operation_definition = operation_definitions.get(operation_name) # OperationDefinition
- if operation_definition is None:
- context.validation.report(
- 'interface definition "%s" refers to an unknown operation "%s" in "%s"'
- % (interface_name, operation_name, presentation._fullname),
- locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES)
+ our_input_assignments = our_operation_template.inputs
+ our_implementation = our_operation_template.implementation
- if (our_input_assignments is not None) or (our_implementation is not None):
- # Make sure we have the dict
- if (operation_name not in interface_assignment._raw) \
- or (interface_assignment._raw[operation_name] is None):
- interface_assignment._raw[operation_name] = OrderedDict()
+ if operation_definition is None:
+ context.validation.report(
+ u'interface definition "{0}" refers to an unknown operation "{1}" in "{2}"'
+ .format(interface_name, operation_name, presentation._fullname),
+ locator=our_operation_template._locator, level=Issue.BETWEEN_TYPES)
- if our_implementation is not None:
- interface_assignment._raw[operation_name]['implementation'] = \
- deepcopy_with_locators(our_implementation._raw)
+ # Make sure we have the dict
+ if (operation_name not in interface_assignment._raw) \
+ or (interface_assignment._raw[operation_name] is None):
+ interface_assignment._raw[operation_name] = OrderedDict()
- # Assign/merge operation inputs
- input_definitions = operation_definition.inputs \
- if operation_definition is not None else None
- assign_raw_inputs(context, interface_assignment._raw[operation_name],
- our_input_assignments, input_definitions, interface_name,
- operation_name, presentation)
+ if our_implementation is not None:
+ interface_assignment._raw[operation_name]['implementation'] = \
+ deepcopy_with_locators(our_implementation._raw)
+
+ # Assign/merge operation inputs
+ input_definitions = operation_definition.inputs \
+ if operation_definition is not None else None
+ assign_raw_inputs(context, interface_assignment._raw[operation_name],
+ our_input_assignments, input_definitions, interface_name,
+ operation_name, presentation)
def merge_raw_input_definition(context, the_raw_input, our_input, interface_name, operation_name,
presentation, type_name):
# Check if we changed the type
- # TODO: allow a sub-type?
- input_type1 = the_raw_input.get('type')
- input_type2 = our_input.type
- if input_type1 != input_type2:
+ input_type1_name = the_raw_input.get('type')
+ input_type1 = get_type_by_name(context, input_type1_name, 'data_types')
+ if input_type1 is None:
+ input_type1 = get_primitive_data_type(input_type1_name)
+ input_type2 = our_input._get_type(context)
+ if input_type1 is not input_type2 and \
+ (not hasattr(input_type1, '_is_descendant') or \
+ not input_type1._is_descendant(context, input_type2)):
if operation_name is not None:
context.validation.report(
- 'interface %s "%s" changes operation input "%s.%s" type from "%s" to "%s" in "%s"'
- % (type_name, interface_name, operation_name, our_input._name, input_type1,
- input_type2, presentation._fullname),
- locator=input_type2._locator, level=Issue.BETWEEN_TYPES)
+ u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of '
+ u'operation {3} "{4}.{5}" in {6}'
+ .format(our_input.type, input_type1_name, our_input._name, type_name,
+ interface_name, operation_name, presentation._fullname),
+ locator=our_input._locator, level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'interface %s "%s" changes input "%s" type from "%s" to "%s" in "%s"'
- % (type_name, interface_name, our_input._name, input_type1, input_type2,
- presentation._fullname),
- locator=input_type2._locator, level=Issue.BETWEEN_TYPES)
+ u'type "{0}" is not a descendant of overridden type "{1}" for input "{2}" of '
+ u'interface {3} "{4}" in {5}'
+ .format(our_input.type, input_type1_name, our_input._name, type_name,
+ interface_name, presentation._fullname),
+ locator=our_input._locator, level=Issue.BETWEEN_TYPES)
# Merge
merge(the_raw_input, our_input._raw)
@@ -405,8 +415,8 @@
if (type2 is not None) and not type1._is_descendant(context, type2):
context.validation.report(
- 'interface definition type "{0}" is not a descendant of overridden '
- 'interface definition type "{1}"' \
+ u'interface definition type "{0}" is not a descendant of overridden '
+ u'interface definition type "{1}"' \
.format(type1._name, type2._name),
locator=our_source._locator, level=Issue.BETWEEN_TYPES)
@@ -448,37 +458,42 @@
merge_interface_definition(context, interface, the_type, presentation, 'type')
-def assign_raw_inputs(context, values, assignments, definitions, interface_name, operation_name,
+def assign_raw_inputs(context, raw, assignments, definitions, interface_name, operation_name,
presentation):
- if not assignments:
- return
+ if assignments is None:
+ assignments = {}
+ if definitions is None:
+ definitions = {}
# Make sure we have the dict
- if ('inputs' not in values) or (values['inputs'] is None):
- values['inputs'] = OrderedDict()
+ if ('inputs' not in raw) or (raw['inputs'] is None):
+ raw['inputs'] = OrderedDict()
+
+ # Defaults
+ for input_name, definition in definitions.iteritems():
+ if ('default' in definition._raw) and (input_name not in raw['inputs']):
+ raw['inputs'][input_name] = coerce_parameter_value(context, definition, definition,
+ definition.default, 'default')
# Assign inputs
for input_name, assignment in assignments.iteritems():
- if (definitions is not None) and (input_name not in definitions):
+ if (not context.presentation.configuration.get('tosca.adhoc_inputs', True)) and \
+ (input_name not in definitions):
if operation_name is not None:
context.validation.report(
- 'interface definition "%s" assigns a value to an unknown operation input'
- ' "%s.%s" in "%s"'
- % (interface_name, operation_name, input_name, presentation._fullname),
+ u'interface definition "{0}" assigns a value to an unknown operation input'
+ u' "{1}.{2}" in "{3}"'
+ .format(interface_name, operation_name, input_name, presentation._fullname),
locator=assignment._locator, level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'interface definition "%s" assigns a value to an unknown input "%s" in "%s"'
- % (interface_name, input_name, presentation._fullname),
+ u'interface definition "{0}" assigns a value to an unknown input "{1}" in "{2}"'
+ .format(interface_name, input_name, presentation._fullname),
locator=assignment._locator, level=Issue.BETWEEN_TYPES)
- definition = definitions.get(input_name) if definitions is not None else None
-
- # Note: default value has already been assigned
-
- # Coerce value
- values['inputs'][input_name] = coerce_parameter_value(context, assignment, definition,
- assignment.value)
+ definition = definitions.get(input_name) # Could be None!
+ raw['inputs'][input_name] = coerce_parameter_value(context, assignment, definition,
+ assignment.value)
def validate_required_inputs(context, presentation, assignment, definition, original_assignment,
@@ -487,7 +502,11 @@
# (as opposed to topology template and workflow inputs) is done only in the parsing stage.
# This reasoning follows the TOSCA spirit, where anything that is declared as required in the
# type, must be assigned in the corresponding template.
- input_definitions = definition.inputs
+
+ # Note: InterfaceDefinition need _get_inputs, but OperationDefinition doesn't
+ input_definitions = definition._get_inputs(context) \
+ if hasattr(definition, '_get_inputs') \
+ else definition.inputs
if input_definitions:
for input_name, input_definition in input_definitions.iteritems():
if input_definition.required:
@@ -498,16 +517,17 @@
if value is None:
if operation_name is not None:
context.validation.report(
- 'interface definition "%s" does not assign a value to a required'
- ' operation input "%s.%s" in "%s"'
- % (interface_name, operation_name, input_name, presentation._fullname),
+ u'interface definition "{0}" does not assign a value to a required'
+ u' operation input "{1}.{2}" in "{3}"'
+ .format(interface_name, operation_name, input_name,
+ presentation._fullname),
locator=get_locator(original_assignment, presentation._locator),
level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'interface definition "%s" does not assign a value to a required input'
- ' "%s" in "%s"'
- % (interface_name, input_name, presentation._fullname),
+ u'interface definition "{0}" does not assign a value to a required'
+ u' input "{1}" in "{2}"'
+ .format(interface_name, input_name, presentation._fullname),
locator=get_locator(original_assignment, presentation._locator),
level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
index 9bafeec..e411104 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/parameters.py
@@ -88,7 +88,7 @@
definition = definitions[name]
values[name] = coerce_parameter_value(context, value, definition, value.value)
else:
- context.validation.report('assignment to undefined {0} "{1}" in "{2}"'
+ context.validation.report(u'assignment to undefined {0} "{1}" in "{2}"'
.format(field_name, name, presentation._fullname),
locator=value._locator, level=Issue.BETWEEN_TYPES)
@@ -99,7 +99,7 @@
if (name not in values) and \
(('default' in definition._raw) or (field_name == 'attribute')):
values[name] = coerce_parameter_value(context, presentation, definition,
- definition.default)
+ definition.default, 'default')
validate_required_values(context, presentation, values, definitions)
@@ -131,7 +131,8 @@
parameter.value)
else:
default = parameter.default if hasattr(parameter, 'default') else None
- values[name] = coerce_parameter_value(context, presentation, parameter, default)
+ values[name] = coerce_parameter_value(context, presentation, parameter, default,
+ 'default')
return values
@@ -147,11 +148,21 @@
if not definitions:
return
+
+ def has_value(name):
+ if values is None:
+ return False
+ value = values.get(name)
+ if value is None:
+ return False
+ if isinstance(value, Value) and (value.value is None):
+ return False
+ return True
+
for name, definition in definitions.iteritems():
- if getattr(definition, 'required', False) and \
- ((values is None) or (values.get(name) is None)):
- context.validation.report('required property "%s" is not assigned a value in "%s"'
- % (name, presentation._fullname),
+ if getattr(definition, 'required', False) and not has_value(name):
+ context.validation.report(u'required property "{0}" is not assigned a value in "{1}"'
+ .format(name, presentation._fullname),
locator=presentation._get_child_locator('properties'),
level=Issue.BETWEEN_TYPES)
@@ -166,14 +177,14 @@
our_property_definition._reset_method_cache()
type2 = our_property_definition._get_type(context)
- if type1 != type2:
- if not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2):
- context.validation.report(
- 'property definition type "{0}" is not a descendant of overridden '
- 'property definition type "{1}"' \
- .format(type1_name, type2._name),
- locator=presentation._get_child_locator(field_name, property_name),
- level=Issue.BETWEEN_TYPES)
+ if (type1 is not type2) and \
+ (not hasattr(type1, '_is_descendant') or not type1._is_descendant(context, type2)):
+ context.validation.report(
+ u'property definition type "{0}" is not a descendant of overridden '
+ u'property definition type "{1}"' \
+ .format(our_property_definition.type, type1_name),
+ locator=presentation._get_child_locator(field_name, property_name),
+ level=Issue.BETWEEN_TYPES)
merge(raw_property_definition, our_property_definition._raw)
@@ -225,6 +236,6 @@
def convert_parameter_definitions_to_values(context, definitions):
values = OrderedDict()
for name, definition in definitions.iteritems():
- default = definition.default
- values[name] = coerce_parameter_value(context, definition, definition, default)
+ values[name] = coerce_parameter_value(context, definition, definition, definition.default,
+ 'default')
return values
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
index 6bdb5b1..79958a1 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/requirements.py
@@ -82,7 +82,6 @@
for requirement_name, requirement_definition in requirement_definitions:
# Allowed occurrences
allowed_occurrences = requirement_definition.occurrences
- allowed_occurrences = allowed_occurrences if allowed_occurrences is not None else None
# Count actual occurrences
actual_occurrences = 0
@@ -103,28 +102,30 @@
None, presentation)
validate_requirement_assignment(context, presentation, requirement_assignment,
relationship_property_definitions,
- relationship_interface_definitions)
+ relationship_interface_definitions,
+ requirement_definition)
requirement_assignments.append((requirement_name, requirement_assignment))
elif actual_occurrences > 1:
context.validation.report(
- 'requirement "%s" is allowed only one occurrence in "%s": %d'
- % (requirement_name, presentation._fullname, actual_occurrences),
+ u'requirement "{0}" is allowed only one occurrence in "{1}": {2:d}'
+ .format(requirement_name, presentation._fullname, actual_occurrences),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
else:
if not allowed_occurrences.is_in(actual_occurrences):
if allowed_occurrences.value[1] == 'UNBOUNDED':
context.validation.report(
- 'requirement "%s" does not have at least %d occurrences in "%s": has %d'
- % (requirement_name, allowed_occurrences.value[0],
- presentation._fullname, actual_occurrences),
+ u'requirement "{0}" does not have at least {1:d} occurrences in "{3}":'
+ u' has {4:d}'
+ .format(requirement_name, allowed_occurrences.value[0],
+ presentation._fullname, actual_occurrences),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'requirement "%s" is allowed between %d and %d occurrences in "%s":'
- ' has %d'
- % (requirement_name, allowed_occurrences.value[0],
- allowed_occurrences.value[1], presentation._fullname,
- actual_occurrences),
+ u'requirement "{0}" is allowed between {1:d} and {2:d} occurrences in'
+ u' "{3}": has {4:d}'
+ .format(requirement_name, allowed_occurrences.value[0],
+ allowed_occurrences.value[1], presentation._fullname,
+ actual_occurrences),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return requirement_assignments
@@ -134,7 +135,7 @@
# Utils
#
-def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches
+def convert_requirement_from_definition_to_assignment(context, requirement_definition, # pylint: disable=too-many-branches
our_requirement_assignment, container):
from ..assignments import RequirementAssignment
@@ -174,9 +175,9 @@
# Make sure the type is derived
if not definition_relationship_type._is_descendant(context, relationship_type):
context.validation.report(
- 'assigned relationship type "%s" is not a descendant of declared relationship type'
- ' "%s"' \
- % (relationship_type._name, definition_relationship_type._name),
+ u'assigned relationship type "{0}" is not a descendant of declared relationship '
+ u' type "{1}"'
+ .format(relationship_type._name, definition_relationship_type._name),
locator=container._locator, level=Issue.BETWEEN_TYPES)
if relationship_type is not None:
@@ -253,12 +254,13 @@
or our_requirement_assignment,
requirement_assignment,
relationship_property_definitions,
- relationship_interface_definitions)
+ relationship_interface_definitions,
+ requirement_definition)
requirement_assignments.append((requirement_name, requirement_assignment))
else:
- context.validation.report('requirement "%s" not declared at node type "%s" in "%s"'
- % (requirement_name, presentation.type,
- presentation._fullname),
+ context.validation.report(u'requirement "{0}" not declared at node type "{1}" in "{2}"'
+ .format(requirement_name, presentation.type,
+ presentation._fullname),
locator=our_requirement_assignment._locator,
level=Issue.BETWEEN_TYPES)
@@ -278,7 +280,7 @@
requirement._raw['node_filter'] = deepcopy_with_locators(our_node_filter._raw)
our_relationship = our_requirement.relationship # RelationshipAssignment
- if (our_relationship is not None) and (our_relationship.type is None):
+ if our_relationship is not None:
# Make sure we have a dict
if 'relationship' not in requirement._raw:
requirement._raw['relationship'] = OrderedDict()
@@ -291,7 +293,8 @@
def merge_requirement_assignment_relationship(context, presentation, property_definitions,
interface_definitions, requirement, our_relationship):
- our_relationship_properties = our_relationship._raw.get('properties')
+ our_relationship_properties = our_relationship._raw.get('properties') \
+ if isinstance(our_relationship._raw, dict) else None
if our_relationship_properties:
# Make sure we have a dict
if 'properties' not in requirement._raw['relationship']:
@@ -305,10 +308,10 @@
coerce_parameter_value(context, presentation, definition, prop)
else:
context.validation.report(
- 'relationship property "%s" not declared at definition of requirement "%s"'
- ' in "%s"'
- % (property_name, requirement._fullname,
- presentation._container._container._fullname),
+ u'relationship property "{0}" not declared at definition of requirement "{1}"'
+ u' in "{2}"'
+ .format(property_name, requirement._fullname,
+ presentation._container._container._fullname),
locator=our_relationship._get_child_locator('properties', property_name),
level=Issue.BETWEEN_TYPES)
@@ -330,28 +333,58 @@
interface_definition, interface_name)
else:
context.validation.report(
- 'relationship interface "%s" not declared at definition of requirement "%s"'
- ' in "%s"'
- % (interface_name, requirement._fullname,
- presentation._container._container._fullname),
+ u'relationship interface "{0}" not declared at definition of requirement "{1}"'
+ u' in "{2}"'
+ .format(interface_name, requirement._fullname,
+ presentation._container._container._fullname),
locator=our_relationship._locator, level=Issue.BETWEEN_TYPES)
def validate_requirement_assignment(context, presentation, requirement_assignment,
relationship_property_definitions,
- relationship_interface_definitions):
- relationship = requirement_assignment.relationship
- if relationship is None:
- return
+ relationship_interface_definitions, requirement_definition):
+ # Validate node
+ definition_node_type = requirement_definition._get_node_type(context)
+ assignment_node_type, node_variant = requirement_assignment._get_node(context)
+ if node_variant == 'node_template':
+ assignment_node_type = assignment_node_type._get_type(context)
+ if (assignment_node_type is not None) and (definition_node_type is not None) and \
+ (assignment_node_type is not definition_node_type) and \
+ (not definition_node_type._is_descendant(context, assignment_node_type)):
+ context.validation.report(
+ u'requirement assignment node "{0}" is not derived from node type "{1}" of requirement '
+ u'definition in {2}'
+ .format(requirement_assignment.node, requirement_definition.node,
+ presentation._container._fullname),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
- validate_required_values(context, presentation, relationship.properties,
- relationship_property_definitions)
+ # Validate capability
+ definition_capability_type = requirement_definition._get_capability_type(context)
+ assignment_capability_type, capability_variant = requirement_assignment._get_capability(context)
+ if capability_variant == 'capability_assignment':
+ assignment_capability_type = assignment_capability_type._get_type(context)
+ if (assignment_capability_type is not None) and (definition_capability_type is not None) and \
+ (assignment_capability_type is not definition_capability_type) and \
+ (not definition_capability_type._is_descendant(context, assignment_capability_type)):
+ context.validation.report(
+ u'requirement assignment capability "{0}" is not derived from capability type "{1}" of '
+ u'requirement definition in {2}'
+ .format(requirement_assignment.capability, requirement_definition.capability,
+ presentation._container._fullname),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+ relationship = requirement_assignment.relationship
+
+ values = OrderedDict((name, prop.value)
+ for name, prop in relationship.properties.iteritems()) \
+ if (relationship and relationship.properties) else {}
+ validate_required_values(context, presentation, values, relationship_property_definitions)
if relationship_interface_definitions:
for interface_name, relationship_interface_definition \
in relationship_interface_definitions.iteritems():
interface_assignment = relationship.interfaces.get(interface_name) \
- if relationship.interfaces is not None else None
+ if (relationship and relationship.interfaces) else None
validate_required_inputs(context, presentation, interface_assignment,
relationship_interface_definition, None, interface_name)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
index e2af4b8..ae2f924 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/modeling/substitution_mappings.py
@@ -18,8 +18,7 @@
def validate_substitution_mappings_requirement(context, presentation):
-
- # validate that the requirement in substitution_mapping is defined in the substitution node type
+ # Validate that the requirement in substitution_mapping is defined in the substitution node type
substitution_node_type = presentation._container._get_type(context)
if substitution_node_type is None:
return
@@ -29,7 +28,7 @@
break
else:
context.validation.report(
- 'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format(
+ u'substitution mapping requirement "{0}" is not declared in node type "{1}"'.format(
presentation._name, substitution_node_type._name),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return
@@ -38,7 +37,7 @@
_report_invalid_mapping_format(context, presentation, field='requirement')
return
- # validate that the mapped requirement is defined in the corresponding node template
+ # Validate that the mapped requirement is defined in the corresponding node template
node_template = _get_node_template(context, presentation)
if node_template is None:
_report_missing_node_template(context, presentation, field='requirement')
@@ -50,24 +49,24 @@
break
else:
context.validation.report(
- 'substitution mapping requirement "{0}" refers to an unknown requirement of node '
- 'template "{1}": {mapped_requirement_name}'.format(
+ u'substitution mapping requirement "{0}" refers to an unknown requirement of node '
+ u'template "{1}": {mapped_requirement_name}'.format(
presentation._name, node_template._name,
mapped_requirement_name=safe_repr(mapped_requirement_name)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return
- # validate that the requirement's capability type in substitution_mapping is derived from the
+ # Validate that the requirement's capability type in substitution_mapping is derived from the
# requirement's capability type in the corresponding node template
substitution_type_requirement_capability_type = \
substitution_type_requirement._get_capability_type(context)
node_template_requirement_capability_type = \
node_template_requirement._get_capability(context)[0]
- if not node_template_requirement_capability_type._is_descendant(
- context, substitution_type_requirement_capability_type):
+ if not substitution_type_requirement_capability_type._is_descendant(
+ context, node_template_requirement_capability_type):
context.validation.report(
- 'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant '
- 'of the mapped node template capability type "{2}"'.format(
+ u'substitution mapping requirement "{0}" of capability type "{1}" is not a descendant '
+ u'of the mapped node template capability type "{2}"'.format(
presentation._name,
substitution_type_requirement_capability_type._name,
node_template_requirement_capability_type._name),
@@ -75,8 +74,7 @@
def validate_substitution_mappings_capability(context, presentation):
-
- # validate that the capability in substitution_mapping is defined in the substitution node type
+ # Validate that the capability in substitution_mapping is defined in the substitution node type
substitution_node_type = presentation._container._get_type(context)
if substitution_node_type is None:
return
@@ -84,8 +82,8 @@
substitution_type_capability = substitution_type_capabilities.get(presentation._name)
if substitution_type_capability is None:
context.validation.report(
- 'substitution mapping capability "{0}" '
- 'is not declared in node type "{substitution_type}"'.format(
+ u'substitution mapping capability "{0}" '
+ u'is not declared in node type "{substitution_type}"'.format(
presentation._name, substitution_type=substitution_node_type._name),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return
@@ -94,7 +92,7 @@
_report_invalid_mapping_format(context, presentation, field='capability')
return
- # validate that the capability in substitution_mapping is declared in the corresponding
+ # Validate that the capability in substitution_mapping is declared in the corresponding
# node template
node_template = _get_node_template(context, presentation)
if node_template is None:
@@ -105,22 +103,21 @@
if node_template_capability is None:
context.validation.report(
- 'substitution mapping capability "{0}" refers to an unknown '
- 'capability of node template "{1}": {mapped_capability_name}'.format(
+ u'substitution mapping capability "{0}" refers to an unknown '
+ u'capability of node template "{1}": {mapped_capability_name}'.format(
presentation._name, node_template._name,
mapped_capability_name=safe_repr(mapped_capability_name)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return
- # validate that the capability type in substitution_mapping is derived from the capability type
+ # Validate that the capability type in substitution_mapping is derived from the capability type
# in the corresponding node template
substitution_type_capability_type = substitution_type_capability._get_type(context)
node_template_capability_type = node_template_capability._get_type(context)
-
if not substitution_type_capability_type._is_descendant(context, node_template_capability_type):
context.validation.report(
- 'node template capability type "{0}" is not a descendant of substitution mapping '
- 'capability "{1}" of type "{2}"'.format(
+ u'node template capability type "{0}" is not a descendant of substitution mapping '
+ u'capability "{1}" of type "{2}"'.format(
node_template_capability_type._name,
presentation._name,
substitution_type_capability_type._name),
@@ -132,7 +129,9 @@
#
def _validate_mapping_format(presentation):
- """Validate that the mapping is a list of 2 strings"""
+ """
+ Validate that the mapping is a list of 2 strings.
+ """
if not isinstance(presentation._raw, list) or \
len(presentation._raw) != 2 or \
not isinstance(presentation._raw[0], basestring) or \
@@ -150,8 +149,8 @@
def _report_missing_node_template(context, presentation, field):
context.validation.report(
- 'substitution mappings {field} "{node_template_mapping}" '
- 'refers to an unknown node template: {node_template_name}'.format(
+ u'substitution mappings {field} "{node_template_mapping}" '
+ u'refers to an unknown node template: {node_template_name}'.format(
field=field,
node_template_mapping=presentation._name,
node_template_name=safe_repr(presentation._raw[0])),
@@ -160,7 +159,7 @@
def _report_invalid_mapping_format(context, presentation, field):
context.validation.report(
- 'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format(
+ u'substitution mapping {field} "{field_name}" is not a list of 2 strings: {value}'.format(
field=field,
field_name=presentation._name,
value=safe_repr(presentation._raw)),
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
index 0e3c94d..9fa056d 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/extensible.py
@@ -30,4 +30,4 @@
@cachedmethod
def _get_extension(self, name, default=None):
extensions = self._extensions
- return extensions.get(name, default) if extensions is not None else None # pylint: disable=no-member
+ return extensions.get(name, default) if extensions is not None else None # pylint: disable=no-member
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py
index f14164a..9cd58e2 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_getters.py
@@ -14,10 +14,12 @@
# limitations under the License.
from aria.utils.formatting import safe_repr
+from aria.utils.type import full_type_name
from aria.parser.exceptions import InvalidValueError
+from aria.parser.presentation import NULL
-def data_type_class_getter(cls):
+def data_type_class_getter(cls, allow_null=False):
"""
Wraps the field value in a specialized data type class.
@@ -26,12 +28,14 @@
def getter(field, presentation, context=None):
raw = field.default_get(presentation, context)
- if raw is not None:
- try:
- return cls(None, None, raw, None)
- except ValueError as e:
- raise InvalidValueError(
- '%s is not a valid "%s" in "%s": %s'
- % (field.full_name, field.full_cls_name, presentation._name, safe_repr(raw)),
- cause=e, locator=field.get_locator(raw))
+ if (raw is None) or (allow_null and (raw is NULL)):
+ return raw
+ try:
+ return cls(None, None, raw, None)
+ except ValueError as e:
+ raise InvalidValueError(
+ u'{0} is not a valid "{1}" in "{2}": {3}'
+ .format(field.full_name, full_type_name(cls), presentation._name,
+ safe_repr(raw)),
+ cause=e, locator=field.get_locator(raw))
return getter
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
index e5853d8..2ff5143 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/field_validators.py
@@ -52,8 +52,8 @@
else:
if copy.copy is not None:
context.validation.report(
- '"copy" field refers to a %s that itself is a copy in "%s": %s'
- % (template_type_name, presentation._fullname, safe_repr(value)),
+ u'"copy" field refers to a {0} that itself is a copy in "{1}": {2}'
+ .format(template_type_name, presentation._fullname, safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
return validator_fn
@@ -84,8 +84,8 @@
container_data_type = get_container_data_type(presentation)
if (container_data_type is not None) and (container_data_type._name == value):
context.validation.report(
- 'type of property "%s" creates a circular value hierarchy: %s'
- % (presentation._fullname, safe_repr(value)),
+ u'type of property "{0}" creates a circular value hierarchy: {1}'
+ .format(presentation._fullname, safe_repr(value)),
locator=presentation._get_child_locator('type'), level=Issue.BETWEEN_TYPES)
# Can be a complex data type
@@ -135,14 +135,14 @@
if use_entry_schema:
if value is None:
context.validation.report(
- '"entry_schema" does not have a value as required by data type "%s" in "%s"'
- % (get_data_type_name(the_type), presentation._container._fullname),
+ u'"entry_schema" does not have a value as required by data type "{0}" in "{1}"'
+ .format(get_data_type_name(the_type), presentation._container._fullname),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
else:
if value is not None:
context.validation.report(
- '"entry_schema" has a value but it is not used by data type "%s" in "%s"'
- % (get_data_type_name(the_type), presentation._container._fullname),
+ u'"entry_schema" has a value but it is not used by data type "{0}" in "{1}"'
+ .format(get_data_type_name(the_type), presentation._container._fullname),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
@@ -201,8 +201,8 @@
if value is not None:
if presentation._get_primitive_ancestor(context) is None:
context.validation.report(
- 'data type "%s" defines constraints but does not have a primitive ancestor'
- % presentation._fullname,
+ u'data type "{0}" defines constraints but does not have a primitive ancestor'
+ .format(presentation._fullname),
locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
@@ -220,8 +220,8 @@
if values is not None:
if presentation._get_primitive_ancestor(context) is not None:
context.validation.report(
- 'data type "%s" defines properties even though it has a primitive ancestor'
- % presentation._fullname,
+ u'data type "{0}" defines properties even though it has a primitive ancestor'
+ .format(presentation._fullname),
locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
@@ -272,17 +272,18 @@
# Upper bound be coercible
upper = coerce_value(context, presentation, the_type, None, None, upper, field.name)
- # Second "in_range" value must be greater than first
+ # Second "in_range" value must be greater or equal than first
if (lower is not None) and (upper is not None) and (lower >= upper):
context.validation.report(
- 'upper bound of "in_range" constraint is not greater than the lower bound'
- ' in "%s": %s <= %s'
- % (presentation._container._fullname, safe_repr(lower), safe_repr(upper)),
+ u'upper bound of "in_range" constraint is not greater than the lower bound'
+ u' in "{0}": {1} <= {2}'
+ .format(presentation._container._fullname, safe_repr(lower),
+ safe_repr(upper)),
locator=presentation._locator, level=Issue.FIELD)
else:
context.validation.report(
- 'constraint "%s" is not a list of exactly 2 elements in "%s"'
- % (field.name, presentation._fullname),
+ u'constraint "{0}" is not a list of exactly 2 elements in "{1}": {2}'
+ .format(field.name, presentation._fullname, safe_repr(values)),
locator=presentation._get_child_locator(field.name), level=Issue.FIELD)
@@ -325,8 +326,8 @@
re.compile(value)
except re.error as e:
context.validation.report(
- 'constraint "%s" is not a valid regular expression in "%s"'
- % (field.name, presentation._fullname),
+ u'constraint "{0}" is not a valid regular expression in "{1}": {2}'
+ .format(field.name, presentation._fullname, safe_repr(value)),
locator=presentation._get_child_locator(field.name), level=Issue.FIELD, exception=e)
@@ -379,21 +380,21 @@
if get_type_by_name(context, value, 'capability_types') is not None:
if node is not None:
context.validation.report(
- '"%s" refers to a capability type even though "node" has a value in "%s"'
- % (presentation._name, presentation._container._fullname),
+ u'"{0}" refers to a capability type even though "node" has a value in "{1}"'
+ .format(presentation._name, presentation._container._fullname),
locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_FIELDS)
return
if node_variant == 'node_template':
context.validation.report(
- 'requirement "%s" refers to an unknown capability definition name or capability'
- ' type in "%s": %s'
- % (presentation._name, presentation._container._fullname, safe_repr(value)),
+ u'requirement "{0}" refers to an unknown capability definition name or capability'
+ u' type in "{1}": {2}'
+ .format(presentation._name, presentation._container._fullname, safe_repr(value)),
locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'requirement "%s" refers to an unknown capability type in "%s": %s'
- % (presentation._name, presentation._container._fullname, safe_repr(value)),
+ u'requirement "{0}" refers to an unknown capability type in "{1}": {2}'
+ .format(presentation._name, presentation._container._fullname, safe_repr(value)),
locator=presentation._get_child_locator(field.name), level=Issue.BETWEEN_TYPES)
@@ -412,9 +413,9 @@
_, node_type_variant = presentation._get_node(context)
if node_type_variant != 'node_type':
context.validation.report(
- 'requirement "%s" has a node filter even though "node" does not refer to a node'
- ' type in "%s"'
- % (presentation._fullname, presentation._container._fullname),
+ u'requirement "{0}" has a node filter even though "node" does not refer to a node'
+ u' type in "{1}"'
+ .format(presentation._fullname, presentation._container._fullname),
locator=presentation._locator, level=Issue.BETWEEN_FIELDS)
@@ -468,6 +469,54 @@
#
+# GroupTemplate
+#
+
+def group_members_validator(field, presentation, context):
+ """
+ Makes sure that the field's elements refer to node templates and that they match the node types
+ declared in the group type.
+
+ Used with the :func:`field_validator` decorator for the ``targets`` field in
+ :class:`GroupTemplate`.
+ """
+
+ field.default_validate(presentation, context)
+
+ values = getattr(presentation, field.name)
+ if values is not None:
+ node_templates = \
+ context.presentation.get('service_template', 'topology_template', 'node_templates') \
+ or {}
+ for value in values:
+ if value not in node_templates:
+ report_issue_for_unknown_type(context, presentation, 'node template', field.name,
+ value)
+
+ group_type = presentation._get_type(context)
+ if group_type is None:
+ break
+
+ node_types = group_type._get_members(context)
+
+ is_valid = False
+
+ if value in node_templates:
+ our_node_type = node_templates[value]._get_type(context)
+ for node_type in node_types:
+ if node_type._is_descendant(context, our_node_type):
+ is_valid = True
+ break
+
+ if not is_valid:
+ context.validation.report(
+ u'group definition target does not match a node type'
+ u' declared in the group type in "{0}": {1}'
+ .format(presentation._name, safe_repr(value)),
+ locator=presentation._locator, level=Issue.BETWEEN_TYPES)
+
+
+#
# PolicyTemplate
#
@@ -484,13 +533,12 @@
values = getattr(presentation, field.name)
if values is not None:
+ node_templates = \
+ context.presentation.get('service_template', 'topology_template', 'node_templates') \
+ or {}
+ groups = context.presentation.get('service_template', 'topology_template', 'groups') \
+ or {}
for value in values:
- node_templates = \
- context.presentation.get('service_template', 'topology_template',
- 'node_templates') \
- or {}
- groups = context.presentation.get('service_template', 'topology_template', 'groups') \
- or {}
if (value not in node_templates) and (value not in groups):
report_issue_for_unknown_type(context, presentation, 'node template or group',
field.name, value)
@@ -519,9 +567,9 @@
if not is_valid:
context.validation.report(
- 'policy definition target does not match either a node type or a group type'
- ' declared in the policy type in "%s": %s'
- % (presentation._name, safe_repr(value)),
+ u'policy definition target does not match either a node type or a group type'
+ u' declared in the policy type in "{0}": {1}'
+ .format(presentation._name, safe_repr(value)),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
@@ -547,8 +595,8 @@
for name, _ in values:
if name not in properties:
context.validation.report(
- 'node filter refers to an unknown property definition in "%s": %s'
- % (node_type._name, name),
+ u'node filter refers to an unknown property definition in "{0}": {1}'
+ .format(node_type._name, name),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
@@ -564,7 +612,7 @@
field.default_validate(presentation, context)
values = getattr(presentation, field.name)
- if values is not None: # pylint: disable=too-many-nested-blocks
+ if values is not None: # pylint: disable=too-many-nested-blocks
node_type = presentation._get_node_type(context)
if node_type is not None:
capabilities = node_type._get_capabilities(context)
@@ -577,12 +625,12 @@
for property_name, _ in properties:
if property_name not in capability_properties:
context.validation.report(
- 'node filter refers to an unknown capability definition'
- ' property in "%s": %s'
- % (node_type._name, property_name),
+ u'node filter refers to an unknown capability definition'
+ u' property in "{0}": {1}'
+ .format(node_type._name, property_name),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
else:
context.validation.report(
- 'node filter refers to an unknown capability definition in "%s": %s'
- % (node_type._name, name),
+ u'node filter refers to an unknown capability definition in "{0}": {1}'
+ .format(node_type._name, name),
locator=presentation._locator, level=Issue.BETWEEN_TYPES)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
index 5f9750e..f31b6c9 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presentation/types.py
@@ -14,7 +14,7 @@
# limitations under the License.
-def convert_name_to_full_type_name(context, name, types_dict): # pylint: disable=unused-argument
+def convert_name_to_full_type_name(context, name, types_dict): # pylint: disable=unused-argument
"""
Converts a type name to its full type name, or else returns it unchanged.
diff --git a/extensions/aria_extension_tosca/simple_v1_0/presenter.py b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
index 28c9f7b..e84decc 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/presenter.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/presenter.py
@@ -23,7 +23,7 @@
from .templates import ServiceTemplate
-class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstract-method
+class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstract-method
"""
ARIA presenter for the `TOSCA Simple Profile v1.0 cos01 <http://docs.oasis-open.org/tosca
/TOSCA-Simple-Profile-YAML/v1.0/cos01/TOSCA-Simple-Profile-YAML-v1.0-cos01.html>`__.
@@ -70,7 +70,7 @@
@cachedmethod
def _get_import_locations(self, context):
import_locations = []
- if context.presentation.import_profile:
+ if context.presentation.configuration.get('tosca.import_profile', True):
import_locations.append(self.SIMPLE_PROFILE_LOCATION)
imports = self._get('service_template', 'imports')
if imports:
@@ -78,5 +78,5 @@
return FrozenList(import_locations) if import_locations else EMPTY_READ_ONLY_LIST
@cachedmethod
- def _get_model(self, context): # pylint: disable=no-self-use
+ def _get_model(self, context): # pylint: disable=no-self-use
return create_service_template_model(context)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/templates.py b/extensions/aria_extension_tosca/simple_v1_0/templates.py
index 3c36bb8..d4d012e 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/templates.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/templates.py
@@ -19,11 +19,11 @@
from aria.parser.presentation import (has_fields, primitive_field, primitive_list_field,
object_field, object_list_field, object_dict_field,
object_sequenced_list_field, field_validator,
- type_validator, list_type_validator)
+ type_validator)
from .assignments import (PropertyAssignment, AttributeAssignment, RequirementAssignment,
CapabilityAssignment, InterfaceAssignment, ArtifactAssignment)
-from .definitions import ParameterDefinition
+from .definitions import (InputDefinition, OutputDefinition)
from .filters import NodeFilter
from .misc import (Description, MetaData, Repository, Import, SubstitutionMappings)
from .modeling.parameters import (get_assigned_and_defined_parameter_values, get_parameter_values)
@@ -34,7 +34,8 @@
from .modeling.policies import get_policy_targets
from .modeling.copy import get_default_raw_from_copy
from .presentation.extensible import ExtensiblePresentation
-from .presentation.field_validators import copy_validator, policy_targets_validator
+from .presentation.field_validators import (copy_validator, group_members_validator,
+ policy_targets_validator)
from .presentation.types import (convert_name_to_full_type_name, get_type_by_name)
from .types import (ArtifactType, DataType, CapabilityType, InterfaceType, RelationshipType,
NodeType, GroupType, PolicyType)
@@ -182,6 +183,7 @@
def _validate(self, context):
super(NodeTemplate, self)._validate(context)
self._get_property_values(context)
+ self._get_attribute_default_values(context)
self._get_requirements(context)
self._get_capabilities(context)
self._get_interfaces(context)
@@ -284,12 +286,17 @@
return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'property'))
@cachedmethod
+ def _get_attribute_default_values(self, context):
+ return FrozenDict(get_assigned_and_defined_parameter_values(context, self, 'attribute'))
+
+ @cachedmethod
def _get_interfaces(self, context):
return FrozenDict(get_template_interfaces(context, self, 'relationship template'))
def _validate(self, context):
super(RelationshipTemplate, self)._validate(context)
self._get_property_values(context)
+ self._get_attribute_default_values(context)
self._get_interfaces(context)
def _dump(self, context):
@@ -340,7 +347,7 @@
:type: {:obj:`basestring`: :class:`PropertyAssignment`}
"""
- @field_validator(list_type_validator('node template', 'topology_template', 'node_templates'))
+ @field_validator(group_members_validator)
@primitive_list_field(str)
def members(self):
"""
@@ -464,13 +471,13 @@
:type: :class:`Description`
"""
- @object_dict_field(ParameterDefinition)
+ @object_dict_field(InputDefinition)
def inputs(self):
"""
An optional list of input parameters (i.e., as parameter definitions) for the Topology
Template.
- :type: {:obj:`basestring`: :class:`ParameterDefinition`}
+ :type: {:obj:`basestring`: :class:`InputDefinition`}
"""
@object_dict_field(NodeTemplate)
@@ -506,13 +513,13 @@
:type: {:obj:`basestring`: :class:`PolicyTemplate`}
"""
- @object_dict_field(ParameterDefinition)
+ @object_dict_field(OutputDefinition)
def outputs(self):
"""
An optional list of output parameters (i.e., as parameter definitions) for the Topology
Template.
- :type: {:obj:`basestring`: :class:`ParameterDefinition`}
+ :type: {:obj:`basestring`: :class:`OutputDefinition`}
"""
@object_field(SubstitutionMappings)
diff --git a/extensions/aria_extension_tosca/simple_v1_0/types.py b/extensions/aria_extension_tosca/simple_v1_0/types.py
index 43af44b..8ab7b07 100644
--- a/extensions/aria_extension_tosca/simple_v1_0/types.py
+++ b/extensions/aria_extension_tosca/simple_v1_0/types.py
@@ -34,6 +34,7 @@
from .modeling.data_types import (get_data_type, get_inherited_constraints, coerce_data_type_value,
validate_data_type_name)
from .modeling.interfaces import (get_inherited_interface_definitions, get_inherited_operations)
+from .modeling.groups import get_inherited_members
from .modeling.policies import get_inherited_targets
from .modeling.parameters import get_inherited_parameter_definitions
from .modeling.requirements import get_inherited_requirement_definitions
@@ -70,7 +71,7 @@
"""
@field_getter(data_type_class_getter(Version))
- @primitive_field()
+ @primitive_field(str)
def version(self):
"""
An optional version for the Artifact Type definition.
@@ -153,7 +154,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Data Type definition.
@@ -210,7 +212,7 @@
if not isinstance(parent, DataType):
return parent
else:
- return parent._get_primitive_ancestor(context) # pylint: disable=no-member
+ return parent._get_primitive_ancestor(context) # pylint: disable=no-member
return None
@cachedmethod
@@ -261,7 +263,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Capability Type definition.
@@ -312,7 +315,9 @@
@cachedmethod
def _is_descendant(self, context, other_type):
- """returns True iff `other_type` is a descendant of the represented capability type"""
+ """
+ Checks if ``other_type`` is our descendant (or equal to us).
+ """
if other_type is None:
return False
elif other_type._name == self._name:
@@ -324,12 +329,17 @@
return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties'))
@cachedmethod
+ def _get_attributes(self, context):
+ return FrozenDict(get_inherited_parameter_definitions(context, self, 'attributes'))
+
+ @cachedmethod
def _get_valid_source_types(self, context):
return get_inherited_valid_source_types(context, self)
def _validate(self, context):
super(CapabilityType, self)._validate(context)
self._get_properties(context)
+ self._get_attributes(context)
def _dump(self, context):
self._dump_content(context, (
@@ -363,7 +373,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Interface Type definition.
@@ -417,8 +428,7 @@
def _validate(self, context):
super(InterfaceType, self)._validate(context)
self._get_inputs(context)
- for operation in self.operations.itervalues(): # pylint: disable=no-member
- operation._validate(context)
+ self._get_operations(context)
def _dump(self, context):
self._dump_content(context, (
@@ -450,7 +460,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Relationship Type definition.
@@ -508,6 +519,9 @@
@cachedmethod
def _is_descendant(self, context, the_type):
+ """
+ Checks if ``other_type`` is our descendant (or equal to us).
+ """
if the_type is None:
return False
elif the_type._name == self._name:
@@ -565,7 +579,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Node Type definition.
@@ -639,6 +654,9 @@
@cachedmethod
def _is_descendant(self, context, the_type):
+ """
+ Checks if ``other_type`` is our descendant (or equal to us).
+ """
if the_type is None:
return False
elif the_type._name == self._name:
@@ -721,7 +739,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Group Type definition.
@@ -775,6 +794,9 @@
@cachedmethod
def _is_descendant(self, context, the_type):
+ """
+ Checks if ``other_type`` is our descendant (or equal to us).
+ """
if the_type is None:
return False
elif the_type._name == self._name:
@@ -786,6 +808,11 @@
return FrozenDict(get_inherited_parameter_definitions(context, self, 'properties'))
@cachedmethod
+ def _get_members(self, context):
+ node_types = get_inherited_members(context, self)
+ return FrozenList(node_types)
+
+ @cachedmethod
def _get_interfaces(self, context):
return FrozenDict(get_inherited_interface_definitions(context, self, 'group type'))
@@ -827,7 +854,8 @@
:type: :obj:`basestring`
"""
- @object_field(Version)
+ @field_getter(data_type_class_getter(Version))
+ @primitive_field(str)
def version(self):
"""
An optional version for the Policy Type definition.
diff --git a/requirements.in b/requirements.in
index c5bfc78..e8bc336 100644
--- a/requirements.in
+++ b/requirements.in
@@ -17,7 +17,7 @@
blinker>=1.4, <1.5
bottle>=0.12, <0.13
CacheControl[filecache]>=0.11.0, <0.13
-click>=6, < 7
+click>=6, <7
click_didyoumean>=0.0.3, <0.1
colorama>=0.3, <=0.4
Jinja2>=2.9, <3.0
@@ -29,7 +29,6 @@
requests>=2.3, <2.14
retrying>=1.3, <1.4
ruamel.yaml>=0.15, <0.16
-setuptools>=36, <37
shortuuid>=0.5, <0.6
SQLAlchemy>=1.1, <1.2
wagon>=0.6, <0.7
diff --git a/requirements.txt b/requirements.txt
index 2af8fe3..74fe9d5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,29 +7,26 @@
backports.shutil-get-terminal-size==1.0.0
blinker==1.4
bottle==0.12.13
-cachecontrol[filecache]==0.12.1
+cachecontrol[filecache]==0.12.3
click-didyoumean==0.0.3
click==6.7
colorama==0.3.9
decorator==4.1.2 # via networkx
-jinja2==2.9.6
-jsonpickle==0.9.4
+jinja2==2.10
+jsonpickle==0.9.5
lockfile==0.12.2 # via cachecontrol
-logutils==0.3.4.1
+logutils==0.3.5
markupsafe==1.0 # via jinja2
msgpack-python==0.4.8 # via cachecontrol
networkx==2.0
prettytable==0.7.2
-psutil==5.4.0
+psutil==5.4.1
requests==2.13.0
retrying==1.3.3
-ruamel.ordereddict==0.4.9 # via ruamel.yaml
-ruamel.yaml==0.15.34
+ruamel.ordereddict==0.4.13 # via ruamel.yaml
+ruamel.yaml==0.15.35
shortuuid==0.5.0
-six==1.10.0 # via retrying
-sqlalchemy==1.1.6
-wagon==0.6.0
+six==1.11.0 # via retrying
+sqlalchemy==1.1.15
+wagon==0.6.1
wheel==0.29.0 # via wagon
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
diff --git a/setup.py b/setup.py
index 8483c5b..c833da8 100644
--- a/setup.py
+++ b/setup.py
@@ -60,7 +60,7 @@
with open(os.path.join(root_dir, 'requirements.in')) as requirements:
for requirement in requirements.readlines():
- requirement = requirement.split('#')[0].strip() # Get rid of comments or trailing comments
+ requirement = requirement.split('#', 1)[0].strip() # Remove comments
if not requirement:
continue # Skip empty and comment lines
@@ -70,7 +70,7 @@
# https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies
# https://hynek.me/articles/conditional-python-dependencies/
if ';' in requirement:
- package, condition = requirement.split(';')
+ package, condition = requirement.split(';', 1)
cond_name = ':{0}'.format(condition.strip())
extras_require.setdefault(cond_name, [])
extras_require[cond_name].append(package.strip())
diff --git a/tests/cli/test_node_templates.py b/tests/cli/test_node_templates.py
index ff7ff28..548d968 100644
--- a/tests/cli/test_node_templates.py
+++ b/tests/cli/test_node_templates.py
@@ -18,7 +18,7 @@
from aria.cli.env import _Environment
-from .base_test import ( # pylint: disable=unused-import
+from .base_test import ( # pylint: disable=unused-import
TestCliBase,
mock_storage
)
diff --git a/tests/cli/test_nodes.py b/tests/cli/test_nodes.py
index 0233989..4d089a6 100644
--- a/tests/cli/test_nodes.py
+++ b/tests/cli/test_nodes.py
@@ -18,7 +18,7 @@
from aria.cli.env import _Environment
-from .base_test import ( # pylint: disable=unused-import
+from .base_test import ( # pylint: disable=unused-import
TestCliBase,
mock_storage
)
diff --git a/tests/cli/test_service_templates.py b/tests/cli/test_service_templates.py
index cc0150e..3c822a4 100644
--- a/tests/cli/test_service_templates.py
+++ b/tests/cli/test_service_templates.py
@@ -24,7 +24,7 @@
from aria.exceptions import AriaException
from aria.storage import exceptions as storage_exceptions
-from .base_test import ( # pylint: disable=unused-import
+from .base_test import ( # pylint: disable=unused-import
TestCliBase,
assert_exception_raised,
raise_exception,
diff --git a/tests/cli/test_services.py b/tests/cli/test_services.py
index 7dc84bc..286a2c3 100644
--- a/tests/cli/test_services.py
+++ b/tests/cli/test_services.py
@@ -22,7 +22,7 @@
from aria.modeling.exceptions import ParameterException
from aria.storage import exceptions as storage_exceptions
-from .base_test import ( # pylint: disable=unused-import
+from .base_test import ( # pylint: disable=unused-import
TestCliBase,
raise_exception,
assert_exception_raised,
diff --git a/tests/end2end/test_hello_world.py b/tests/end2end/test_hello_world.py
index 094ffc3..d603992 100644
--- a/tests/end2end/test_hello_world.py
+++ b/tests/end2end/test_hello_world.py
@@ -15,7 +15,7 @@
import requests
-from .testenv import testenv # pylint: disable=unused-import
+from .testenv import testenv # pylint: disable=unused-import
from .. import helpers
@@ -55,7 +55,7 @@
service = service_templates[0].services[service_name]
assert service.name == service_name
assert len(service.executions) == 1
- assert len(service.nodes) == 2
+ assert len(service.nodes) == 1
assert service.outputs['port'].value == 9090
assert all(node.state == node.STARTED for node in service.nodes.itervalues())
assert len(service.executions[0].logs) > 0
diff --git a/tests/end2end/test_nodecellar.py b/tests/end2end/test_nodecellar.py
index e8cfa84..2873040 100644
--- a/tests/end2end/test_nodecellar.py
+++ b/tests/end2end/test_nodecellar.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from .testenv import testenv # pylint: disable=unused-import
+from .testenv import testenv # pylint: disable=unused-import
from .. import helpers
diff --git a/tests/parser/__init__.py b/tests/extensions/__init__.py
similarity index 100%
copy from tests/parser/__init__.py
copy to tests/extensions/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/__init__.py
similarity index 100%
rename from tests/instantiation/__init__.py
rename to tests/extensions/aria_extension_tosca/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
similarity index 74%
rename from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
rename to tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
index cfd4d3c..14a2e8a 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/aria_v1_0/test_profile.py
@@ -13,11 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from tests.parser.service_templates import consume_types_use_case
-
-def test_use_case_shorthand_1_name():
- consume_types_use_case('shorthand-1', 'types')
-
-def test_use_case_typequalified_1_name():
- consume_types_use_case('typequalified-1', 'types')
+def test_profile(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - aria-1.0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/conftest.py b/tests/extensions/aria_extension_tosca/conftest.py
new file mode 100644
index 0000000..fdb2d4c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/conftest.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.
+
+"""
+PyTest configuration module.
+
+Add support for a "--tosca-parser" CLI option.
+
+For more information on PyTest hooks, see the `PyTest documentation
+<https://docs.pytest.org/en/latest/writing_plugins.html#pytest-hook-reference>`__.
+"""
+
+import pytest
+
+from ...mechanisms.parsing.aria import AriaParser
+
+
+def pytest_addoption(parser):
+ parser.addoption('--tosca-parser', action='store', default='aria', help='TOSCA parser')
+
+
+def pytest_report_header(config):
+ tosca_parser = config.getoption('--tosca-parser')
+ return 'tosca-parser: {0}'.format(tosca_parser)
+
+
+@pytest.fixture(scope='session')
+def parser(request):
+ tosca_parser = request.config.getoption('--tosca-parser')
+ verbose = request.config.getoption('verbose') > 0
+ if tosca_parser == 'aria':
+ with AriaParser() as p:
+ p.verbose = verbose
+ yield p
+ else:
+ pytest.fail('configured tosca-parser not supported: {0}'.format(tosca_parser))
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_nfv_v1_0/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
index cfd4d3c..fb756bc 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
@@ -13,11 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from tests.parser.service_templates import consume_types_use_case
-
-def test_use_case_shorthand_1_name():
- consume_types_use_case('shorthand-1', 'types')
-
-def test_use_case_typequalified_1_name():
- consume_types_use_case('typequalified-1', 'types')
+def test_profile(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_profile_for_nfv_1_0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py
similarity index 100%
rename from tests/parser/test_tosca_simple_v1_0/__init__.py
rename to tests/extensions/aria_extension_tosca/simple_v1_0/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/data.py b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py
new file mode 100644
index 0000000..104e6bb
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/data.py
@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+# Keywords
+
+TYPE_NAME_PLURAL = {
+ 'artifact': 'artifacts',
+ 'data': 'datatypes',
+ 'capability': 'capabilities',
+ 'interface': 'interfaces',
+ 'relationship': 'relationships',
+ 'node': 'nodes',
+ 'group': 'groups',
+ 'policy': 'policies'
+}
+TEMPLATE_NAME_SECTIONS = {
+ 'node': 'node_templates',
+ 'group': 'groups',
+ 'relationship': 'relationship_templates',
+ 'policy': 'policies'
+}
+PRIMITIVE_TYPE_NAMES = ('string', 'integer', 'float', 'boolean')
+PARAMETER_TYPE_NAMES = PRIMITIVE_TYPE_NAMES + ('MyType',)
+CONSTRAINTS_WITH_VALUE = ('equal', 'greater_than', 'greater_or_equal', 'less_than', 'less_or_equal')
+CONSTRAINTS_WITH_VALUE_LIST = ('valid_values',)
+CONSTRAINTS_WITH_VALUE_RANGE = ('in_range',)
+CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT = ('length', 'min_length', 'max_length')
+
+
+# Values
+
+PRIMITIVE_VALUES = ('null', 'true', 'a string', '123', '0.123', '[]', '{}')
+NOT_A_DICT = ('null', 'true', 'a string', '123', '0.123', '[]')
+NOT_A_DICT_WITH_ONE_KEY = NOT_A_DICT + ('{}', '{k1: v1, k2: v2}',)
+NOT_A_DICT_OR_STRING = ('null', 'true', '123', '0.123', '[]')
+NOT_A_LIST = ('null', 'true', 'a string', '123', '0.123', '{}')
+NOT_A_LIST_OF_TWO = NOT_A_LIST + ('[]', '[a]', '[a, b, c]')
+NOT_A_STRING = ('null', 'true', '123', '0.123', '[]', '{}')
+NOT_A_BOOL = ('null', 'a string', '123', '0.123', '[]', '{}')
+NOT_A_RANGE = NOT_A_LIST + (
+ '[]', '[ 1 ]', '[ 1, 2, 3 ]',
+ '[ 1, 1 ]', '[ 2, 1 ]',
+ '[ 1, a string ]', '[ a string, 1 ]',
+ '[ 1.5, 2 ]', '[ 1, 2.5 ]'
+)
+OCCURRENCES = ('[ 0, 1 ]', '[ 10, UNBOUNDED ]')
+BAD_OCCURRENCES = NOT_A_RANGE + ('[ -1, 1 ]', '[ 0, unbounded ]')
+GOOD_VERSIONS = ("'6.1'", '2.0.1', '3.1.0.beta', "'1.0.0.alpha-10'")
+BAD_VERSIONS = ('a_string', '1.2.3.4.5', '1.2.beta', '1.0.0.alpha-x')
+STATUSES = ('supported', 'unsupported', 'experimental', 'deprecated')
+PARAMETER_VALUES = (
+ ('string', 'a string'),
+ ('integer', '1'),
+ ('float', '1.1'),
+ ('MyType', '{my_field: a string}')
+)
+ENTRY_SCHEMA_VALUES = (
+ ('string', 'a string', 'another string'),
+ ('integer', '1', '2'),
+ ('float', '1.1', '2.2'),
+ ('MyType', '{my_field: a string}', '{}')
+)
+ENTRY_SCHEMA_VALUES_BAD = (
+ ('string', 'a string', '1'),
+ ('integer', '1', 'a string'),
+ ('float', '1.1', 'a string'),
+ ('MyType', '{my_field: a string}', 'a string')
+)
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py
new file mode 100644
index 0000000..23a274a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_concat.py
@@ -0,0 +1,102 @@
+ # -*- coding: utf-8 -*-
+# 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.
+
+
+def test_functions_concat_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { concat: [] }
+""").assert_success()
+
+
+def test_functions_concat_strings(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { concat: [ a, b, c ] }
+""").assert_success()
+
+
+def test_functions_concat_mixed(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { concat: [ a, 1, 1.1, null, [], {} ] }
+""").assert_success()
+
+
+def test_functions_concat_nested(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { concat: [ a, { concat: [ b, c ] } ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_concat_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: string
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { concat: [ 一, 二, 三 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py
new file mode 100644
index 0000000..6fa1a3c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_artifact.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+
+# Syntax
+
+def test_functions_get_artifact_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_artifact: [] } # needs at least two args
+""").assert_failure()
+
+
+# Arguments
+
+def test_functions_get_artifact_2_arguments(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+ artifacts:
+ my_artifact:
+ type: MyType
+ file: filename
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_artifact: [ my_node, my_artifact ] }
+""").assert_success()
+
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_functions_get_artifact_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+ artifacts:
+ my_artifact:
+ type: MyType
+ file: filename
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_artifact: [ unknown, my_artifact ] }
+""").assert_failure()
+
+
+def test_functions_get_artifact_3_arguments(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+ artifacts:
+ my_artifact:
+ type: MyType
+ file: filename
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_artifact: [ my_node, my_artifact, path ] }
+""").assert_success()
+
+
+def test_functions_get_artifact_4_arguments(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+ artifacts:
+ my_artifact:
+ type: MyType
+ file: filename
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_artifact: [ my_node, my_artifact, path, true ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_artifact_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ 類型: {}
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: string
+ artifacts:
+ 神器:
+ type: 類型
+ file: 文件名
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { get_artifact: [ 模板, 神器, 路徑, true ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py
new file mode 100644
index 0000000..a4c97a4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_input.py
@@ -0,0 +1,94 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+def test_functions_get_input_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_input: unknown }
+""").assert_failure()
+
+
+def test_functions_get_input(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ inputs:
+ my_input:
+ type: string
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_input: my_input }
+""").assert_success()
+
+
+def test_functions_get_input_nested(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ inputs:
+ my_input:
+ type: string
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_input: { concat: [ my, _, input ] } }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_input_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: string
+topology_template:
+ inputs:
+ 输入:
+ type: string
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { get_input: 输入 }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py
new file mode 100644
index 0000000..ffa2f9c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_nodes_of_type.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+def test_functions_get_nodes_of_type_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_nodes_of_type: unknown }
+""", import_profile=True).assert_failure()
+
+
+def test_functions_get_nodes_of_type(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: list
+ entry_schema: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_nodes_of_type: MyType }
+""", import_profile=True).assert_success()
+
+
+# Unicode
+
+def test_functions_get_nodes_of_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: list
+ entry_schema: string
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { get_nodes_of_type: 類型 }
+""", import_profile=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py
new file mode 100644
index 0000000..c115d0d
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_get_operation_output.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+# Syntax
+
+def test_functions_get_operation_output_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_operation_output: [] } # needs at least two args
+""").assert_failure()
+
+
+# Arguments
+
+def test_functions_get_operation_output(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation: {}
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+ interfaces:
+ MyInterface:
+ type: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { get_operation_output: [ my_node, MyInterface, my_operation, my_variable ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_get_operation_output_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ 類型:
+ 手術: {}
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: string
+ interfaces:
+ 接口:
+ type: 類型
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { get_operation_output: [ 模板, 接口, 手術, 變量 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py
new file mode 100644
index 0000000..8f99824
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_function_token.py
@@ -0,0 +1,119 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+def test_functions_token_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { token: [] } # needs exactly three args
+""").assert_failure()
+
+
+def test_functions_token_syntax_index_type(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { token: [ hello world, ' ', c ] }
+""").assert_failure()
+
+
+def test_functions_token_syntax_index_negative(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { token: [ hello world, ' ', -1 ] }
+""").assert_failure()
+
+
+def test_functions_token(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { token: [ hello world, ' ', 1 ] }
+""").assert_success()
+
+
+def test_functions_token_nested(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ properties:
+ my_parameter: { token: [ { token: [ hello/goodbye world, ' ', 0 ] }, '/', 1 ] }
+""").assert_success()
+
+
+# Unicode
+
+def test_functions_token_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型:
+ properties:
+ 參數:
+ type: string
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ properties:
+ 參數: { token: [ '你好,世界', ',', 1 ] }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py
new file mode 100644
index 0000000..365971a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/test_functions_modelable_entity.py
@@ -0,0 +1,247 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+
+# TODO:
+# other keywords (HOST, SOURCE, TARGET)
+# requirements
+# capabilities
+
+
+PERMUTATIONS = (
+ ('get_property', 'properties'),
+ ('get_attribute', 'attributes')
+)
+
+
+# Syntax
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_syntax_empty(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter: { {{ function }}: [] } # needs at least two args
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_syntax_single(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter: { {{ function }}: [ SELF ] } # needs at least two args
+""", dict(function=function, section=section)).assert_failure()
+
+
+# Entities
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_same(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter1:
+ type: string
+ my_parameter2:
+ type: string
+ default: a value
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter1: { {{ function }}: [ my_node, my_parameter2 ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_other(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter1:
+ type: string
+ my_parameter2:
+ type: string
+ default: a value
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType
+ {{ section }}:
+ my_parameter1: { {{ function }}: [ my_node2, my_parameter2 ] }
+ my_node2:
+ type: MyType
+ {{ section }}:
+ my_parameter1: a value
+""", dict(function=function, section=section)).assert_success()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_unknown(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ properties:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter: { get_property: [ unknown, my_parameter ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+# Cyclical
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_cyclical_simple(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter: { {{ function }}: [ my_node, my_parameter ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_cyclical_complex(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter1:
+ type: string
+ my_parameter2:
+ type: string
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter1: { {{ function }}: [ my_node, my_parameter2 ] }
+ my_parameter2: { {{ function }}: [ my_node, my_parameter1 ] }
+""", dict(function=function, section=section)).assert_failure()
+
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_sub(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter1:
+ type: string
+ my_parameter2:
+ type: MyType
+ default:
+ my_field: a value
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter1: { {{ function }}: [ my_node, my_parameter2, my_field ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+# Keywords
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_self(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ {{ section }}:
+ my_parameter1:
+ type: string
+ my_parameter2:
+ type: string
+ default: a value
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ {{ section }}:
+ my_parameter1: { {{ function }}: [ SELF, my_parameter2 ] }
+""", dict(function=function, section=section)).assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('function,section', PERMUTATIONS)
+def test_functions_modelable_entity_unicode(parser, function, section):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型:
+ {{ section }}:
+ 參數一:
+ type: string
+ 參數二:
+ type: string
+ default: 值
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+ {{ section }}:
+ 參數一: { {{ function }}: [ 模板, 參數二 ] }
+""", dict(function=function, section=section)).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.py
new file mode 100644
index 0000000..7d333e4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_copy.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = ('node', 'relationship')
+
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_templates_copy_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+ copying_template:
+ copy: {{ value }}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_copy(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+ copying_template:
+ copy: my_template
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_copy_unknown(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+ copying_template:
+ copy: unknown
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py
new file mode 100644
index 0000000..98ccc8c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_interface.py
@@ -0,0 +1,914 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_interfaces() %}
+ interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+ interfaces: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces: {{ caller()|indent(14) }}
+{%- endmacro %}
+{% macro interfaces() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_TYPE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+relationship_types:
+ MyType:
+ interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+RELATIONSHIP_TEMPLATE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_interfaces() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+relationship_types:
+ MyType:
+ interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro interfaces() %}
+ requirements:
+ - my_requirement:
+ relationship: my_template
+ relationship_templates:
+ my_template:
+ type: MyType
+ interfaces: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'relationship': RELATIONSHIP_MACROS,
+ 'relationship-type': RELATIONSHIP_TYPE_MACROS,
+ 'relationship-template': RELATIONSHIP_TEMPLATE_MACROS
+}
+
+PERMUTATIONS = (
+ ('main', 'node'),
+ ('main', 'group'),
+ ('main', 'relationship'),
+ ('relationship', 'node'),
+ ('relationship-type', 'node'),
+ ('relationship-template', 'node')
+)
+
+
+# Interfaces section
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(2, 1)
+))
+def test_template_interfaces_section_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() -%}
+{}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interfaces_section_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() -%}
+{}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Interface
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(2, 1)
+))
+def test_template_interface_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Interface input
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(2, 1)
+))
+def test_template_interface_inputs_section_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ inputs: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_inputs_section_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ inputs: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_VALUES,
+ counts=(2, 2)
+))
+def test_template_interface_input_from_type(parser, macros, name, type_name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ inputs:
+ my_input:
+ type: {{ type_name }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ inputs:
+ my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+ value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_VALUES,
+ counts=(2, 2)
+))
+def test_template_interface_input_from_interface_type(parser, macros, name, type_name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+interface_types:
+ MyType:
+ inputs:
+ my_input:
+ type: {{ type_name }}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ inputs:
+ my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+ value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_input_missing(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ inputs:
+ my_input: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name]),
+ adhoc_inputs=False).assert_failure()
+
+
+# Operation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT_OR_STRING,
+ counts=(2, 1)
+))
+def test_template_interface_operation_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_syntax_unsupported(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ unsupported: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_from_type(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_from_interface_type(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType:
+ my_operation: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_missing(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+# Operation implementation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT_OR_STRING,
+ counts=(2, 1)
+))
+def test_template_interface_operation_implementation_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_syntax_unsupported(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation:
+ unsupported: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_STRING,
+ counts=(2, 1)
+))
+def test_template_interface_operation_implementation_primary_syntax_type(parser, macros, name,
+ value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation:
+ primary: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_primary_short_form(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation: an implementation
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_LIST,
+ counts=(2, 1)
+))
+def test_template_interface_operation_implementation_dependencies_syntax_type(parser, macros, name,
+ value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation:
+ dependencies: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_STRING,
+ counts=(2, 1)
+))
+def test_template_interface_operation_implementation_dependencies_syntax_element_type(parser,
+ macros, name,
+ value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation:
+ dependencies:
+ - {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_implementation_dependencies_syntax_empty(parser, macros,
+ name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ implementation:
+ dependencies: []
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Operation input
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_VALUES,
+ counts=(2, 2)
+))
+def test_template_interface_operation_input_from_type(parser, macros, name, type_name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ inputs:
+ my_input:
+ type: {{ type_name }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ inputs:
+ my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+ value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,type_name,value', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_VALUES,
+ counts=(2, 2)
+))
+def test_template_interface_operation_input_from_interface_type(parser, macros, name, type_name,
+ value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+interface_types:
+ MyType:
+ my_operation:
+ inputs:
+ my_input:
+ type: {{ type_name }}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ inputs:
+ my_input: {{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], type_name=type_name,
+ value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_operation_input_missing(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call type_interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call interfaces() %}
+MyInterface:
+ my_operation:
+ inputs:
+ my_input: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name]),
+ adhoc_inputs=False).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_template_interface_unicode(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ 類型: {}
+{{ name }}_types:
+ 類型:
+{%- call type_interfaces() %}
+接口:
+ type: 類型
+ 手術:
+ inputs:
+ 輸入:
+ type: string
+{% endcall %}
+topology_template:
+ {{ section }}:
+ 模板:
+ type: 類型
+{%- call interfaces() %}
+接口:
+ 手術:
+ inputs:
+ 輸入: 值
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py
new file mode 100644
index 0000000..c5fcd30
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters.py
@@ -0,0 +1,781 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Unified testing for properties, attributes, and inputs.
+
+Additional tests for properties are in test_template_properties.py.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Assigning to parameters defined at a type
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+ {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Assigning to parameters defined at a capability type
+CAPABILITY_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+ capabilities:
+ my_capability:
+ type: MyType
+capability_types:
+ MyType:
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+ capabilities:
+ my_capability:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to parameters defined at an artifact type
+ARTIFACT_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %} {}
+artifact_types:
+ MyType:
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+ artifacts:
+ my_artifact:
+ type: MyType
+ file: a file
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined at an interface type
+INTERFACE_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+ interfaces:
+ MyInterface:
+ type: MyType
+interface_types:
+ MyType:
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+ interfaces:
+ MyInterface:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined at an operation of an interface type
+OPERATION_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro type_parameters() %}
+ interfaces:
+ MyInterface:
+ type: MyType
+interface_types:
+ MyType:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+{% macro parameters() %}
+ interfaces:
+ MyInterface:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined (added/overridden) at an interface of the template's type
+LOCAL_INTERFACE_MACROS = """
+{% macro additions() %}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ interfaces:
+ MyInterface:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+ interfaces:
+ MyInterface:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Assigning to inputs defined (added/overridden) at an operation of an interface of the template's
+# type
+LOCAL_OPERATION_MACROS = """
+{% macro additions() %}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ interfaces:
+ MyInterface:
+ type: MyType
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+{% macro parameters() %}
+ interfaces:
+ MyInterface:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to parameters defined at a relationship type
+RELATIONSHIP_TYPE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+relationship_types:
+ MyType:
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ {{ parameter_section }}: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined at an interface type
+RELATIONSHIP_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ MyInterface:
+ type: MyType
+interface_types:
+ MyType:
+ {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined at an operation of an interface
+# type
+RELATIONSHIP_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ MyInterface:
+ type: MyType
+interface_types:
+ MyType:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# interface of a relationship type
+RELATIONSHIP_TYPE_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+relationship_types:
+ MyType:
+ interfaces:
+ MyInterface:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# operation of an interface of a relationship type
+RELATIONSHIP_TYPE_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+relationship_types:
+ MyType:
+ interfaces:
+ MyInterface:
+ type: MyType
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# interface of the relationship of the node type
+RELATIONSHIP_LOCAL_INTERFACE_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ MyInterface:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(18) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# At a relationship of a node template, assigning to inputs defined (added/overridden) at an
+# operation of an interface of the relationship of the node type
+RELATIONSHIP_LOCAL_OPERATION_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- endmacro %}
+{% macro type_parameters() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ MyInterface:
+ type: MyType
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+{% macro parameters() %}
+ requirements:
+ - my_requirement:
+ relationship:
+ interfaces:
+ MyInterface:
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'capability': CAPABILITY_MACROS,
+ 'artifact': ARTIFACT_MACROS,
+ 'interface': INTERFACE_MACROS,
+ 'operation': OPERATION_MACROS,
+ 'local-interface': LOCAL_INTERFACE_MACROS,
+ 'local-operation': LOCAL_OPERATION_MACROS,
+ 'relationship-type': RELATIONSHIP_TYPE_MACROS,
+ 'relationship-interface': RELATIONSHIP_INTERFACE_MACROS,
+ 'relationship-operation': RELATIONSHIP_OPERATION_MACROS,
+ 'relationship-type-interface': RELATIONSHIP_TYPE_INTERFACE_MACROS,
+ 'relationship-type-operation': RELATIONSHIP_TYPE_OPERATION_MACROS,
+ 'relationship-local-interface': RELATIONSHIP_LOCAL_INTERFACE_MACROS,
+ 'relationship-local-operation': RELATIONSHIP_LOCAL_OPERATION_MACROS
+}
+
+PERMUTATIONS = (
+ ('main', 'node', 'properties'),
+ ('main', 'node', 'attributes'),
+ ('main', 'group', 'properties'),
+ ('main', 'relationship', 'properties'),
+ ('main', 'relationship', 'attributes'),
+ ('main', 'policy', 'properties'),
+ ('capability', 'node', 'properties'),
+ ('capability', 'node', 'attributes'),
+ ('artifact', 'node', 'properties'),
+ ('interface', 'node', 'inputs'),
+ ('interface', 'group', 'inputs'),
+ ('interface', 'relationship', 'inputs'),
+ ('operation', 'node', 'inputs'),
+ ('operation', 'group', 'inputs'),
+ ('operation', 'relationship', 'inputs'),
+ ('local-interface', 'node', 'inputs'),
+ ('local-interface', 'group', 'inputs'),
+ ('local-interface', 'relationship', 'inputs'),
+ ('local-operation', 'node', 'inputs'),
+ ('local-operation', 'group', 'inputs'),
+ ('local-operation', 'relationship', 'inputs'),
+ ('relationship-type', 'node', 'properties'),
+ ('relationship-interface', 'node', 'inputs'),
+ #('relationship-operation', 'node', 'inputs'), # fix
+ ('relationship-type-interface', 'node', 'inputs'),
+ ('relationship-type-operation', 'node', 'inputs'), # fix
+ ('relationship-local-interface', 'node', 'inputs'),
+ #('relationship-operation', 'node', 'inputs'), # fix
+)
+
+
+# Parameters section
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(3, 1)
+))
+def test_template_parameters_section_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameters_section_syntax_empty(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section)).assert_success()
+
+
+# Parameter
+
+@pytest.mark.parametrize('macros,name,parameter_section', (('capability', 'node', 'attributes'),))
+def test_template_parameter_missing(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() -%}
+{}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter: a value
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section)).assert_failure()
+
+
+# Entry schema
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+ PERMUTATIONS,
+ data.ENTRY_SCHEMA_VALUES,
+ counts=(3, 1)
+))
+def test_template_parameter_map(parser, macros, name, parameter_section, values):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+ default: default value
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: map
+ entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ key1: {{ values[1] }}
+ key2: {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ values=values), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+ PERMUTATIONS,
+ data.ENTRY_SCHEMA_VALUES_BAD,
+ counts=(3, 1)
+))
+def test_template_parameter_map_bad(parser, macros, name, parameter_section, values):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+ default: default value
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: map
+ entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ key1: {{ values[1] }}
+ key2: {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ values=values), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_map_required_field(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: map
+ entry_schema: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ key: {my_field: a value}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_map_required_field_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: map
+ entry_schema: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ key: {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+ PERMUTATIONS,
+ data.ENTRY_SCHEMA_VALUES,
+ counts=(3, 1)
+))
+def test_template_parameter_list(parser, macros, name, parameter_section, values):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+ default: default value
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: list
+ entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ - {{ values[1] }}
+ - {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ values=values), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,values', matrix(
+ PERMUTATIONS,
+ data.ENTRY_SCHEMA_VALUES_BAD,
+ counts=(3, 1)
+))
+def test_template_parameter_list_bad(parser, macros, name, parameter_section, values):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+ default: default value
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: list
+ entry_schema: {{ values[0] }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ - {{ values[1] }}
+ - {{ values[2] }}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ values=values), import_profile=True).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_list_required_field(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: list
+ entry_schema: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ - {my_field: a value}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section), import_profile=True).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_list_required_field_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: list
+ entry_schema: MyType
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() %}
+my_parameter:
+ - {}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section), import_profile=True).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_template_parameter_unicode(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ 類型:
+{%- call type_parameters() %}
+參數:
+ type: string
+{% endcall %}
+topology_template:
+ {{ section }}:
+ 模板:
+ type: 類型
+{%- call parameters() %}
+參數: 值
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name],
+ parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py
new file mode 100644
index 0000000..bd82766
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_template_parameters_properties.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Unified testing for properties (including inputs).
+
+These tests are in addition to the common tests for parameters in test_template_parameters.py.
+"""
+
+import pytest
+
+from .test_template_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = tuple(
+ (macros, name, parameter_section)
+ for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+ if parameter_section != 'attributes'
+)
+
+
+# Required
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_TYPE_NAMES,
+ counts=(3, 1)
+))
+def test_template_parameter_required(parser, macros, name, parameter_section, type_name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: {{ type_name }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ type_name=type_name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_TYPE_NAMES,
+ counts=(3, 1)
+))
+def test_template_parameter_not_required(parser, macros, name, parameter_section, type_name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: {{ type_name }}
+ required: false
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ type_name=type_name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,type_name,value', matrix(
+ PERMUTATIONS,
+ data.PARAMETER_VALUES,
+ counts=(3, 2)
+))
+def test_template_parameter_required_with_default(parser, macros, name, parameter_section,
+ type_name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+{{ name }}_types:
+ MyType:
+{%- call type_parameters() %}
+my_parameter:
+ type: {{ type_name }}
+ default: {{ value }}
+{% endcall %}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], parameter_section=parameter_section,
+ type_name=type_name, value=value)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py
new file mode 100644
index 0000000..62a10ed
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/test_templates.py
@@ -0,0 +1,128 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = ('node', 'group', 'relationship', 'policy')
+
+
+# Templates section
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT
+))
+def test_templates_section_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ {{ section }}: {{ value }}
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_templates_section_syntax_empty(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ {{ section }}: {}
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
+
+
+# Template
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_syntax_unsupported(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+ unsupported: {}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_syntax_empty(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ {{ section }}:
+ my_template: {} # "type" is required
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_template_description_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+topology_template:
+ {{ section }}:
+ my_template:
+ type: MyType
+ description: {{ value }}
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('name,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_template_type_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ {{ section }}:
+ my_template:
+ type: {{ value }}
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name], value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_type_unknown(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ {{ section }}:
+ my_template:
+ type: UnknownType
+""", dict(section=data.TEMPLATE_NAME_SECTIONS[name])).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_template_unicode(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ 類型: {}
+topology_template:
+ {{ section }}:
+ 模板:
+ type: 類型
+ description: 描述
+""", dict(name=name, section=data.TEMPLATE_NAME_SECTIONS[name])).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py
new file mode 100644
index 0000000..e9ccc89
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_artifacts.py
@@ -0,0 +1,307 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Here we are testing not only artifacts attached to node templates, but also artifacts attached to
+node types. The reason is that artifacts attached node types use the same property assignment
+(rather than definition) syntax we see in templates.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Artifacts attached to a node template
+TEMPLATE_MACROS = """
+{% macro artifacts() %}
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ artifacts: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Artifacts attached to a node type
+TYPE_MACROS = """
+{% macro artifacts() %}
+node_types:
+ MyType:
+ artifacts: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'template': TEMPLATE_MACROS,
+ 'type': TYPE_MACROS
+}
+
+PERMUTATIONS = (
+ 'template',
+ 'type'
+)
+
+
+
+# Artifacts section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_artifacts_section_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() -%}
+{{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifacts_section_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() -%}
+{}
+{% endcall %}
+""").assert_success()
+
+
+# Artifact
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_artifact_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_syntax_unsupported(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact: {} # "type" and "file" are required
+{% endcall %}
+""").assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_type_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+ type: {{ value }}
+ file: a file
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_type_unknown(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{%- call artifacts() %}
+my_artifact:
+ type: UnknownType
+ file: a file
+{% endcall %}
+""").assert_failure()
+
+
+# File
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_file_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_file(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+{% endcall %}
+""").assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_description_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ description: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_description(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ description: a description
+{% endcall %}
+""").assert_success()
+
+
+# Repository
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_repository_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ repository: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_repository_unknown(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ repository: unknown
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_repository(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ repository: my_repository
+{% endcall %}
+""").assert_success()
+
+
+# Deploy path
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_STRING))
+def test_node_template_artifact_deploy_path_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ deploy_path: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_deploy_path(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType: {}
+{%- call artifacts() %}
+my_artifact:
+ type: MyType
+ file: a file
+ deploy_path: a path
+{% endcall %}
+""").assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_artifact_unicode(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ 知識庫:
+ url: 網址
+artifact_types:
+ 類型: {}
+{%- call artifacts() %}
+神器:
+ type: 類型
+ file: 文件
+ repository: 知識庫
+ deploy_path: 路徑
+{% endcall %}
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py
new file mode 100644
index 0000000..5ea2c13
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_directives.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_template_directives_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ directives: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_template_directives_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ directives: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_directives_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ directives: []
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_directives_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型: {}
+topology_template:
+ node_templates:
+ 節點:
+ type: 類型
+ directives:
+ - 指示一
+ - 指示二
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py
new file mode 100644
index 0000000..fad439e
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filter_constraints.py
@@ -0,0 +1,346 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Compare with test_type_properties.py. Note that though the constraints are the same, their syntax
+is very different, making it difficult to test all permutations together.
+"""
+
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Properties for node filter in node template
+MAIN_MACROS = """
+{% macro additions() %}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+node_types:
+ MyType1: {}
+ MyType2:
+ properties:
+ data_property:
+ type: MyType
+ string_property:
+ type: string
+{%- endmacro %}
+{% macro properties() %}
+ node_filter:
+ properties: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Capability properties for node filter in node template
+MAIN_CAPABILITY_MACROS = """
+{% macro additions() %}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+capability_types:
+ MyType:
+ properties:
+ data_property:
+ type: MyType
+ string_property:
+ type: string
+node_types:
+ MyType1: {}
+ MyType2:
+ capabilities:
+ my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+ node_filter:
+ capabilities:
+ - my_capability:
+ properties: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# Properties for node filter in requirement
+REQUIREMENT_MACROS = """
+{% macro additions() %}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+capability_types:
+ MyType: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyType2:
+ properties:
+ data_property:
+ type: MyType
+ string_property:
+ type: string
+ capabilities:
+ my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+ requirements:
+ - my_requirement:
+ node: MyType2
+ node_filter:
+ properties: {{ caller()|indent(16) }}
+{%- endmacro %}
+"""
+
+# Capability properties for node filter in requirement
+REQUIREMENT_CAPABILITY_MACROS = """
+{% macro additions() %}
+data_types:
+ MyType:
+ properties:
+ my_field:
+ type: string
+capability_types:
+ MyType:
+ properties:
+ data_property:
+ type: MyType
+ string_property:
+ type: string
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyType2:
+ capabilities:
+ my_capability: MyType
+{%- endmacro %}
+{% macro properties() %}
+ requirements:
+ - my_requirement:
+ node: MyType2
+ node_filter:
+ capabilities:
+ - my_capability:
+ properties: {{ caller()|indent(22) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'requirement': REQUIREMENT_MACROS,
+ 'main-capability': MAIN_CAPABILITY_MACROS,
+ 'requirement-capability': REQUIREMENT_CAPABILITY_MACROS
+}
+
+PERMUTATIONS = (
+ 'main', 'requirement', 'main-capability', 'requirement-capability'
+)
+
+
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT_WITH_ONE_KEY))
+def test_node_template_node_filter_constraints_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_syntax_unsupported(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { unsupported: a string }
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS, data.CONSTRAINTS_WITH_VALUE))
+def test_node_template_node_filter_constraints_with_value(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: {my_field: a string} }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_LIST))
+def test_node_template_node_filter_constraints_with_value_list(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: a}, {my_field: b}, {my_field: c} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: string a}, {my_field: string b} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range_too_many(parser, macros,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: a}, {my_field: b}, {my_field: c} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE))
+def test_node_template_node_filter_constraints_with_value_range_bad(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- data_property: { {{ constraint }}: [ {my_field: string b}, {my_field: string a} ] }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_pattern(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- string_property: { pattern: ^pattern$ }
+{% endcall %}
+""").assert_success()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_constraints_pattern_bad(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- string_property: { pattern: ( }
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT))
+def test_node_template_node_filter_constraints_with_value_integer(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- string_property: { {{ constraint }}: 1 }
+{% endcall %}
+""", dict(constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,constraint', matrix(PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT))
+def test_node_template_node_filter_constraints_with_value_integer_bad(parser, macros, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call properties() %}
+- string_property: { {{ constraint }}: -1 }
+{% endcall %}
+""", dict(constraint=constraint)).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py
new file mode 100644
index 0000000..0a54961
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_node_filters.py
@@ -0,0 +1,313 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Node filter in node template
+MAIN_MACROS = """
+{% macro additions() %}
+node_types:
+ MyType1: {}
+ MyType2:
+ properties:
+ my_property:
+ type: string
+{%- endmacro %}
+{% macro node_filter() %}
+ node_filter: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+
+# Node filter in requirement
+REQUIREMENT_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType:
+ properties:
+ my_property:
+ type: string
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyType2:
+ properties:
+ my_property:
+ type: string
+ capabilities:
+ my_capability: MyType
+{%- endmacro %}
+{% macro node_filter() %}
+ requirements:
+ - my_requirement:
+ node: MyType2
+ node_filter: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'requirement': REQUIREMENT_MACROS
+}
+
+PERMUTATIONS = (
+ 'main', 'requirement'
+)
+
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_node_filter_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() -%}
+{{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_syntax_unsupported(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() -%}
+{}
+{% endcall %}
+""").assert_success()
+
+
+# Properties section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_properties_section_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+properties: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_properties_section_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+properties: []
+{% endcall %}
+""").assert_success()
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_capabilities_section_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capabilities_section_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities: []
+{% endcall %}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_DICT))
+def test_node_template_node_filter_capability_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities:
+ - my_capability: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_syntax_unsupported(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities:
+ - my_capability:
+ unsupported: {}
+{% endcall %}
+""").assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities:
+ - my_capability: {}
+{% endcall %}
+""").assert_success()
+
+
+# Capability properties section
+
+@pytest.mark.parametrize('macros,value', matrix(PERMUTATIONS, data.NOT_A_LIST))
+def test_node_template_node_filter_capability_properties_section_syntax_type(parser, macros, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities:
+ - my_capability:
+ properties: {{ value }}
+{% endcall %}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros', PERMUTATIONS)
+def test_node_template_node_filter_capability_properties_section_syntax_empty(parser, macros):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+{%- call node_filter() %}
+capabilities:
+ - my_capability:
+ properties: []
+{% endcall %}
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_node_filter_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ 類型: {}
+node_types:
+ 類型一:
+ requirements:
+ - 需求:
+ capability: 類型
+ 類型二:
+ properties:
+ 屬性:
+ type: string
+ capabilities:
+ 能力: 類型
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型一
+ node_filter:
+ properties:
+ - 屬性: { equal: 值 }
+ capabilities:
+ - my_capability:
+ properties:
+ - 屬性: { equal: 值 }
+ requirements:
+ - 需求:
+ node: 類型二
+ node_filter:
+ properties:
+ - 屬性: { equal: 值 }
+ capabilities:
+ - 能力:
+ properties:
+ - 屬性: { equal: 值 }
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py
new file mode 100644
index 0000000..fdf6f16
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/test_node_template_requirements.py
@@ -0,0 +1,853 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+
+
+# Section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_template_requirements_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_requirements_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements: []
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_template_requirement_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_template_requirement_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ unsupported: {}
+""").assert_failure()
+
+
+def test_node_template_requirement_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement: {}
+""").assert_success()
+
+
+# Capability
+
+def test_node_template_requirement_capability_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: unknown # neither a type nor a name
+""").assert_failure()
+
+
+# Capability type
+
+def test_node_template_requirement_capability_type_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+""").assert_success()
+
+
+def test_node_template_requirement_capability_type_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType1
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_type_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1:
+ derived_from: MyType2
+ MyType2: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType1
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType2
+""").assert_failure()
+
+
+def test_node_template_requirement_capability_type_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement: MyType
+""").assert_success()
+
+
+# Capability definition name
+
+def test_node_template_requirement_capability_name(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement: MyType
+ MyType2:
+ capabilities:
+ my_capability: MyType
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ capability: my_capability
+ my_node2:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_name_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement: MyType1
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ capability: my_capability
+ my_node2:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_capability_name_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement: MyType1
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ capability: my_capability
+ my_node2:
+ type: MyType2
+""").assert_failure()
+
+
+# Node
+
+def test_node_template_requirement_node_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ node: unknown
+""").assert_failure()
+
+
+# Node type
+
+def test_node_template_requirement_node_type_undefined(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement: MyType1
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+ MyType3:
+ derived_from: MyType2
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: MyType3
+""").assert_success()
+
+
+def test_node_template_requirement_node_type_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+ MyType3: {}
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: MyType3
+""").assert_failure()
+
+
+# Node template
+
+def test_node_template_requirement_node_template_undefined(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement: MyType1
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ my_node2:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ my_node2:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+ MyType3:
+ derived_from: MyType2
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ my_node2:
+ type: MyType3
+""").assert_success()
+
+
+def test_node_template_requirement_node_template_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ node: MyType2
+ MyType2:
+ capabilities:
+ my_capability: MyType2
+ MyType3: {}
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ requirements:
+ - my_requirement:
+ node: my_node2
+ my_node2:
+ type: MyType3
+""").assert_failure()
+
+
+# Relationship
+
+def test_node_template_requirement_relationship_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ unsupported: {}
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: {}
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: unknown
+""").assert_failure()
+
+
+# Relationship type
+
+def test_node_template_requirement_relationship_type_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_type_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType1
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_type_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType1:
+ derived_from: MyType2
+ MyType2: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType1
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType2
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_type_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+""").assert_success()
+
+
+# Relationship template
+
+def test_node_template_requirement_relationship_template_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ relationship_templates:
+ my_relationship:
+ type: MyType
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: my_relationship
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_template_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType1
+topology_template:
+ relationship_templates:
+ my_relationship:
+ type: MyType2
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: my_relationship
+""").assert_success()
+
+
+def test_node_template_requirement_relationship_template_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType1:
+ derived_from: MyType2
+ MyType2: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType1
+topology_template:
+ relationship_templates:
+ my_relationship:
+ type: MyType2
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: my_relationship
+""").assert_failure()
+
+
+def test_node_template_requirement_relationship_template_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+topology_template:
+ relationship_templates:
+ my_relationship:
+ type: MyType
+ node_templates:
+ my_node:
+ type: MyType
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: my_relationship
+""").assert_success()
+
+
+# Unicode
+
+def test_node_template_requirement_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ 類型: {}
+relationship_types:
+ 類型: {}
+node_types:
+ 類型:
+ requirements:
+ - 需求:
+ capability: 類型
+ relationship: 類型
+topology_template:
+ relationship_templates:
+ 關係:
+ type: 類型
+ node_templates:
+ 節點:
+ type: 類型
+ requirements:
+ - 需求:
+ capability: 類型
+ relationship: 關係
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py
new file mode 100644
index 0000000..cf7f54d
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_group.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Members
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_group_members_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType: {}
+topology_template:
+ groups:
+ my_group:
+ type: MyType
+ members: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_group_members_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType: {}
+topology_template:
+ groups:
+ my_group:
+ type: MyType
+ members: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_group_members_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType: {}
+topology_template:
+ groups:
+ my_group:
+ type: MyType
+ members: []
+""").assert_success()
+
+
+def test_group_members(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+group_types:
+ MyType:
+ members: [ MyType1, MyType2 ]
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ my_node2:
+ type: MyType2
+ groups:
+ my_group:
+ type: MyType
+ members: [ my_node1, my_node2 ]
+""").assert_success()
+
+
+def test_group_members_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+group_types:
+ MyType:
+ members: [ MyType1 ]
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType2
+ groups:
+ my_group:
+ type: MyType
+ members: [ my_node ]
+""").assert_success()
+
+
+def test_group_members_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+group_types:
+ MyType:
+ members: [ MyType1 ]
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType2
+ groups:
+ my_group:
+ type: MyType
+ members: [ my_node ]
+""").assert_failure()
+
+
+def test_group_members_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType: {}
+topology_template:
+ groups:
+ my_group:
+ type: MyType
+ members: [ unknown ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_group_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型: {}
+group_types:
+ 類型:
+ members: [ 類型 ]
+topology_template:
+ node_templates:
+ 節點:
+ type: 類型
+ groups:
+ 政策:
+ type: 類型
+ members: [ 節點 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py
new file mode 100644
index 0000000..4828583
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_policy.py
@@ -0,0 +1,272 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Targets
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_policy_targets_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType: {}
+topology_template:
+ policies:
+ my_policy:
+ type: MyType
+ targets: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_policy_targets_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType: {}
+topology_template:
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_policy_targets_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType: {}
+topology_template:
+ policies:
+ my_policy:
+ type: MyType
+ targets: []
+""").assert_success()
+
+
+def test_policy_targets_nodes(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1, MyType2 ]
+topology_template:
+ node_templates:
+ my_node1:
+ type: MyType1
+ my_node2:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_node1, my_node2 ]
+""").assert_success()
+
+
+def test_policy_targets_nodes_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+policy_types:
+ MyType:
+ targets: [ MyType1 ]
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_node ]
+""").assert_success()
+
+
+def test_policy_targets_nodes_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1:
+ derived_from: MyType2
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1 ]
+topology_template:
+ node_templates:
+ my_node:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_node ]
+""").assert_failure()
+
+
+def test_policy_targets_groups(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType1: {}
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1, MyType2 ]
+topology_template:
+ groups:
+ my_group1:
+ type: MyType1
+ my_group2:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_group1, my_group2 ]
+""").assert_success()
+
+
+def test_policy_targets_groups_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+policy_types:
+ MyType:
+ targets: [ MyType1 ]
+topology_template:
+ groups:
+ my_group:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_group ]
+""").assert_success()
+
+
+def test_policy_targets_groups_not_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+ MyType1: {}
+ MyType1:
+ derived_from: MyType2
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1 ]
+topology_template:
+ groups:
+ my_group:
+ type: MyType2
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_group ]
+""").assert_failure()
+
+
+def test_policy_targets_nodes_and_groups(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyNodeType: {}
+group_types:
+ MyGroupType: {}
+policy_types:
+ MyType:
+ targets: [ MyNodeType, MyGroupType ]
+topology_template:
+ node_templates:
+ my_node:
+ type: MyNodeType
+ groups:
+ my_group:
+ type: MyGroupType
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_node, my_group ]
+""").assert_success()
+
+
+def test_policy_targets_ambiguous(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyNodeType: {}
+group_types:
+ MyGroupType: {}
+policy_types:
+ MyType:
+ targets: [ MyNodeType, MyGroupType ]
+topology_template:
+ node_templates:
+ my_template:
+ type: MyNodeType
+ groups:
+ my_template:
+ type: MyGroupType
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ my_template ]
+""").assert_success()
+
+
+def test_policy_targets_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType: {}
+topology_template:
+ policies:
+ my_policy:
+ type: MyType
+ targets: [ unknown ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_policy_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型: {}
+policy_types:
+ 類型:
+ targets: [ 類型 ]
+topology_template:
+ node_templates:
+ 節點:
+ type: 類型
+ policies:
+ 政策:
+ type: 類型
+ targets: [ 節點 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py
new file mode 100644
index 0000000..8903b97
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_substitution_mappings.py
@@ -0,0 +1,449 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ substitution_mappings: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ unsupported: {}
+""").assert_failure()
+
+
+def test_substitution_mappings_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ substitution_mappings: {} # "node_type" is required
+""").assert_failure()
+
+
+# Node type
+
+def test_substitution_mappings_node_type_syntax_type(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ description: a description
+ substitution_mappings:
+ node_type: {{ value }}
+""").assert_failure()
+
+
+# Requirements section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_requirements_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_requirements_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ requirements: {}
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST_OF_TWO)
+def test_substitution_mappings_requirement_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_requirement_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: [ my_template, my_internal_requirement ]
+""").assert_success()
+
+
+def test_substitution_mappings_requirement_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType2
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: [ my_template, my_internal_requirement ]
+""").assert_success()
+
+
+def test_substitution_mappings_requirement_bad(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType2
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: [ my_template, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ unknown: [ my_template, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown_mapped_template(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: [ unknown, my_internal_requirement ]
+""").assert_failure()
+
+
+def test_substitution_mappings_requirement_unknown_mapped_requirement(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ MyInternalType:
+ requirements:
+ - my_internal_requirement:
+ capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ requirements:
+ my_requirement: [ my_template, unknown ]
+""").assert_failure()
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_substitution_mappings_capabilities_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ capabilities: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_capabilities_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ capabilities: {}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST_OF_TWO)
+def test_substitution_mappings_capability_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType
+topology_template:
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_substitution_mappings_capability_same(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: [ my_template, my_internal_capability ]
+""").assert_success()
+
+
+def test_substitution_mappings_capability_derived(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType1
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType2
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: [ my_template, my_internal_capability ]
+""").assert_success()
+
+
+def test_substitution_mappings_capability_bad(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType1
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType2
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: [ my_template, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ unknown: [ my_template, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown_mapped_template(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: [ unknown, my_internal_capability ]
+""").assert_failure()
+
+
+def test_substitution_mappings_capability_unknown_mapped_capability(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability: MyType
+ MyInternalType:
+ capabilities:
+ my_internal_capability: MyType
+topology_template:
+ node_templates:
+ my_template:
+ type: MyInternalType
+ substitution_mappings:
+ node_type: MyType
+ capabilities:
+ my_capability: [ my_template, unknown ]
+""").assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py
new file mode 100644
index 0000000..ac86a4a
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/test_topology_template.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_topology_template_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_topology_template_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ unsupported: {}
+""").assert_failure()
+
+
+def test_topology_template_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template: {}
+""").assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_topology_template_description_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_topology_template_description(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ description: a description
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
new file mode 100644
index 0000000..3defb70
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from . import data
+
+
+@pytest.mark.parametrize('value', data.PRIMITIVE_VALUES)
+def test_dsl_definitions_syntax_anything(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+def test_dsl_definitions_anchor(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions:
+ key: &ANCHOR
+ field: a value
+""").assert_success()
+
+
+# Unicode
+
+def test_dsl_definitions_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+dsl_definitions:
+ 定義: &ANCHOR # YAML does not allow the anchor name to be Unicode
+ 領域: 值
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py
new file mode 100644
index 0000000..07a0d9b
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_imports.py
@@ -0,0 +1,200 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from . import data
+from ....mechanisms.web_server import WebServer
+
+
+# Fixtures
+
+NODE_TYPE_IMPORT = """
+node_types:
+ MyNode: {}
+"""
+
+NODE_TYPE_IMPORT_UNICODE = """
+node_types:
+ 類型: {}
+"""
+
+BAD_IMPORT = """
+node_types:
+ MyNode:
+ derived_from: UnknownType
+"""
+
+@pytest.fixture(scope='session')
+def repository():
+ repository = WebServer()
+ repository.add_text_yaml('/imports/node-type.yaml', NODE_TYPE_IMPORT)
+ repository.add_text_yaml('/imports/{0}.yaml'.format(WebServer.escape('節點類型')),
+ NODE_TYPE_IMPORT_UNICODE)
+ repository.add_text_yaml('/imports/bad.yaml', BAD_IMPORT)
+ with repository:
+ yield repository.root
+
+
+# Imports section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_imports_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_imports_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports: []
+""").assert_success()
+
+
+# Import
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_import_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_import_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - unsupported: {}
+""").assert_failure()
+
+
+def test_import_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - {} # "file" is required
+""").assert_failure()
+
+
+# File
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_import_file_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - file: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_import_file_short_form(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - {{ repository }}/imports/node-type.yaml
+topology_template:
+ node_templates:
+ my_node:
+ type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+def test_import_file(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - file: {{ repository }}/imports/node-type.yaml
+topology_template:
+ node_templates:
+ my_node:
+ type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Repository
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_import_repository(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: {{ repository }}/imports/
+imports:
+ - file: node-type.yaml
+ repository: my_repository
+topology_template:
+ node_templates:
+ my_node:
+ type: MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Namespace
+
+@pytest.mark.xfail(reason='not yet implemented')
+def test_import_namespace(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - file: {{ repository }}/imports/node-type.yaml
+ namespace_uri:
+ namespace_prefix: my_namespace
+topology_template:
+ node_templates:
+ my_node:
+ type: my_namespace.MyNode
+""", dict(repository=repository)).assert_success()
+
+
+# Bad imports
+
+def test_import_not_found(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - does_not_exist
+""").assert_failure()
+
+
+def test_import_bad(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - {{ repository }}/imports/bad.yaml
+topology_template:
+ node_templates:
+ my_node:
+ type: MyNode
+""", dict(repository=repository)).assert_failure()
+
+
+# Unicode
+
+def test_import_unicode(parser, repository):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+imports:
+ - {{ repository }}/imports/節點類型.yaml
+topology_template:
+ node_templates:
+ 模板:
+ type: 類型
+""", dict(repository=repository)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py
new file mode 100644
index 0000000..f80c40b
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_metadata.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from . import data
+from ....mechanisms.utils import matrix
+
+
+NORMATIVE_FIELD_NAMES = ('template_name', 'template_author', 'template_version')
+
+
+# Metadata section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_metadata_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_metadata_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata: {}
+""").assert_success()
+
+
+# Fields
+
+@pytest.mark.parametrize('field,value', matrix(
+ NORMATIVE_FIELD_NAMES,
+ data.NOT_A_STRING
+))
+def test_metadata_normative_fields_syntax_type(parser, field, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+ {{ field }}: {{ value }}
+""", dict(field=field, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_metadata_non_normative_fields_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+ non_normative: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Template version
+
+@pytest.mark.parametrize('value', data.GOOD_VERSIONS)
+def test_metadata_normative_template_version(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+ template_version: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+@pytest.mark.parametrize('value', data.BAD_VERSIONS)
+def test_metadata_normative_template_bad_version(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+ template_version: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Unicode
+
+def test_metadata_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+metadata:
+ template_name: 詠嘆調
+ template_author: 詠嘆調
+ template_version: 1.0.0.詠嘆調-10
+ non_normative1: 詠嘆調一
+ non_normative2: 詠嘆調二
+ non_normative3: 詠嘆調三
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py
new file mode 100644
index 0000000..54cfd90
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_names.py
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+
+def test_names_shorthand(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ node_templates:
+ my_server:
+ type: Compute
+ requirements:
+ - local_storage:
+ node: my_block_storage
+ relationship:
+ type: AttachesTo
+ properties:
+ location: /path1/path2
+ my_block_storage:
+ type: BlockStorage
+ properties:
+ size: 10 GB
+""", import_profile=True).assert_success()
+
+
+def test_names_type_qualified(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+topology_template:
+ node_templates:
+ my_server:
+ type: tosca:Compute
+ requirements:
+ - local_storage:
+ node: my_block_storage
+ relationship:
+ type: AttachesTo
+ properties:
+ location: /path1/path2
+ my_block_storage:
+ type: tosca:BlockStorage
+ properties:
+ size: 10 GB
+""", import_profile=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
index cfd4d3c..922f9dc 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
@@ -13,11 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from tests.parser.service_templates import consume_types_use_case
-
-def test_use_case_shorthand_1_name():
- consume_types_use_case('shorthand-1', 'types')
-
-def test_use_case_typequalified_1_name():
- consume_types_use_case('typequalified-1', 'types')
+def test_profile(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+""", import_profile=True, validate_normative=True).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py
new file mode 100644
index 0000000..9d40e22
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_repositories.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from . import data
+
+
+# Repositories section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_repositories_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repositories_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories: {}
+""").assert_success()
+
+
+# Repository
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_repository_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ unsupported: {}
+""").assert_failure()
+
+
+def test_repository_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository: {} # "url" is required
+""").assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_repository_description_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_description(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ description: a description
+""").assert_success()
+
+
+# URL
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_repository_url_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_repository_url_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository: a url
+""").assert_success()
+
+
+# Credential
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_repository_credential_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ credential: {{ value }}
+""", dict(value=value), import_profile=True).assert_failure()
+
+
+def test_repository_credential_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ credential:
+ unsupported: {}
+""", import_profile=True).assert_failure()
+
+
+def test_repository_credential_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ credential: {}
+""", import_profile=True).assert_success()
+
+
+def test_repository_credential_full(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ my_repository:
+ url: a url
+ credential:
+ protocol: a protocol
+ token_type: a token type
+ token: a token
+ keys:
+ key1: value1
+ key2: value2
+ user: a user
+""", import_profile=True).assert_success()
+
+
+# Unicode
+
+def test_repository_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+repositories:
+ 知識庫:
+ url: 網址
+ description: 描述
+ credential:
+ protocol: 協議
+ token_type: 類型
+ token: 代幣
+ keys:
+ 鍵一: 值
+ 鍵二: 值
+ user: 用戶
+""", import_profile=True).assert_success()
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
similarity index 74%
copy from tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
index cfd4d3c..2bcd018 100644
--- a/tests/parser/test_tosca_simple_v1_0/presentation/test_types.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# 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.
@@ -13,11 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from tests.parser.service_templates import consume_types_use_case
-
-def test_use_case_shorthand_1_name():
- consume_types_use_case('shorthand-1', 'types')
-
-def test_use_case_typequalified_1_name():
- consume_types_use_case('typequalified-1', 'types')
+def test_service_template_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+unsupported: {}
+""").assert_failure()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/common/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py
new file mode 100644
index 0000000..6343442
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_interfaces.py
@@ -0,0 +1,469 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Interfaces defined at a type
+MAIN_MACROS = """
+{% macro additions() %}
+{%- endmacro %}
+{% macro interfaces() %}
+ interfaces: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+# Interfaces defined (added/overridden) at a relationship of a requirement within a node type
+RELATIONSHIP_MACROS = """
+{% macro additions() %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+{%- endmacro %}
+{% macro interfaces() %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces: {{ caller()|indent(14) }}
+{%- endmacro %}
+"""
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'relationship': RELATIONSHIP_MACROS
+}
+
+PERMUTATIONS = (
+ ('main', 'node'),
+ ('main', 'group'),
+ ('main', 'relationship'),
+ ('relationship', 'node')
+)
+
+PERMUTATIONS_NO_RELATIONSHIP = tuple(
+ (macros, name)
+ for macros, name in PERMUTATIONS
+ if macros != 'relationship'
+)
+
+
+# Interfaces section
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_DICT,
+ counts=(2, 1)
+))
+def test_type_interfaces_section_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interfaces_section_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() -%}
+{}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Interface
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_DICT,
+ counts=(2, 1)
+))
+def test_type_interface_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface: {} # "type" is required
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+# Type
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_type_override(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+{{ name }}_types:
+ MyType1:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType1
+{% endcall %}
+ MyType2:
+ derived_from: MyType1
+{%- call interfaces() %}
+MyInterface:
+ type: MyType2
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# We are skipping relationship interfaces, because node requirements can be overridden completely
+@pytest.mark.parametrize('macros,name', PERMUTATIONS_NO_RELATIONSHIP)
+def test_type_interface_type_override_bad(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType1: {}
+ MyType2: {}
+{{ name }}_types:
+ MyType1:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType1
+{% endcall %}
+ MyType2:
+ derived_from: MyType1
+{%- call interfaces() %}
+MyInterface:
+ type: MyType2
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+# Operation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_DICT_OR_STRING,
+ counts=(2, 1)
+))
+def test_type_interface_operation_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_syntax_unsupported(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ unsupported: {}
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation: {}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Operation description
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_STRING,
+ counts=(2, 1)
+))
+def test_type_interface_operation_description_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ description: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_description(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ description: a description
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Operation implementation
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_DICT_OR_STRING,
+ counts=(2, 1)
+))
+def test_type_interface_operation_implementation_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_syntax_unsupported(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation:
+ unsupported: {}
+{% endcall %}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation: {}
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_STRING,
+ counts=(2, 1)
+))
+def test_type_interface_operation_implementation_primary_syntax_type(parser, macros, name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation:
+ primary: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_primary_short_form(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation: an implementation
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_LIST,
+ counts=(2, 1)
+))
+def test_type_interface_operation_implementation_dependencies_syntax_type(parser, macros, name,
+ value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies: {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,value', matrix(
+ PERMUTATIONS, data.NOT_A_STRING,
+ counts=(2, 1)
+))
+def test_type_interface_operation_implementation_dependencies_syntax_element_type(parser, macros,
+ name, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies:
+ - {{ value }}
+{% endcall %}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_operation_implementation_dependencies_syntax_empty(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ MyType: {}
+{{ name }}_types:
+ MyType:
+{%- call interfaces() %}
+MyInterface:
+ type: MyType
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies: []
+{% endcall %}
+""", dict(name=name)).assert_success()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name', PERMUTATIONS)
+def test_type_interface_unicode(parser, macros, name):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+interface_types:
+ 類型: {}
+{{ name }}_types:
+ 類型:
+{%- call interfaces() %}
+接口:
+ type: 類型
+ 手術:
+ implementation: 履行
+{% endcall %}
+""", dict(name=name)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py
new file mode 100644
index 0000000..3cbf47c
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters.py
@@ -0,0 +1,418 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Unified testing for properties, attributes, and inputs.
+
+Additional tests for properties are in test_type_properties.py.
+
+Note: artifact definitions within node types use parameter assignments rather than definitions, and
+thus are tested not here but under test_template_parameters.py.
+"""
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+# Defining parameters at a type
+MAIN_MACROS = """
+{% macro additions(section=True) %}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ {{ parameter_section }}: {{ caller()|indent(6) }}
+{%- endmacro %}
+"""
+
+# Defining parameters at a nested type (e.g. inputs of an operation within an interface type)
+NESTED_MACROS = """
+{% macro additions(section=True) %}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ nested:
+ {{ parameter_section }}: {{ caller()|indent(8) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an interface of a type
+INTERFACE_MACROS = """
+{% macro additions(section=True) %}
+interface_types:
+ MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ interfaces:
+ my_interface:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an operation of an interface of a type
+OPERATION_MACROS = """
+{% macro additions(section=True) %}
+interface_types:
+ MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ interfaces:
+ my_interface:
+ type: MyType
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(12) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an interface of a relationship of a requirement of a node type
+RELATIONSHIP_INTERFACE_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ my_interface:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(18) }}
+{%- endmacro %}
+"""
+
+# Defining inputs at an operation of an interface of a relationship of a requirement of a node type
+RELATIONSHIP_OPERATION_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+interface_types:
+ MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ my_interface:
+ type: MyType
+ my_operation:
+ {{ parameter_section }}: {{ caller()|indent(20) }}
+{%- endmacro %}
+"""
+
+# Defining parameters at a capability of a node type
+CAPABILITY_MACROS = """
+{% macro additions(section=True) %}
+capability_types:
+ MyType: {}
+{%- if section %}
+{{ name }}_types:
+{%- endif %}
+{%- endmacro %}
+{% macro parameters(t='MyType', d=None) %}
+ {{ t }}:
+{%- if d %}
+ derived_from: {{ d }}
+{%- endif %}
+ capabilities:
+ my_capability:
+ type: MyType
+ {{ parameter_section }}: {{ caller()|indent(10) }}
+{%- endmacro %}
+"""
+
+# Defining inputs/outputs at a topology template
+TOPOLOGY_MACROS = """
+{% macro additions(section=None) %}
+topology_template:
+{%- endmacro %}
+{% macro parameters(t=None) %}
+ {{ parameter_section }}: {{ caller()|indent(4) }}
+{%- endmacro %}
+"""
+
+
+MACROS = {
+ 'main': MAIN_MACROS,
+ 'nested': NESTED_MACROS,
+ 'interface': INTERFACE_MACROS,
+ 'operation': OPERATION_MACROS,
+ 'relationship-interface': RELATIONSHIP_INTERFACE_MACROS,
+ 'relationship-operation': RELATIONSHIP_OPERATION_MACROS,
+ 'capability': CAPABILITY_MACROS,
+ 'topology': TOPOLOGY_MACROS
+}
+
+PERMUTATIONS_TYPE_REQUIRED = (
+ ('main', 'node', 'properties'),
+ ('main', 'node', 'attributes'),
+ ('main', 'group', 'properties'),
+ ('main', 'relationship', 'properties'),
+ ('main', 'relationship', 'attributes'),
+ ('main', 'capability', 'properties'),
+ ('main', 'capability', 'attributes'),
+ ('main', 'policy', 'properties'),
+ ('main', 'interface', 'inputs'),
+ ('main', 'artifact', 'properties'),
+ ('main', 'data', 'properties'),
+ ('nested', 'interface', 'inputs'),
+ ('interface', 'node', 'inputs'),
+ ('interface', 'group', 'inputs'),
+ ('interface', 'relationship', 'inputs'),
+ ('operation', 'node', 'inputs'),
+ ('operation', 'group', 'inputs'),
+ ('operation', 'relationship', 'inputs'),
+ ('relationship-interface', 'node', 'inputs'),
+ ('relationship-operation', 'node', 'inputs'),
+ ('capability', 'node', 'properties'),
+ ('capability', 'node', 'attributes'),
+ ('topology', None, 'inputs')
+)
+
+PERMUTATIONS = PERMUTATIONS_TYPE_REQUIRED + (
+ ('topology', None, 'outputs'),
+)
+
+
+# Parameters section
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(3, 1)
+))
+def test_type_parameters_section_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() -%}
+{{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameters_section_syntax_empty(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() -%}
+{}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Parameter
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT,
+ counts=(3, 1)
+))
+def test_type_parameter_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS_TYPE_REQUIRED)
+def test_type_parameter_syntax_empty(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter: {} # type is required
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_syntax_unsupported(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ unsupported: {}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_STRING,
+ counts=(3, 1)
+))
+def test_type_parameter_description_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ description: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_description(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ description: a description
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Default
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_default(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ default: a string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_default_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: integer
+ default: a string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Status
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.STATUSES,
+ counts=(3, 1)
+))
+def test_type_parameter_status(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ status: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_status_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ status: not a status
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_unicode(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('類型') %}
+參數:
+ type: string
+ description: 描述
+ default: 值
+ status: supported
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py
new file mode 100644
index 0000000..49a57f6
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_inheritance.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Unified testing for properties and inputs.
+
+These tests are in addition to the common tests for parameters in test_type_parameters.py.
+
+Compare with test_node_template_node_filter_constraints.py. Note that though the constraints are the
+same, their syntax is very different, making it difficult to test all permutations together.
+"""
+
+import pytest
+
+from .test_type_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+
+
+PERMUTATIONS = tuple(
+ (macros, name, parameter_section)
+ for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+ if name is not None
+)
+
+PERMUTATIONS_NO_RELATIONSHIP = tuple(
+ (macros, name, parameter_section)
+ for macros, name, parameter_section in PERMUTATIONS
+ if macros not in ('relationship-interface', 'relationship-operation')
+)
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_add(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('MyType1') %}
+my_parameter1:
+ type: string
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter2:
+ type: string
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_add_default(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('MyType1') %}
+my_parameter:
+ type: string
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+ type: string
+ default: my value
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_type_override(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType1: {}
+ MyDataType2:
+ derived_from: MyDataType1
+{{- additions(name != 'data') }}
+{%- call parameters('MyType1') %}
+my_parameter:
+ type: MyDataType1
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+ type: MyDataType2
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# We are skipping relationship interfaces, because node requirements can be overridden completely
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS_NO_RELATIONSHIP)
+def test_type_parameter_type_override_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType1: {}
+ MyDataType2: {}
+{{- additions(name != 'data') }}
+{%- call parameters('MyType1') %}
+my_parameter:
+ type: MyDataType1
+{% endcall %}
+{%- call parameters('MyType2', 'MyType1') %}
+my_parameter:
+ type: MyDataType2
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py
new file mode 100644
index 0000000..16fc875
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_type_parameters_properties.py
@@ -0,0 +1,312 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+Unified testing for properties and inputs.
+
+These tests are in addition to the common tests for parameters in test_type_parameters.py.
+
+Compare with test_node_template_node_filter_constraints.py. Note that though the constraints are the
+same, their syntax is very different, making it difficult to test all permutations together.
+"""
+
+import pytest
+
+from .test_type_parameters import (MACROS, PERMUTATIONS as PARAMETER_PERMUTATIONS)
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+PERMUTATIONS = tuple(
+ (macros, name, parameter_section)
+ for macros, name, parameter_section in PARAMETER_PERMUTATIONS
+ if parameter_section != 'attributes'
+)
+
+
+# Required
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_BOOL,
+ counts=(3, 1)
+))
+def test_type_parameter_required_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ required: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_required(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ required: true
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+# Constraints
+
+@pytest.mark.parametrize('macros,name,parameter_section,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_LIST,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_syntax_type(parser, macros, name, parameter_section, value):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints: {{ value }}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_syntax_empty(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints: []
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value(parser, macros, name, parameter_section, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType:
+ properties:
+ my_field:
+ type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+ type: MyDataType
+ constraints:
+ - {{ constraint }}: {my_field: a string}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_LIST,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_list(parser, macros, name, parameter_section,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType:
+ properties:
+ my_field:
+ type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+ type: MyDataType
+ constraints:
+ - {{ constraint }}:
+ - {my_field: string one}
+ - {my_field: string two}
+ - {my_field: string three}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range(parser, macros, name, parameter_section,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType:
+ properties:
+ my_field:
+ type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+ type: MyDataType
+ constraints:
+ - {{ constraint }}:
+ - {my_field: string a}
+ - {my_field: string b}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range_too_many(parser, macros, name,
+ parameter_section, constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType:
+ properties:
+ my_field:
+ type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+ type: MyDataType
+ constraints:
+ - {{ constraint }}:
+ - {my_field: string a}
+ - {my_field: string b}
+ - {my_field: string c}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_RANGE,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_range_bad(macros, parser, name, parameter_section,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyDataType:
+ properties:
+ my_field:
+ type: string
+{{- additions(name != 'data') }}
+{%- call parameters() %}
+my_parameter:
+ type: MyDataType
+ constraints:
+ - {{ constraint }}:
+ - {my_field: string b}
+ - {my_field: string a}
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints:
+ - pattern: ^pattern$
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern_bad(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints:
+ - pattern: (
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_failure()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_integer(parser, macros, name, parameter_section,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints:
+ - {{ constraint }}: 1
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_success()
+
+
+@pytest.mark.parametrize('macros,name,parameter_section,constraint', matrix(
+ PERMUTATIONS,
+ data.CONSTRAINTS_WITH_VALUE_NON_NEGATIVE_INT,
+ counts=(3, 1)
+))
+def test_type_parameter_constraints_with_value_integer_bad(parser, macros, name, parameter_section,
+ constraint):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters() %}
+my_parameter:
+ type: string
+ constraints:
+ - {{ constraint }}: -1
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section, constraint=constraint)).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('macros,name,parameter_section', PERMUTATIONS)
+def test_type_parameter_constraints_pattern_unicode(parser, macros, name, parameter_section):
+ parser.parse_literal(MACROS[macros] + """
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{- additions() }}
+{%- call parameters('類型') %}
+參數:
+ type: string
+ constraints:
+ - pattern: ^模式$
+{% endcall %}
+""", dict(name=name, parameter_section=parameter_section)).assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py
new file mode 100644
index 0000000..a175498
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/test_types.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+from ......mechanisms.utils import matrix
+
+
+CASES_WITHOUT_UNSUPPORTED_FIELDS = ('artifact', 'data', 'capability', 'relationship', 'node',
+ 'group', 'policy')
+
+PERMUTATIONS = CASES_WITHOUT_UNSUPPORTED_FIELDS + ('interface',)
+
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_DICT
+))
+def test_type_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', CASES_WITHOUT_UNSUPPORTED_FIELDS)
+def test_type_syntax_unsupported(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ unsupported: {}
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_syntax_empty(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType: {}
+""", dict(name=name)).assert_success()
+
+
+# Description
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_STRING
+))
+def test_type_description_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ description: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_description(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ description: a description
+""", dict(name=name)).assert_success()
+
+
+# Derived from
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.NOT_A_STRING
+))
+def test_type_derived_from_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ derived_from: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_derived_from(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+""", dict(name=name)).assert_success()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_derived_from_unknown(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ derived_from: UnknownType
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_derived_from_self(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ derived_from: MyType
+""", dict(name=name)).assert_failure()
+
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_derived_from_circular(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType1:
+ derived_from: MyType3
+ MyType2:
+ derived_from: MyType1
+ MyType3:
+ derived_from: MyType2
+""", dict(name=name)).assert_failure()
+
+
+# Version
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.GOOD_VERSIONS
+))
+def test_type_version(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ version: {{ value }}
+""", dict(name=name, value=value)).assert_success()
+
+
+@pytest.mark.parametrize('name,value', matrix(
+ PERMUTATIONS,
+ data.BAD_VERSIONS
+))
+def test_type_version_bad(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ MyType:
+ version: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+# Unicode
+
+@pytest.mark.parametrize('name', PERMUTATIONS)
+def test_type_unicode(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+{{ name }}_types:
+ 類型一: {}
+ 類型二:
+ derived_from: 類型一
+ version: 1.0.0.詠嘆調-10
+ description: 描述
+""", dict(name=name)).assert_success()
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/__init__.py
similarity index 100%
copy from tests/instantiation/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/__init__.py
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py
new file mode 100644
index 0000000..5904a89
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_capabilities.py
@@ -0,0 +1,302 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+
+
+# Capabilities section
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_capabilities_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capabilities_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities: {}
+""").assert_success()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_capability_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities:
+ my_capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ unsupported: {}
+""").assert_failure()
+
+
+def test_node_type_capability_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities:
+ my_capability: {} # "type" is required
+""").assert_failure()
+
+
+# Description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_description_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_description(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ description: a description
+""").assert_success()
+
+
+# Type
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_type_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_type_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: UnknownType
+""").assert_failure()
+
+
+def test_node_type_capability_type_override(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2:
+ derived_from: MyType1
+node_types:
+ MyType1:
+ capabilities:
+ my_capability:
+ type: MyType1
+ MyType2:
+ derived_from: MyType1
+ capabilities:
+ my_capability:
+ type: MyType2
+""").assert_success()
+
+
+def test_node_type_capability_type_override_bad(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ capabilities:
+ my_capability:
+ type: MyType1
+ MyType2:
+ derived_from: MyType1
+ capabilities:
+ my_capability:
+ type: MyType2
+""").assert_failure()
+
+
+# Valid source types
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_type_capability_valid_source_types_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ valid_source_types: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_capability_valid_source_types_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ valid_source_types: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_capability_valid_source_types_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ valid_source_types: []
+""").assert_success()
+
+
+
+def test_node_type_capability_valid_source_types(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType1:
+ capabilities:
+ my_capability:
+ type: MyType
+ valid_source_types: [ MyType1, MyType2 ]
+ MyType2: {}
+""").assert_success()
+
+
+def test_node_type_capability_valid_source_types_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ valid_source_types: [ UnknownType ]
+""").assert_failure()
+
+
+# Occurrences
+
+@pytest.mark.parametrize('value', data.OCCURRENCES)
+def test_node_type_capability_occurrences(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ occurrences: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+@pytest.mark.parametrize('value', data.BAD_OCCURRENCES)
+def test_node_type_capability_occurrences_bad(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ capabilities:
+ my_capability:
+ type: MyType
+ occurrences: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Unicode
+
+def test_node_type_capability_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ 類型: {}
+node_types:
+ 類型:
+ capabilities:
+ 能力:
+ type: 類型
+ properties:
+ 參數:
+ type: string
+ description: 描述
+ default: 值
+ status: supported
+ valid_source_types: [ 類型 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py
new file mode 100644
index 0000000..257fab1
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_relationship_interfaces.py
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+# 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.
+
+"""
+These tests are in addition to those in common/test_type_interface.py.
+"""
+
+
+# Type
+
+def test_node_type_relationship_interface_type_override(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+interface_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ my_interface:
+ type: MyType1
+ MyType2:
+ derived_from: MyType1
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType
+ interfaces:
+ my_interface:
+ type: MyType2 # overriding the requirement has no restrictions
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py
new file mode 100644
index 0000000..22917e4
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/test_node_type_requirements.py
@@ -0,0 +1,361 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from ... import data
+
+
+# Requirements section
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_node_type_requirements_section_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirements_section_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements: []
+""").assert_success()
+
+
+# Requirement
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT)
+def test_node_type_requirement_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ unsupported: {}
+""").assert_failure()
+
+
+def test_node_type_requirement_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: {} # "capability" is required
+""").assert_failure()
+
+
+# Capability
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_capability_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_capability_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability:
+ unsupported: {}
+""").assert_failure()
+
+
+# Capability type
+
+def test_node_type_requirement_capability_type_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_capability_type_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement: MyType
+""").assert_success()
+
+
+def test_node_type_requirement_capability_type_override(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType1
+ MyType2:
+ derived_from: MyType1
+ requirements:
+ - my_requirement:
+ capability: MyType2 # you are allowed to change the capability type to anything
+""").assert_success()
+
+
+# Node
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_node_type_requirement_node_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ node: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_node_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ node: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_node_override(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ node: MyType3
+ MyType2:
+ derived_from: MyType1
+ requirements:
+ - my_requirement:
+ capability: MyType
+ node: MyType4 # you are allowed to change the node type to anything
+ MyType3: {}
+ MyType4: {}
+""").assert_success()
+
+
+# Relationship
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_relationship_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_relationship_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ unsupported: {}
+""").assert_failure()
+
+
+# Relationship type
+
+@pytest.mark.parametrize('value', data.NOT_A_DICT_OR_STRING)
+def test_node_type_requirement_relationship_type_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_node_type_requirement_relationship_type_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: UnknownType
+""").assert_failure()
+
+
+def test_node_type_requirement_relationship_type_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship: MyType
+""").assert_success()
+
+
+def test_node_type_requirement_relationship_type_override(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+relationship_types:
+ MyType1: {}
+ MyType2: {}
+node_types:
+ MyType1:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType1
+ MyType2:
+ derived_from: MyType1
+ requirements:
+ - my_requirement:
+ capability: MyType
+ relationship:
+ type: MyType2 # you are allowed to change the relationship type to anything
+""").assert_success()
+
+
+# Occurrences
+
+@pytest.mark.parametrize('value', data.OCCURRENCES)
+def test_node_type_requirement_occurrences(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ occurrences: {{ value }}
+""", dict(value=value)).assert_success()
+
+
+@pytest.mark.parametrize('value', data.BAD_OCCURRENCES)
+def test_node_type_requirement_occurrences_bad(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType: {}
+node_types:
+ MyType:
+ requirements:
+ - my_requirement:
+ capability: MyType
+ occurrences: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# Unicode
+
+def test_node_type_requirement_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ 類型: {}
+relationship_types:
+ 類型: {}
+node_types:
+ 類型:
+ requirements:
+ - 需求:
+ capability: 類型
+ node: 類型
+ relationship:
+ type: 類型
+ occurrences: [ 0, UNBOUNDED ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py
new file mode 100644
index 0000000..d269a44
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_artifact_type.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# MIME type
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_artifact_type_mime_type_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType:
+ mime_type: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+# File extension
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_artifact_type_file_ext_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType:
+ file_ext: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_artifact_type_file_ext_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType:
+ file_ext: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_artifact_type_file_ext_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ MyType:
+ file_ext: []
+""").assert_success()
+
+
+# Unicode
+
+
+def test_artifact_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+artifact_types:
+ 類型:
+ file_ext: [ 延期一, 延期二 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_type.py
new file mode 100644
index 0000000..217f163
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_capability_type.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Valid source types
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_capability_type_valid_source_types_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType:
+ valid_source_types: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_capability_type_valid_source_types_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType:
+ valid_source_types: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_capability_type_valid_source_types_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType:
+ valid_source_types: []
+""").assert_success()
+
+
+def test_capability_type_valid_source_types(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+capability_types:
+ MyType:
+ valid_source_types: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_capability_type_valid_source_types_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType:
+ valid_source_types: [ UnknownType ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_capability_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型一: {}
+ 類型二: {}
+capability_types:
+ 類型:
+ valid_source_types: [ 類型一, 類型二 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py
new file mode 100644
index 0000000..5c0dd70
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_data_type.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+from .....mechanisms.utils import matrix
+
+
+# Derived from primitive
+
+@pytest.mark.parametrize('name', data.PRIMITIVE_TYPE_NAMES)
+def test_data_type_derived_from_primitive(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyType:
+ derived_from: {{ name }}
+""", dict(name=name)).assert_success()
+
+
+# Constraints
+
+@pytest.mark.parametrize('name,value', matrix(
+ data.PRIMITIVE_TYPE_NAMES,
+ data.NOT_A_LIST
+))
+def test_data_type_constraints_syntax_type(parser, name, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyType:
+ derived_from: string
+ constraints: {{ value }}
+""", dict(name=name, value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('name', data.PRIMITIVE_TYPE_NAMES)
+def test_data_type_constraints_syntax_empty(parser, name):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyType:
+ derived_from: string
+ constraints: []
+""", dict(name=name)).assert_success()
+
+
+def test_data_type_constraints_not_derived_from_primitive(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+data_types:
+ MyType:
+ constraints: [] # can't have constraints if not derived from primitive
+""").assert_failure()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py
new file mode 100644
index 0000000..7816484
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_group_type.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Members
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_group_type_members_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType:
+ members: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_group_type_members_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType:
+ members: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_group_type_members_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType:
+ members: []
+""").assert_success()
+
+
+def test_group_type_members(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+group_types:
+ MyType:
+ members: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_group_type_members_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType:
+ members: [ UnknownType ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_group_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型一: {}
+ 類型二: {}
+group_types:
+ 類型:
+ members: [ 類型一, 類型二 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py
new file mode 100644
index 0000000..c9bb780
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_interface_type.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Operation
+
+def test_interface_type_operation_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation: {}
+""").assert_success()
+
+
+# Operation description
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_interface_type_operation_description_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ description: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_interface_type_operation_description(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ description: a description
+""").assert_success()
+
+
+# Operation implementation
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_interface_type_operation_implementation_primary_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation:
+ primary: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_interface_type_operation_implementation_primary(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation:
+ primary: an implementation
+""").assert_success()
+
+
+def test_interface_type_operation_implementation_primary_short_form(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation: an implementation
+""").assert_success()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_interface_type_operation_dependencies_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_interface_type_operation_dependencies_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies:
+ - {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+def test_interface_type_operation_dependencies_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ MyType:
+ my_operation:
+ implementation:
+ primary: an implementation
+ dependencies: []
+""").assert_success()
+
+
+# Unicode
+
+def test_interface_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+interface_types:
+ 類型:
+ inputs:
+ 輸入:
+ type: string
+ 手術:
+ description: 描述
+ implementation:
+ primary: 履行
+ dependencies:
+ - 依賴
+ inputs:
+ 輸入:
+ type: string
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py
new file mode 100644
index 0000000..0f87741
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_policy_type.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Targets
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_policy_type_targets_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType:
+ targets: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_policy_type_targets_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType:
+ targets: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_policy_type_targets_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType:
+ targets: []
+""").assert_success()
+
+
+def test_policy_type_targets_nodes(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_policy_type_targets_groups(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+group_types:
+ MyType1: {}
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_policy_type_targets_nodes_and_groups(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType1: {}
+group_types:
+ MyType2: {}
+policy_types:
+ MyType:
+ targets: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_policy_type_targets_ambiguous(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ MyType: {}
+group_types:
+ MyType: {}
+policy_types:
+ MyType:
+ targets: [ MyType ]
+""").assert_success()
+
+
+def test_policy_type_targets_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+policy_types:
+ MyType:
+ targets: [ UnknownType ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_policy_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+node_types:
+ 類型一: {}
+ 類型二: {}
+policy_types:
+ 類型:
+ targets: [ 類型一, 類型二 ]
+""").assert_success()
diff --git a/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py
new file mode 100644
index 0000000..495e325
--- /dev/null
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/types/test_relationship_type.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+
+from .. import data
+
+
+# Valid target types
+
+@pytest.mark.parametrize('value', data.NOT_A_LIST)
+def test_relationship_type_valid_target_types_syntax_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+relationship_types:
+ MyType:
+ valid_target_types: {{ value }}
+""", dict(value=value)).assert_failure()
+
+
+@pytest.mark.parametrize('value', data.NOT_A_STRING)
+def test_relationship_type_valid_target_types_syntax_element_type(parser, value):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+relationship_types:
+ MyType:
+ valid_target_types: [ {{ value }} ]
+""", dict(value=value)).assert_failure()
+
+
+def test_relationship_type_valid_target_types_syntax_empty(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+relationship_types:
+ MyType:
+ valid_target_types: []
+""").assert_success()
+
+
+def test_relationship_type_valid_target_types(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ MyType1: {}
+ MyType2: {}
+relationship_types:
+ MyType:
+ valid_target_types: [ MyType1, MyType2 ]
+""").assert_success()
+
+
+def test_relationship_type_valid_target_types_unknown(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+relationship_types:
+ MyType:
+ valid_target_types: [ UnknownType ]
+""").assert_failure()
+
+
+# Unicode
+
+def test_relationship_type_unicode(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+capability_types:
+ 類型一: {}
+ 類型二: {}
+relationship_types:
+ 類型:
+ valid_target_types: [ 類型一, 類型二 ]
+""").assert_success()
diff --git a/tests/parser/__init__.py b/tests/mechanisms/__init__.py
similarity index 100%
copy from tests/parser/__init__.py
copy to tests/mechanisms/__init__.py
diff --git a/tests/mechanisms/parsing/__init__.py b/tests/mechanisms/parsing/__init__.py
new file mode 100644
index 0000000..a50abc4
--- /dev/null
+++ b/tests/mechanisms/parsing/__init__.py
@@ -0,0 +1,81 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pytest
+import jinja2
+
+
+LINE_BREAK = '\n' + '-' * 60
+
+
+class Parsed(object):
+ def __init__(self):
+ self.issues = []
+ self.text = ''
+ self.verbose = False
+
+ def assert_success(self):
+ # See: https://docs.pytest.org/en/latest/example/simple.html
+ # #writing-well-integrated-assertion-helpers
+ __tracebackhide__ = True # pylint: disable=unused-variable
+
+ if len(self.issues) > 0:
+ pytest.fail(u'did not expect parsing errors\n\n{0}\n\n{1}'
+ .format(self.text.strip(), u'\n'.join(self.issues)))
+ else:
+ if self.verbose:
+ print LINE_BREAK
+ print self.text.strip()
+
+ def assert_failure(self):
+ # See: https://docs.pytest.org/en/latest/example/simple.html
+ # #writing-well-integrated-assertion-helpers
+ __tracebackhide__ = True # pylint: disable=unused-variable
+
+ if len(self.issues) > 0:
+ if self.verbose:
+ print LINE_BREAK
+ print u'{0}\n\n{1}'.format(self.text.strip(), u'\n'.join(self.issues))
+ else:
+ pytest.fail(u'expected parsing errors but got none\n\n{0}'
+ .format(self.text.strip()))
+
+
+class Parser(object):
+ def __init__(self):
+ self.verbose = False
+
+ def parse_literal(self, text, context=None, **kwargs):
+ text = render(text, context)
+ parsed = self._parse_literal(text, **kwargs)
+ parsed.verbose = self.verbose
+ return parsed
+
+ def _parse_literal(self, text, **kwargs):
+ raise NotImplementedError
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ pass
+
+
+def render(template, context=None):
+ if not isinstance(template, unicode):
+ template = template.decode('utf-8')
+ template = jinja2.Template(template)
+ template = template.render(context or {})
+ return template
diff --git a/tests/mechanisms/parsing/aria.py b/tests/mechanisms/parsing/aria.py
new file mode 100644
index 0000000..67adcc9
--- /dev/null
+++ b/tests/mechanisms/parsing/aria.py
@@ -0,0 +1,78 @@
+# 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 root 'aria'
+
+from aria.parser.loading import LiteralLocation
+from aria.parser.consumption import (
+ ConsumptionContext,
+ ConsumerChain,
+ Read,
+ Validate,
+ ServiceTemplate
+)
+from aria.utils.imports import import_fullname
+
+from . import (Parser, Parsed)
+
+
+class AriaParser(Parser):
+ def _parse_literal(self, text, **kwargs):
+ context = AriaParser.create_context(import_profile=kwargs.get('import_profile', False),
+ adhoc_inputs=kwargs.get('adhoc_inputs', True),
+ validate_normative=kwargs.get('validate_normative',
+ False))
+ context.presentation.location = LiteralLocation(text)
+ consumer = AriaParser.create_consumer(context)
+ consumer.consume()
+ parsed = Parsed()
+ parsed.text = text
+ for issue in context.validation.issues:
+ parsed.issues.append(unicode(issue))
+ return parsed
+
+ @staticmethod
+ def create_context(loader_source='aria.parser.loading.DefaultLoaderSource',
+ reader_source='aria.parser.reading.DefaultReaderSource',
+ presenter_source='aria.parser.presentation.DefaultPresenterSource',
+ presenter=None,
+ debug=False,
+ cache=True,
+ import_profile=None,
+ adhoc_inputs=None,
+ validate_normative=None):
+ context = ConsumptionContext()
+ context.loading.loader_source = import_fullname(loader_source)()
+ context.reading.reader_source = import_fullname(reader_source)()
+ context.presentation.presenter_source = import_fullname(presenter_source)()
+ context.presentation.presenter_class = import_fullname(presenter)
+ context.presentation.threads = 1 # tests already run in maximum thread density
+ context.presentation.cache = cache
+ if import_profile is not None:
+ context.presentation.configuration['tosca.import_profile'] = import_profile
+ if adhoc_inputs is not None:
+ context.presentation.configuration['tosca.adhoc_inputs'] = adhoc_inputs
+ if validate_normative is not None:
+ context.presentation.configuration['validate_normative'] = validate_normative
+ context.presentation.print_exceptions = debug
+ return context
+
+ @staticmethod
+ def create_consumer(context):
+ return ConsumerChain(context, (
+ Read,
+ Validate,
+ ServiceTemplate
+ ))
diff --git a/tests/mechanisms/utils.py b/tests/mechanisms/utils.py
new file mode 100644
index 0000000..45a442c
--- /dev/null
+++ b/tests/mechanisms/utils.py
@@ -0,0 +1,80 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import itertools
+
+
+def matrix(*iterables, **kwargs):
+ """
+ Generates a matrix of parameters for ``@pytest.mark.parametrize``.
+
+ The matrix is essentially the Cartesian product of the arguments (which should be iterables),
+ with the added ability to "flatten" each value by breaking up tuples and recombining them into a
+ final flat value.
+
+ To do such recombination, use the ``counts`` argument (tuple of integers) to specify the number
+ of elements per value in each iterable in order. Any count greater than 1 (the default) enables
+ recombination of the iterable's values. So, if you are combining three different iterables, then
+ you want ``counts`` to be a tuple of three integers. The first integer in the ``counts`` tuple
+ will be the number of elements in the values of the first iterable, etc.
+
+ Detailed example::
+
+ x = ('hello', 'goodbye')
+ y = ('Linus', 'Richard')
+ matrix(x, y) ->
+ ('hello', 'Linus'),
+ ('hello', 'Richard'),
+ ('goodbye', 'Linus'),
+ ('goodbye', 'Richard')
+
+ y = (('Linus', 'Torvalds'), ('Richard', 'Stallman'))
+
+ # Without flattening:
+
+ matrix(x, y) ->
+ ('hello', ('Linus', 'Torvalds')),
+ ('hello', ('Richard', 'Stallman')),
+ ('goodbye', ('Linus', 'Torvalds')),
+ ('goodbye', ('Richard', 'Stallman'))
+
+ # The values in our second iterable, y, have two elements that we want to flatten, so we will
+ # set the second "count" value to 2:
+
+ matrix(x, y, counts=(1, 2)) ->
+ ('hello', 'Linus', 'Torvalds'),
+ ('hello', 'Richard', 'Stallman'),
+ ('goodbye', 'Linus', 'Torvalds'),
+ ('goodbye', 'Richard', 'Stallman')
+ """
+ counts = kwargs.get('counts')
+ for product in itertools.product(*iterables):
+ if counts:
+ elements = []
+ for value_index, value in enumerate(product):
+ try:
+ count = counts[value_index]
+ except IndexError:
+ count = 1
+ if count == 1:
+ # As is
+ elements.append(value)
+ else:
+ # Recombine
+ for element_index in range(count):
+ elements.append(value[element_index])
+ yield tuple(elements)
+ else:
+ yield product
diff --git a/tests/mechanisms/web_server.py b/tests/mechanisms/web_server.py
new file mode 100644
index 0000000..8711cb4
--- /dev/null
+++ b/tests/mechanisms/web_server.py
@@ -0,0 +1,84 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+import threading
+
+import tornado.web
+import tornado.ioloop
+import tornado.netutil
+import tornado.httpserver
+
+
+logging.getLogger('tornado.access').disabled = True
+
+
+class WebServer(threading.Thread):
+ def __init__(self):
+ super(WebServer, self).__init__()
+ self.daemon = True
+
+ self.content = []
+
+ # Arbitrary free socket
+ self.sockets = tornado.netutil.bind_sockets(0, '')
+ for s in self.sockets:
+ name = s.getsockname()
+ if name[0] == '0.0.0.0': # IPv4 (IPv6 would be '::')
+ self.port = name[1]
+ break
+
+ @property
+ def root(self):
+ return 'http://localhost:{0}'.format(self.port)
+
+ def add_text(self, url, content, content_type='text/plain'):
+ self.content.append((url, TextHandler, dict(content=content, content_type=content_type)))
+
+ def add_text_yaml(self, url, content):
+ self.add_text(url, content, 'application/x-yaml')
+
+ def stop(self):
+ self.ioloop.add_callback(self.ioloop.stop)
+
+ def run(self): # Thread override
+ application = tornado.web.Application(self.content)
+ server = tornado.httpserver.HTTPServer(application)
+ server.add_sockets(self.sockets)
+ self.ioloop = tornado.ioloop.IOLoop.current()
+ print 'Tornado starting'
+ self.ioloop.start()
+ print 'Tornado stopped'
+
+ @staticmethod
+ def escape(segment):
+ return tornado.escape.url_escape(segment)
+
+ def __enter__(self):
+ self.start()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.stop()
+
+
+class TextHandler(tornado.web.RequestHandler):
+ def initialize(self, content, content_type): # pylint: disable=arguments-differ
+ self.content = content
+ self.content_type = content_type
+
+ def get(self):
+ self.write(self.content)
+ self.set_header('Content-Type', self.content_type)
diff --git a/tests/mock/workflow.py b/tests/mock/workflow.py
index b12b9fa..74f63c2 100644
--- a/tests/mock/workflow.py
+++ b/tests/mock/workflow.py
@@ -19,7 +19,7 @@
@workflow
-def mock_workflow(graph, ctx, output_path=None, **kwargs): # pylint: disable=unused-argument
+def mock_workflow(graph, ctx, output_path=None, **kwargs): # pylint: disable=unused-argument
if output_path:
# writes call arguments to the specified output file
with open(output_path, 'w') as f:
diff --git a/tests/modeling/__init__.py b/tests/modeling/__init__.py
index 072ef54..424252c 100644
--- a/tests/modeling/__init__.py
+++ b/tests/modeling/__init__.py
@@ -26,7 +26,7 @@
)
-class MockModel(models.aria_declarative_base, mixins.ModelMixin): #pylint: disable=abstract-method
+class MockModel(models.aria_declarative_base, mixins.ModelMixin): # pylint: disable=abstract-method
__tablename__ = 'mock_model'
model_dict = Column(modeling_types.Dict)
model_list = Column(modeling_types.List)
diff --git a/tests/orchestrator/context/test_operation.py b/tests/orchestrator/context/test_operation.py
index 111e121..ba33dc2 100644
--- a/tests/orchestrator/context/test_operation.py
+++ b/tests/orchestrator/context/test_operation.py
@@ -106,7 +106,7 @@
)
operations = interface.operations
assert len(operations) == 1
- assert dataholder['function'] == operations.values()[0].function # pylint: disable=no-member
+ assert dataholder['function'] == operations.values()[0].function # pylint: disable=no-member
assert dataholder['arguments']['putput'] is True
# Context based attributes (sugaring)
@@ -150,7 +150,7 @@
assert dataholder['actor_name'] == relationship.name
assert interface_name in dataholder['task_name']
operations = interface.operations
- assert dataholder['function'] == operations.values()[0].function # pylint: disable=no-member
+ assert dataholder['function'] == operations.values()[0].function # pylint: disable=no-member
assert dataholder['arguments']['putput'] is True
# Context based attributes (sugaring)
diff --git a/tests/orchestrator/context/test_serialize.py b/tests/orchestrator/context/test_serialize.py
index 6e9c950..099515c 100644
--- a/tests/orchestrator/context/test_serialize.py
+++ b/tests/orchestrator/context/test_serialize.py
@@ -23,6 +23,7 @@
from tests import mock
from tests import storage
+
TEST_FILE_CONTENT = 'CONTENT'
TEST_FILE_ENTRY_ID = 'entry'
TEST_FILE_NAME = 'test_file'
@@ -47,7 +48,7 @@
node.interfaces[interface.name] = interface
context.model.node.update(node)
- graph = _mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
+ graph = _mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
eng = engine.Engine(executor)
eng.execute(context)
diff --git a/tests/orchestrator/context/test_workflow.py b/tests/orchestrator/context/test_workflow.py
index 6d53c2a..9869354 100644
--- a/tests/orchestrator/context/test_workflow.py
+++ b/tests/orchestrator/context/test_workflow.py
@@ -31,7 +31,7 @@
def test_execution_creation_on_workflow_context_creation(self, storage):
ctx = self._create_ctx(storage)
- execution = storage.execution.get(ctx.execution.id) # pylint: disable=no-member
+ execution = storage.execution.get(ctx.execution.id) # pylint: disable=no-member
assert execution.service == storage.service.get_by_name(
mock.models.SERVICE_NAME)
assert execution.workflow_name == mock.models.WORKFLOW_NAME
diff --git a/tests/orchestrator/execution/test_execution_compiler.py b/tests/orchestrator/execution/test_execution_compiler.py
index 42e5272..3d96852 100644
--- a/tests/orchestrator/execution/test_execution_compiler.py
+++ b/tests/orchestrator/execution/test_execution_compiler.py
@@ -38,13 +38,14 @@
storage
)
-from ...fixtures import ( # pylint: disable=unused-import
+from ...fixtures import ( # pylint: disable=unused-import
plugins_dir,
plugin_manager,
fs_model as model,
resource_storage as resource
)
+
custom_events = {
'is_resumed': Event(),
'is_active': Event(),
diff --git a/tests/orchestrator/execution_plugin/test_local.py b/tests/orchestrator/execution_plugin/test_local.py
index 5af6a2f..539730a 100644
--- a/tests/orchestrator/execution_plugin/test_local.py
+++ b/tests/orchestrator/execution_plugin/test_local.py
@@ -499,7 +499,7 @@
operation_name='op',
arguments=arguments))
return graph
- tasks_graph = mock_workflow(ctx=workflow_context) # pylint: disable=no-value-for-parameter
+ tasks_graph = mock_workflow(ctx=workflow_context) # pylint: disable=no-value-for-parameter
graph_compiler.GraphCompiler(workflow_context, executor.__class__).compile(tasks_graph)
eng = engine.Engine(executor)
eng.execute(workflow_context)
diff --git a/tests/orchestrator/execution_plugin/test_ssh.py b/tests/orchestrator/execution_plugin/test_ssh.py
index e39f3ba..f619bf7 100644
--- a/tests/orchestrator/execution_plugin/test_ssh.py
+++ b/tests/orchestrator/execution_plugin/test_ssh.py
@@ -260,7 +260,7 @@
graph.sequence(*ops)
return graph
- tasks_graph = mock_workflow(ctx=self._workflow_context) # pylint: disable=no-value-for-parameter
+ tasks_graph = mock_workflow(ctx=self._workflow_context) # pylint: disable=no-value-for-parameter
graph_compiler.GraphCompiler(
self._workflow_context, self._executor.__class__).compile(tasks_graph)
eng = engine.Engine(self._executor)
diff --git a/tests/orchestrator/workflows/executor/test_process_executor.py b/tests/orchestrator/workflows/executor/test_process_executor.py
index e050d18..d801ffb 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor.py
@@ -34,7 +34,7 @@
import tests.storage
import tests.resources
from tests.helpers import FilesystemDataHolder
-from tests.fixtures import ( # pylint: disable=unused-import
+from tests.fixtures import ( # pylint: disable=unused-import
plugins_dir,
plugin_manager,
)
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_extension.py b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
index 0093976..7ab1ea5 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_extension.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_extension.py
@@ -56,7 +56,7 @@
arguments=arguments)
graph.add_tasks(task)
return graph
- graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
+ graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
eng = engine.Engine(executor)
eng.execute(context)
diff --git a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
index 8aaf4ef..b469e71 100644
--- a/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
+++ b/tests/orchestrator/workflows/executor/test_process_executor_tracked_changes.py
@@ -72,9 +72,9 @@
context=context, executor=executor, op_func=_mock_updating_operation, arguments=arguments)
expected_after_update = expected_initial.copy()
- expected_after_update.update(arguments['committed']) # pylint: disable=no-member
+ expected_after_update.update(arguments['committed']) # pylint: disable=no-member
expected_after_change = expected_after_update.copy()
- expected_after_change.update(arguments['changed_but_refreshed']) # pylint: disable=no-member
+ expected_after_change.update(arguments['changed_but_refreshed']) # pylint: disable=no-member
assert out['initial'] == expected_initial
assert out['after_update'] == expected_after_update
@@ -106,7 +106,7 @@
arguments=wf_arguments)
graph.add_tasks(task)
return graph
- graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
+ graph = mock_workflow(ctx=context) # pylint: disable=no-value-for-parameter
graph_compiler.GraphCompiler(context, executor.__class__).compile(graph)
eng = engine.Engine(executor)
eng.execute(context)
diff --git a/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py b/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/tests/parser/test_tosca_simple_v1_0/presentation/__init__.py
+++ /dev/null
diff --git a/tests/requirements.txt b/tests/requirements.txt
index f2c3a45..078bce3 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -12,10 +12,11 @@
fasteners==0.14.1
mock==2.0.0
-pylint==1.6.5
+pylint==1.6.5 # see ARIA-314 about upgrading to 1.7
pytest==3.2.3
pytest-cov==2.5.1
pytest-mock==1.6.3
pytest-xdist==1.20.1
sh==1.12.14
testtools==2.3.0
+tornado==4.5.2
diff --git a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
index 260f0bf..0a0098a 100644
--- a/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
+++ b/tests/resources/service-templates/tosca-simple-1.0/node-cellar/node-cellar.yaml
@@ -19,7 +19,7 @@
description: >-
Node Cellar TOSCA blueprint.
- Here is some Unicode: 中國.
+ Here is some Unicode: 詠嘆調.
metadata:
template_name: node-cellar
@@ -215,6 +215,10 @@
os_users: # map of os.UserInfo
root:
password: admin123
+ capabilities:
+ scalable:
+ properties:
+ max_instances: 2
interfaces:
Standard:
inputs:
@@ -277,7 +281,7 @@
node_cellar_group:
type: openstack.Secured
members:
- - loadbalancer
+ - loadbalancer_host
- application_host
- data_host
interfaces:
diff --git a/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml b/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml
deleted file mode 100644
index bb5a84e..0000000
--- a/tests/resources/service-templates/tosca-simple-1.0/types/shorthand-1/shorthand-1.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-tosca_definitions_version: tosca_simple_yaml_1_0
-
-description: >-
- TOSCA simple profile that defines a compute instance and a block storage with the "shorthand type"
-
-topology_template:
-
- node_templates:
-
- my_server:
- type: Compute
- requirements:
- - local_storage:
- node: my_block_storage
- relationship:
- type: AttachesTo
- properties:
- location: /path1/path2
-
- my_block_storage:
- type: BlockStorage
- properties:
- size: 10 GB
diff --git a/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml b/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml
deleted file mode 100644
index b54604f..0000000
--- a/tests/resources/service-templates/tosca-simple-1.0/types/typequalified-1/typequalified-1.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-tosca_definitions_version: tosca_simple_yaml_1_0
-
-description: >-
- TOSCA simple profile that defines a compute instance and a block storage with the "typequalified type"
-
-topology_template:
-
- node_templates:
-
- my_server:
- type: tosca:Compute
- requirements:
- - local_storage:
- node: my_block_storage
- relationship:
- type: AttachesTo
- properties:
- location: /path1/path2
-
- my_block_storage:
- type: tosca:BlockStorage
- properties:
- size: 10 GB
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 518d624..bc5434a 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -49,7 +49,7 @@
@pytest.fixture(scope='module', autouse=True)
def module_cleanup():
- modeling.models.aria_declarative_base.metadata.remove(tests_modeling.MockModel.__table__) #pylint: disable=no-member
+ modeling.models.aria_declarative_base.metadata.remove(tests_modeling.MockModel.__table__) # pylint: disable=no-member
def test_storage_base(storage):
@@ -162,7 +162,7 @@
assert_include(service2)
-class MockModel(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin): #pylint: disable=abstract-method
+class MockModel(modeling.models.aria_declarative_base, modeling.mixins.ModelMixin): # pylint: disable=abstract-method
__tablename__ = 'op_mock_model'
name = Column(Text)
diff --git a/tests/test_extension.py b/tests/test_extension.py
index f0378fd..247bbb6 100644
--- a/tests/test_extension.py
+++ b/tests/test_extension.py
@@ -13,12 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# pylint: disable=no-member,no-method-argument,unused-variable
+
import pytest
from aria import extension
-# #pylint: disable=no-member,no-method-argument,unused-variable
-
class TestRegistrar(object):
diff --git a/tests/parser/__init__.py b/tests/topology/__init__.py
similarity index 100%
rename from tests/parser/__init__.py
rename to tests/topology/__init__.py
diff --git a/tests/parser/service_templates.py b/tests/topology/service_templates.py
similarity index 80%
rename from tests/parser/service_templates.py
rename to tests/topology/service_templates.py
index 9e8fcae..60d5ad0 100644
--- a/tests/parser/service_templates.py
+++ b/tests/topology/service_templates.py
@@ -48,28 +48,12 @@
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,
- '{0}.yaml'.format(use_case_name))
- context = create_context(uri)
- inputs_file = get_example_uri('tosca-simple-1.0', 'types', use_case_name, 'inputs.yaml')
- if os.path.isfile(inputs_file):
- context.args.append('--inputs={0}'.format(inputs_file))
- consumer, dumper = create_consumer(context, consumer_class_name)
- consumer.consume()
- context.validation.dump_issues()
- 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
-
)
diff --git a/tests/instantiation/test_configuration.py b/tests/topology/test_configuration.py
similarity index 98%
rename from tests/instantiation/test_configuration.py
rename to tests/topology/test_configuration.py
index 6ac0c9c..2a3bcae 100644
--- a/tests/instantiation/test_configuration.py
+++ b/tests/topology/test_configuration.py
@@ -15,9 +15,10 @@
import pytest
-from tests.parser.service_templates import consume_literal
from aria.modeling.utils import parameters_as_values
+from .service_templates import consume_literal
+
TEMPLATE = """
tosca_definitions_version: tosca_simple_yaml_1_0
diff --git a/tests/parser/test_tosca_simple_v1_0/test_end2end.py b/tests/topology/test_end2end.py
similarity index 97%
rename from tests/parser/test_tosca_simple_v1_0/test_end2end.py
rename to tests/topology/test_end2end.py
index 474d90e..a583db5 100644
--- a/tests/parser/test_tosca_simple_v1_0/test_end2end.py
+++ b/tests/topology/test_end2end.py
@@ -13,7 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from ..service_templates import (consume_use_case, consume_node_cellar)
+from .service_templates import (consume_use_case, consume_node_cellar)
# Use Cases
diff --git a/tests/parser/test_reqs_caps.py b/tests/topology/test_reqs_caps.py
similarity index 100%
rename from tests/parser/test_reqs_caps.py
rename to tests/topology/test_reqs_caps.py
diff --git a/tests/parser/utils.py b/tests/topology/utils.py
similarity index 93%
rename from tests/parser/utils.py
rename to tests/topology/utils.py
index f0e890f..47cc45e 100644
--- a/tests/parser/utils.py
+++ b/tests/topology/utils.py
@@ -39,6 +39,8 @@
context.presentation.location = UriLocation(uri) if isinstance(uri, basestring) else uri
context.presentation.presenter_source = import_fullname(presenter_source)()
context.presentation.presenter_class = import_fullname(presenter)
+ context.presentation.threads = 1 # tests already run in maximum thread density
+ context.presentation.validate_normative = False # we have special tests for normative types
context.presentation.print_exceptions = debug
return context
diff --git a/tests/utils/test_plugin.py b/tests/utils/test_plugin.py
index c91d0c9..452983b 100644
--- a/tests/utils/test_plugin.py
+++ b/tests/utils/test_plugin.py
@@ -20,7 +20,7 @@
from aria.orchestrator import exceptions
from aria.utils.plugin import create as create_plugin
-from ..fixtures import ( # pylint: disable=unused-import
+from ..fixtures import ( # pylint: disable=unused-import
plugins_dir,
plugin_manager,
inmemory_model as model
diff --git a/tests/utils/test_versions.py b/tests/utils/test_versions.py
index 222949c..1c05144 100644
--- a/tests/utils/test_versions.py
+++ b/tests/utils/test_versions.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# 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.
@@ -34,8 +35,11 @@
assert VersionString('20.0.1-beta1') < VersionString('20.0.1')
assert VersionString('20.0.1-beta2') < VersionString('20.0.1-rc2')
assert VersionString('20.0.1-alpha2') < VersionString('20.0.1-beta1')
- assert VersionString('20.0.1-dev2') < VersionString('20.0.1-alpha1')
- assert VersionString('20.0.1-DEV2') < VersionString('20.0.1-ALPHA1')
+ assert VersionString('20.0.1-dev2') < VersionString('20.0.1-ALPHA1')
+ assert VersionString('20.0.1-DEV2') < VersionString('20.0.1-alpha1')
+
+ # With Unicode qualifier
+ assert VersionString(u'20.0.1-詠嘆調1') == VersionString(u'20.0.1-詠嘆調2')
# Coercive comparisons
assert VersionString('20.0.0') == VersionString(10 * 2)
@@ -49,7 +53,7 @@
assert VersionString() == VersionString()
assert VersionString() == VersionString.NULL
assert VersionString(None) == VersionString.NULL
- assert VersionString.NULL == None # pylint: disable=singleton-comparison
+ assert VersionString.NULL == None # pylint: disable=singleton-comparison
assert VersionString.NULL == 0
# Invalid version strings
diff --git a/tox.ini b/tox.ini
index b48119d..d642f17 100644
--- a/tox.ini
+++ b/tox.ini
@@ -11,8 +11,9 @@
# limitations under the License.
[tox]
-envlist=core,e2e,windows,ssh,pylint_core,pylint_tests,docs
-processes={env:PYTEST_PROCESSES:auto}
+envlist=core,extensions,e2e,windows,ssh,pylint_core,pylint_tests,docs
+pytest_processes={env:CONCURRENCY:auto}
+pylint_jobs={env:CONCURRENCY:0}
[testenv]
whitelist_externals=
@@ -29,6 +30,7 @@
tests/requirements.txt
basepython=
core: python2.7
+ extensions: python2.7
e2e: python2.7
ssh: python2.7
windows: {env:PYTHON:}\python.exe
@@ -39,25 +41,24 @@
[testenv:core]
commands=
pytest tests \
- --numprocesses={[tox]processes} \
+ --numprocesses={[tox]pytest_processes} \
--ignore=tests/end2end \
+ --ignore=tests/extensions \
--ignore=tests/orchestrator/execution_plugin/test_ssh.py \
--cov-report term-missing \
--cov aria
+[testenv:extensions]
+commands=
+ pytest tests/extensions \
+ --numprocesses={[tox]pytest_processes} \
+ --cov-report term-missing \
+ --cov extensions
+
[testenv:e2e]
commands=
pytest tests/end2end \
- --numprocesses={[tox]processes} \
- --cov-report term-missing \
- --cov aria
-
-[testenv:windows]
-commands=
- pytest tests \
- --numprocesses={[tox]processes} \
- --ignore=tests/end2end \
- --ignore=tests/orchestrator/execution_plugin/test_ssh.py \
+ --numprocesses={[tox]pytest_processes} \
--cov-report term-missing \
--cov aria
@@ -66,19 +67,33 @@
pip install {opts} {packages} .[ssh]
commands=
pytest tests/orchestrator/execution_plugin/test_ssh.py \
- --numprocesses={[tox]processes}
+ --numprocesses={[tox]pytest_processes}
+
+[testenv:windows]
+commands=
+ pytest tests \
+ --numprocesses={[tox]pytest_processes} \
+ --ignore=tests/end2end \
+ --ignore=tests/extensions \
+ --ignore=tests/orchestrator/execution_plugin/test_ssh.py \
+ --cov-report term-missing \
+ --cov aria
[testenv:pylint_core]
commands=
- pylint aria extensions/aria_extension_tosca/ \
+ pylint aria extensions/aria_extension_tosca \
--rcfile=aria/.pylintrc \
--disable=fixme,missing-docstring
+# Disabling due to bugs: https://github.com/PyCQA/pylint/issues/374
+# --jobs={[tox]pylint_jobs}
[testenv:pylint_tests]
commands=
pylint tests \
--rcfile=tests/.pylintrc \
--disable=fixme,missing-docstring
+# Disabling due to bugs: https://github.com/PyCQA/pylint/issues/374
+# --jobs={[tox]pylint_jobs}
[testenv:docs]
install_command=