You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ariatosca.apache.org by em...@apache.org on 2017/12/04 08:33:09 UTC
[incubator-ariatosca] branch ARIA-1-parser-test-suite updated
(9878b76 -> adc53aa)
This is an automated email from the ASF dual-hosted git repository.
emblemparade pushed a change to branch ARIA-1-parser-test-suite
in repository https://gitbox.apache.org/repos/asf/incubator-ariatosca.git.
omit 9878b76 Remove setuptools from requirements
omit 71cf541 More fixes
omit 1dfb81c Fixes
omit 89b9f13 ARIA-1 Parser test suite
new adc53aa ARIA-1 Parser test suite
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (9878b76)
\
N -- N -- N refs/heads/ARIA-1-parser-test-suite (adc53aa)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
--
To stop receiving notification emails like this one, please contact
['"commits@ariatosca.apache.org" <co...@ariatosca.apache.org>'].
[incubator-ariatosca] 01/01: ARIA-1 Parser test suite
Posted by em...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
emblemparade pushed a commit to branch ARIA-1-parser-test-suite
in repository https://gitbox.apache.org/repos/asf/incubator-ariatosca.git
commit adc53aa02fd146a03cd7f857bdb5d111c53fc4d6
Author: Tal Liron <ta...@gmail.com>
AuthorDate: Thu Aug 17 17:50:27 2017 -0500
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.
---
.travis.yml | 22 +-
Makefile | 3 +
README.rst | 53 +-
appveyor.yml | 1 +
aria/__init__.py | 21 +-
aria/cli/commands/services.py | 2 +-
aria/cli/utils.py | 4 +-
aria/modeling/functions.py | 2 +-
aria/modeling/mixins.py | 4 +-
aria/modeling/orchestration.py | 2 +-
aria/modeling/service_common.py | 8 +-
aria/modeling/service_instance.py | 4 +-
aria/modeling/service_template.py | 4 +-
aria/modeling/utils.py | 2 +-
aria/orchestrator/context/common.py | 6 +-
aria/orchestrator/context/operation.py | 12 +-
aria/orchestrator/context/workflow.py | 4 +-
aria/orchestrator/decorators.py | 2 +-
aria/orchestrator/execution_plugin/common.py | 12 +-
.../execution_plugin/ctx_proxy/client.py | 4 +-
.../execution_plugin/ctx_proxy/server.py | 10 +-
.../orchestrator/execution_plugin/instantiation.py | 20 +-
aria/orchestrator/execution_plugin/local.py | 4 +-
.../execution_plugin/ssh/operations.py | 46 +-
aria/orchestrator/execution_plugin/ssh/tunnel.py | 4 +-
aria/orchestrator/plugin.py | 12 +-
aria/orchestrator/topology/instance_handler.py | 106 +--
aria/orchestrator/topology/template_handler.py | 91 +-
aria/orchestrator/topology/topology.py | 10 +-
aria/orchestrator/workflows/api/task.py | 10 +-
aria/orchestrator/workflows/api/task_graph.py | 8 +-
.../workflows/builtin/execute_operation.py | 2 +-
aria/orchestrator/workflows/core/engine.py | 2 +-
aria/orchestrator/workflows/core/events_handler.py | 4 +-
aria/orchestrator/workflows/core/graph_compiler.py | 4 +-
aria/orchestrator/workflows/events_logging.py | 18 +-
aria/orchestrator/workflows/exceptions.py | 8 +-
aria/orchestrator/workflows/executor/celery.py | 2 +-
aria/orchestrator/workflows/executor/dry.py | 6 +-
aria/orchestrator/workflows/executor/process.py | 8 +-
aria/orchestrator/workflows/executor/thread.py | 2 +-
aria/parser/consumption/context.py | 6 +-
aria/parser/consumption/inputs.py | 2 +-
aria/parser/consumption/presentation.py | 275 +++++--
aria/parser/consumption/validation.py | 2 +-
aria/parser/loading/file.py | 18 +-
aria/parser/loading/literal.py | 3 +
aria/parser/loading/loader.py | 13 +-
aria/parser/loading/location.py | 27 +-
aria/parser/loading/request.py | 20 +-
aria/parser/loading/uri.py | 47 +-
aria/parser/presentation/__init__.py | 14 +-
aria/parser/presentation/context.py | 23 +-
aria/parser/presentation/field_validators.py | 23 +-
aria/parser/presentation/fields.py | 125 +--
aria/parser/presentation/presentation.py | 15 +-
aria/parser/presentation/presenter.py | 8 +-
aria/parser/presentation/source.py | 2 +-
aria/parser/presentation/utils.py | 28 +-
aria/parser/reading/__init__.py | 5 +-
aria/parser/reading/context.py | 3 -
aria/parser/reading/exceptions.py | 6 -
aria/parser/reading/jinja.py | 4 +-
aria/parser/reading/json.py | 2 +-
aria/parser/reading/locator.py | 20 +-
aria/parser/reading/reader.py | 11 +-
aria/parser/reading/source.py | 4 +-
aria/parser/reading/yaml.py | 51 +-
aria/parser/specification.py | 2 +-
aria/parser/validation/issue.py | 22 +-
aria/storage/filesystem_rapi.py | 4 +-
aria/utils/caching.py | 2 +-
aria/utils/collections.py | 56 +-
aria/utils/formatting.py | 2 +-
aria/utils/threading.py | 153 ++--
aria/utils/uris.py | 2 +-
aria/utils/versions.py | 4 +-
.../clearwater/clearwater-single-existing.yaml | 12 +-
examples/hello-world/hello-world.yaml | 34 +-
examples/hello-world/scripts/start.sh | 2 +-
.../use-cases/non-normative-types.yaml | 2 +
.../profiles/aria-1.0/aria-1.0.yaml | 3 +
.../profiles/tosca-simple-1.0/artifacts.yaml | 8 +
.../profiles/tosca-simple-1.0/capabilities.yaml | 12 +
.../profiles/tosca-simple-1.0/data.yaml | 14 +
.../profiles/tosca-simple-1.0/groups.yaml | 1 +
.../profiles/tosca-simple-1.0/interfaces.yaml | 3 +
.../profiles/tosca-simple-1.0/nodes.yaml | 14 +
.../profiles/tosca-simple-1.0/policies.yaml | 5 +
.../profiles/tosca-simple-1.0/relationships.yaml | 8 +
.../profiles/tosca-simple-nfv-1.0/artifacts.yaml | 1 +
.../tosca-simple-nfv-1.0/capabilities.yaml | 3 +
.../profiles/tosca-simple-nfv-1.0/data.yaml | 10 +
.../profiles/tosca-simple-nfv-1.0/nodes.yaml | 25 +-
.../tosca-simple-nfv-1.0/relationships.yaml | 2 +
.../simple_nfv_v1_0/presenter.py | 4 +-
.../aria_extension_tosca/simple_v1_0/__init__.py | 12 +-
.../simple_v1_0/assignments.py | 30 +-
.../aria_extension_tosca/simple_v1_0/data_types.py | 111 ++-
.../simple_v1_0/definitions.py | 58 +-
.../aria_extension_tosca/simple_v1_0/misc.py | 18 +-
.../simple_v1_0/modeling/__init__.py | 64 +-
.../simple_v1_0/modeling/capabilities.py | 56 +-
.../simple_v1_0/modeling/copy.py | 2 +-
.../simple_v1_0/modeling/data_types.py | 67 +-
.../simple_v1_0/modeling/functions.py | 135 +--
.../simple_v1_0/modeling/groups.py | 43 +
.../simple_v1_0/modeling/interfaces.py | 160 ++--
.../simple_v1_0/modeling/parameters.py | 45 +-
.../simple_v1_0/modeling/requirements.py | 105 ++-
.../simple_v1_0/modeling/substitution_mappings.py | 51 +-
.../simple_v1_0/presentation/extensible.py | 2 +-
.../simple_v1_0/presentation/field_getters.py | 22 +-
.../simple_v1_0/presentation/field_validators.py | 142 ++--
.../simple_v1_0/presentation/types.py | 2 +-
.../aria_extension_tosca/simple_v1_0/presenter.py | 6 +-
.../aria_extension_tosca/simple_v1_0/templates.py | 23 +-
.../aria_extension_tosca/simple_v1_0/types.py | 52 +-
requirements.in | 3 +-
requirements.txt | 23 +-
setup.py | 4 +-
tests/cli/test_node_templates.py | 2 +-
tests/cli/test_nodes.py | 2 +-
tests/cli/test_service_templates.py | 2 +-
tests/cli/test_services.py | 2 +-
tests/end2end/test_hello_world.py | 4 +-
tests/end2end/test_nodecellar.py | 2 +-
.../__init__.py | 0
.../aria_extension_tosca}/__init__.py | 0
.../aria_extension_tosca/aria_v1_0}/__init__.py | 0
.../aria_v1_0/test_profile.py} | 13 +-
tests/extensions/aria_extension_tosca/conftest.py | 48 ++
.../simple_nfv_v1_0}/__init__.py | 0
.../simple_nfv_v1_0/test_profile.py} | 6 +
.../aria_extension_tosca/simple_v1_0}/__init__.py | 0
.../aria_extension_tosca/simple_v1_0/data.py | 82 ++
.../simple_v1_0/functions}/__init__.py | 0
.../simple_v1_0/functions/test_function_concat.py | 102 +++
.../functions/test_function_get_artifact.py | 156 ++++
.../functions/test_function_get_input.py | 94 +++
.../functions/test_function_get_nodes_of_type.py | 70 ++
.../test_function_get_operation_output.py | 84 ++
.../simple_v1_0/functions/test_function_token.py | 119 +++
.../functions/test_functions_modelable_entity.py | 247 ++++++
.../simple_v1_0/templates}/__init__.py | 0
.../simple_v1_0/templates/common}/__init__.py | 0
.../simple_v1_0/templates/common/test_copy.py | 68 ++
.../templates/common/test_template_interface.py | 914 +++++++++++++++++++++
.../templates/common/test_template_parameters.py | 781 ++++++++++++++++++
.../common/test_template_parameters_properties.py | 132 +++
.../simple_v1_0/templates/common/test_templates.py | 128 +++
.../templates/node_template}/__init__.py | 0
.../node_template/test_node_template_artifacts.py | 307 +++++++
.../node_template/test_node_template_directives.py | 77 ++
.../test_node_template_node_filter_constraints.py | 346 ++++++++
.../test_node_template_node_filters.py | 313 +++++++
.../test_node_template_requirements.py | 853 +++++++++++++++++++
.../simple_v1_0/templates/test_group.py | 159 ++++
.../simple_v1_0/templates/test_policy.py | 272 ++++++
.../templates/test_substitution_mappings.py | 449 ++++++++++
.../templates/test_topology_template.py | 61 ++
.../simple_v1_0/test_dsl_definitions.py | 39 +-
.../simple_v1_0/test_imports.py | 200 +++++
.../simple_v1_0/test_metadata.py | 98 +++
.../aria_extension_tosca/simple_v1_0/test_names.py | 57 ++
.../simple_v1_0/test_profile.py} | 6 +
.../simple_v1_0/test_repositories.py | 179 ++++
.../simple_v1_0/test_service_template.py} | 8 +
.../simple_v1_0/types}/__init__.py | 0
.../simple_v1_0/types/common}/__init__.py | 0
.../types/common/test_type_interfaces.py | 469 +++++++++++
.../types/common/test_type_parameters.py | 418 ++++++++++
.../common/test_type_parameters_inheritance.py | 114 +++
.../common/test_type_parameters_properties.py | 312 +++++++
.../simple_v1_0/types/common/test_types.py | 185 +++++
.../simple_v1_0/types/node_type}/__init__.py | 0
.../types/node_type/test_node_type_capabilities.py | 302 +++++++
.../test_node_type_relationship_interfaces.py | 54 ++
.../types/node_type/test_node_type_requirements.py | 361 ++++++++
.../simple_v1_0/types/test_artifact_type.py | 74 ++
.../simple_v1_0/types/test_capability_type.py | 85 ++
.../simple_v1_0/types/test_data_type.py | 68 ++
.../simple_v1_0/types/test_group_type.py | 85 ++
.../simple_v1_0/types/test_interface_type.py | 149 ++++
.../simple_v1_0/types/test_policy_type.py | 123 +++
.../simple_v1_0/types/test_relationship_type.py | 85 ++
.../__init__.py | 0
tests/mechanisms/parsing/__init__.py | 81 ++
tests/mechanisms/parsing/aria.py | 78 ++
tests/mechanisms/utils.py | 80 ++
tests/mechanisms/web_server.py | 84 ++
tests/mock/workflow.py | 2 +-
tests/modeling/__init__.py | 2 +-
tests/orchestrator/context/test_operation.py | 4 +-
tests/orchestrator/context/test_serialize.py | 3 +-
tests/orchestrator/context/test_workflow.py | 2 +-
.../execution/test_execution_compiler.py | 3 +-
tests/orchestrator/execution_plugin/test_local.py | 2 +-
tests/orchestrator/execution_plugin/test_ssh.py | 2 +-
.../workflows/executor/test_process_executor.py | 2 +-
.../executor/test_process_executor_extension.py | 2 +-
.../test_process_executor_tracked_changes.py | 6 +-
.../presentation/__init__.py | 0
tests/requirements.txt | 3 +-
.../tosca-simple-1.0/node-cellar/node-cellar.yaml | 8 +-
.../types/shorthand-1/shorthand-1.yaml | 23 -
.../types/typequalified-1/typequalified-1.yaml | 23 -
tests/storage/test_model_storage.py | 4 +-
tests/test_extension.py | 4 +-
.../__init__.py | 0
tests/{parser => topology}/service_templates.py | 16 -
.../test_configuration.py | 3 +-
.../test_end2end.py | 2 +-
tests/{parser => topology}/test_reqs_caps.py | 0
tests/{parser => topology}/utils.py | 2 +
tests/utils/test_plugin.py | 2 +-
tests/utils/test_versions.py | 10 +-
tox.ini | 41 +-
218 files changed, 11580 insertions(+), 1232 deletions(-)
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 @@ python:
- '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 @@ clean:
-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 @@ test:
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 @@ and run::
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 @@ and run::
# no additional system requirements are needed
pip install apache-ariatosca[ssh]
-**MacOS**::
+**macOS**::
# TODO
@@ -112,26 +112,14 @@ To uninstall and clean your environment, follow these steps::
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 @@ License
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 @@ ARIA is licensed under the
: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 @@ environment:
- 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 @@ import pkg_resources
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 @@ __all__ = (
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.
-
- It then invokes all registered extension functions.
+ Loads all Python packages with names beginning with ``aria_extension_`` and calls their
+ ``aria_extension`` initialization entry points if they have them.
- :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 @@ def list(service_template_name,
@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 @@ def check_overriding_storage_exceptions(e, model_class, name):
'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 @@ def generate_progress_handler(file_path, action='', max_bar_length=80):
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 @@ class Evaluation(object):
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 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods):
@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 @@ class ParameterMixin(TemplateModelMixin, caching.HasCachedMethods):
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 @@ class TaskBase(mixins.ModelMixin):
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 @@ ARIA modeling service common module
from sqlalchemy import (
Column,
Text,
- Boolean
+ Boolean,
+ PickleType
)
from sqlalchemy.ext.declarative import declared_attr
@@ -90,7 +91,7 @@ class InputBase(ParameterMixin):
""")
@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 @@ class MetadataBase(TemplateModelMixin):
: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 @@ class NodeBase(InstanceModelMixin):
# 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 @@ class NodeBase(InstanceModelMixin):
@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 @@ class InterfaceTemplateBase(TemplateModelMixin):
('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 @@ class PluginSpecificationBase(TemplateModelMixin):
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 @@ class ModelJSONEncoder(JSONEncoder):
# 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 @@ class BaseContext(object):
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 @@ class BaseOperationContext(common.BaseContext):
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 @@ class BaseOperationContext(common.BaseContext):
"""
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 @@ class WorkflowContext(BaseContext):
"""
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 @@ class WorkflowContext(BaseContext):
"""
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 operation(func=None, toolbelt=False, suffix_template='', logging_handlers=No
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 @@ def download_script(ctx, script_path):
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 @@ def create_process_config(script_path, process, operation_kwargs, quote_json_env
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 @@ def create_process_config(script_path, process, operation_kwargs, quote_json_env
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 @@ def check_error(ctx, error_check_func=None, reraise=False):
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 @@ CTX_SOCKET_URL = 'CTX_SOCKET_URL'
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 @@ def _http_request(socket_url, request, method, timeout):
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 @@ class CtxProxy(object):
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 @@ class CtxProxy(object):
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 @@ class CtxProxy(object):
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 @@ def _process_arguments(obj, args):
# 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 @@ def _process_next_operation(obj, args, modifying):
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 @@ def _configure_local(operation):
"""
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 @@ def _configure_remote(operation, reporter):
# 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 @@ def _configure_remote(operation, reporter):
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 @@ def _get_process(operation, reporter):
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 @@ def _get_ssh(operation, reporter):
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 @@ def _validate_type(value, the_type, name, reporter):
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 @@ def _coerce_bool(value, name, reporter):
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 @@ def _dict_to_list_of_strings(the_dict, name, reporter):
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 @@ def _execute_func(script_path, ctx, process, operation_kwargs):
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 @@ def _execute_func(script_path, ctx, process, operation_kwargs):
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 @@ def run_commands(ctx, commands, fabric_env, use_sudo, hide_output, **_):
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 @@ def run_script(ctx, script_path, fabric_env, process, use_sudo, hide_output, **k
# 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 @@ def run_script(ctx, script_path, fabric_env, process, use_sudo, hide_output, **k
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 @@ def run_script(ctx, script_path, fabric_env, process, use_sudo, hide_output, **k
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 @@ def _hide_output(ctx, groups):
""" 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 _fabric_env(ctx, fabric_env, warn_only):
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 @@ class _Paths(object):
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 @@ def remote(ctx, local_port, remote_port=0, local_host='localhost', remote_bind_a
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 @@ class PluginManager(object):
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 @@ class PluginManager(object):
"""
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 @@ class PluginManager(object):
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 @@ class Artifact(common.InstanceHandlerBase):
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 @@ class Capability(common.InstanceHandlerBase):
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 @@ class Group(common.ActorHandlerBase):
**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 @@ class Interface(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Node(common.ActorHandlerBase):
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 @@ class Operation(common.ActorHandlerBase):
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 @@ class Operation(common.ActorHandlerBase):
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 @@ class Policy(common.InstanceHandlerBase):
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 @@ class Relationship(common.ActorHandlerBase):
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 @@ class Substitution(common.InstanceHandlerBase):
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 @@ class SubstitutionMapping(common.InstanceHandlerBase):
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 SubstitutionMapping(common.InstanceHandlerBase):
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 @@ class _Parameter(common.InstanceHandlerBase):
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 @@ class ServiceTemplate(common.TemplateHandlerBase):
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 @@ class ServiceTemplate(common.TemplateHandlerBase):
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 @@ class ArtifactTemplate(common.TemplateHandlerBase):
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 @@ class CapabilityTemplate(common.TemplateHandlerBase):
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 @@ class RequirementTemplate(common.TemplateHandlerBase):
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 RequirementTemplate(common.TemplateHandlerBase):
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 @@ class InterfaceTemplate(common.TemplateHandlerBase):
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 InterfaceTemplate(common.TemplateHandlerBase):
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 NodeTemplate(common.TemplateHandlerBase):
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 @@ class SubstitutionTemplate(common.TemplateHandlerBase):
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 @@ class SubstitutionTemplateMapping(common.TemplateHandlerBase):
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 @@ class SubstitutionTemplateMapping(common.TemplateHandlerBase):
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 @@ class SubstitutionTemplateMapping(common.TemplateHandlerBase):
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 SubstitutionTemplateMapping(common.TemplateHandlerBase):
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 @@ class OperationTemplate(common.TemplateHandlerBase):
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 @@ class Topology(issue.ReporterMixin):
# 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 @@ class Topology(issue.ReporterMixin):
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 @@ class OperationTask(BaseTask):
: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 @@ class OperationTask(BaseTask):
# 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 @@ class OperationTask(BaseTask):
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 @@ class TaskGraph(object):
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 @@ class TaskGraph(object):
``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 @@ class TaskGraph(object):
``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 @@ class TaskGraph(object):
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 @@ def execute_operation(
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 aria.orchestrator.context import operation
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 _update_node_state_if_necessary(ctx, is_transitional=False):
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 @@ class GraphCompiler(object):
@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 @@ from ... import modeling
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 @@ def _start_task_handler(ctx, **kwargs):
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 _start_task_handler(ctx, **kwargs):
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 @@ class ProcessException(ExecutorException):
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 @@ class CeleryExecutor(BaseExecutor):
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 datetime import datetime
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 @@ class DryExecutor(base.BaseExecutor):
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 os
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 @@ class ProcessExecutor(base.BaseExecutor):
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 @@ class ThreadExecutor(BaseExecutor):
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 @@ class ConsumptionContext(object):
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 @@ class Inputs(Consumer):
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..9bddd5e 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 @@ class Read(Consumer):
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 @@ class Read(Consumer):
cycle, for example if the agnostic raw data has dependencies that must also be parsed.
"""
- def consume(self):
- if self.context.presentation.location is None:
- self.context.validation.report('Presentation consumer: missing location')
- return
-
- presenter = None
- imported_presentations = None
+ def __init__(self, context):
+ super(Read, self).__init__(context)
+ self._cache = {}
- 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()
-
- # Handle exceptions
- for e in executor.exceptions:
- self._handle_exception(e)
+ def consume(self):
+ # Present the main location and all imports recursively
+ main_result, all_results = self._present_all()
- imported_presentations = executor.returns
- finally:
- executor.close()
+ # Merge presentations
+ main_result.merge(all_results, self.context)
- # 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)
+ # Cache merged presentations
+ if self.context.presentation.cache:
+ for result in all_results:
+ result.cache()
- 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 @@ class Read(Consumer):
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)
+ # 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:
+ presentation = PRESENTATION_CACHE[canonical_location]
+ return _Result(presentation, canonical_location, origin_canonical_location)
+ except KeyError:
+ pass
+
+ try:
+ # Is the presentation in the local cache?
+ presentation = self._cache[canonical_location]
+ return _Result(presentation, canonical_location, origin_canonical_location)
+ except KeyError:
+ pass
+
+ # 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:
+ import_location = UriLocation(import_location)
+ 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 overrides everything
+ # 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 presenter_class is None:
+ if default_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')
+ 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 presentation is not None and hasattr(presentation, '_link_locators'):
+ if hasattr(presentation, '_link_locators'):
presentation._link_locators()
- # 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)
-
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:
+ raise RuntimeError(u'Only merged presentations can be cached: {0}'
+ .format(self.canonical_location))
+ 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 @@ class Validate(Consumer):
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 @@ class FileTextLoader(Loader):
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 @@ class LiteralLoader(Loader):
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 @@ class Loader(object):
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 @@ class Loader(object):
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 @@ class Location(object):
an appropriate :class:`~aria.parser.loading.Loader`.
"""
- def is_equivalent(self, location):
- raise NotImplementedError
-
@property
def prefix(self):
return None
@@ -47,15 +44,12 @@ class UriLocation(Location):
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 @@ class UriLocation(Location):
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 @@ class LiteralLocation(Location):
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 @@ class RequestLoader(Loader):
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 @@ class RequestTextLoader(RequestLoader):
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 ...extension import parser
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 @@ class UriTextLoader(Loader):
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 @@ class UriTextLoader(Loader):
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 @@ class UriTextLoader(Loader):
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 @@ class UriTextLoader(Loader):
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 @@ Field validators
.. 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 @@ Utilities
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 @@ __all__ = (
'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 @@ class PresentationContext(object):
: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 @@ class PresentationContext(object):
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 @@ class PresentationContext(object):
"""
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 @@ def list_length_validator(length):
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 @@ def has_fields_getitem(self, key):
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 @@ def has_fields_setitem(self, key, value):
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 @@ def has_fields_delitem(self, key):
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 @@ class Field(object):
@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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
# 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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class Field(object):
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 @@ class PresentationBase(HasCachedMethods):
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 @@ class PresentationBase(HasCachedMethods):
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 @@ class PresentationBase(HasCachedMethods):
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 @@ class Presentation(PresentationBase):
"""
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 @@ class AsIsPresentation(PresentationBase):
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 @@ class Presenter(Presentation):
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 @@ class PresenterSource(object):
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 @@ def validate_primitive(value, cls, coerce=False):
: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 @@ def validate_primitive(value, cls, coerce=False):
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 @@ def validate_no_short_form(context, presentation):
"""
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 @@ def validate_no_unknown_fields(context, presentation):
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 get_parent_presentation(context, presentation, *types_dict_names):
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 @@ Reading package.
ReaderException
ReaderNotFoundError
ReaderSyntaxError
- AlreadyReadException
JinjaReader
JsonReader
Locator
@@ -41,14 +40,12 @@ from .context import ReadingContext
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 @@ class ReadingContext(object):
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 @@ class ReaderSyntaxError(ReaderException):
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 @@ class JinjaReader(Reader):
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 @@ class JinjaReader(Reader):
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 @@ class JsonReader(Reader):
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 @@ class Locator(object):
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 @@ class Locator(object):
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 @@ class Locator(object):
def __str__(self):
# Should be in same format as Issue.locator_as_str
- return '"%s":%d:%d' % (self.location, self.line, self.column)
+ 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 @@ class Reader(object):
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 @@ class ReaderSource(object):
"""
@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)
+
+
+# YAML mapping tag
+MAP_TAG = u'tag:yaml.org,2002:map'
-# Add our types to ruamel.yaml
+# 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(
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 @@ class YamlLocator(Locator):
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 @@ class YamlReader(Reader):
# 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 @@ class YamlReader(Reader):
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 @@ import re
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 @@ class Issue(object):
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 @@ class Issue(object):
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 @@ class ReporterMixin(object):
# 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 @@ class FileSystemResourceAPI(api.ResourceAPI):
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 @@ class FileSystemResourceAPI(api.ResourceAPI):
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 functools import partial
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 @@ Additional collection classes and collection utilities.
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 @@ except ImportError:
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 @@ class StrictList(list):
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 @@ class StrictDict(OrderedDict):
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 @@ class StrictDict(OrderedDict):
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 @@ def prune(value, is_removable_function=is_removable):
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 @@ def deepcopy_with_locators(value):
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 @@ class JsonAsRawEncoder(json.JSONEncoder):
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 @@ class DaemonThread(Thread):
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 @@ class FixedThreadPoolExecutor(object):
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 @@ class FixedThreadPoolExecutor(object):
: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 @@ class FixedThreadPoolExecutor(object):
self.size = size
self.timeout = timeout
- self.print_exceptions = print_exceptions
self._tasks = Queue()
self._returns = {}
@@ -124,34 +221,18 @@ class FixedThreadPoolExecutor(object):
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 @@ class FixedThreadPoolExecutor(object):
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 @@ class FixedThreadPoolExecutor(object):
@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 @@ class FixedThreadPoolExecutor(object):
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 @@ def as_file(uri):
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 @@ _INF = float('inf')
_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 @@ class VersionString(unicode):
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 @@ topology_template:
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 @@ tosca_definitions_version: tosca_simple_yaml_1_0
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 @@ topology_template:
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 @@ set -e
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 @@ node_types:
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 @@ policy_types:
aria.Plugin:
_extensions:
+ normative: true
shorthand_name: Plugin
type_qualified_name: aria:Plugin
role: plugin
@@ -40,6 +41,7 @@ policy_types:
aria.Workflow:
_extensions:
+ normative: true
shorthand_name: Workflow
type_qualified_name: aria:Workflow
role: workflow
@@ -61,6 +63,7 @@ policy_types:
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 @@ artifact_types:
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 @@ artifact_types:
tosca.artifacts.File:
_extensions:
+ normative: true
shorthand_name: File
type_qualified_name: tosca:File
specification: tosca-simple-1.0
@@ -41,6 +43,7 @@ artifact_types:
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 @@ artifact_types:
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 @@ artifact_types:
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 @@ artifact_types:
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 @@ artifact_types:
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 @@ artifact_types:
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 @@ capability_types:
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 @@ capability_types:
tosca.capabilities.Node:
_extensions:
+ normative: true
shorthand_name: Node
type_qualified_name: tosca:Node
specification: tosca-simple-1.0
@@ -39,6 +41,7 @@ capability_types:
tosca.capabilities.Container:
_extensions:
+ normative: true
shorthand_name: Container
type_qualified_name: tosca:Container
specification: tosca-simple-1.0
@@ -82,6 +85,7 @@ capability_types:
tosca.capabilities.Attachment:
_extensions:
+ normative: true
shorthand_name: Attachment
type_qualified_name: tosca:Attachment
specification: tosca-simple-1.0
@@ -94,6 +98,7 @@ capability_types:
tosca.capabilities.OperatingSystem:
_extensions:
+ normative: true
shorthand_name: OperatingSystem
type_qualified_name: tosca:OperatingSystem
specification: tosca-simple-1.0
@@ -127,6 +132,7 @@ capability_types:
tosca.capabilities.Scalable:
_extensions:
+ normative: true
shorthand_name: Scalable
type_qualified_name: tosca:Scalable
specification: tosca-simple-1.0
@@ -163,6 +169,7 @@ capability_types:
tosca.capabilities.Endpoint:
_extensions:
+ normative: true
shorthand_name: Endpoint
type_qualified_name: tosca:Endpoint
specification: tosca-simple-1.0
@@ -232,6 +239,7 @@ capability_types:
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 @@ capability_types:
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 @@ capability_types:
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 @@ capability_types:
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 @@ capability_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
tosca.datatypes.Credential:
_extensions:
+ normative: true
shorthand_name: Credential
type_qualified_name: tosca:Credential
specification: tosca-simple-1.0
@@ -145,6 +155,7 @@ data_types:
tosca.datatypes.network.NetworkInfo:
_extensions:
+ normative: true
shorthand_name: NetworkInfo
type_qualified_name: tosca:NetworkInfo
specification: tosca-simple-1.0
@@ -174,6 +185,7 @@ data_types:
tosca.datatypes.network.PortInfo:
_extensions:
+ normative: true
shorthand_name: PortInfo
type_qualified_name: tosca:PortInfo
specification: tosca-simple-1.0
@@ -213,6 +225,7 @@ data_types:
tosca.datatypes.network.PortDef:
_extensions:
+ normative: true
shorthand_name: PortDef
type_qualified_name: tosca:PortDef
specification: tosca-simple-1.0
@@ -226,6 +239,7 @@ data_types:
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 @@ group_types:
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 @@ interface_types:
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 @@ interface_types:
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 @@ interface_types:
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 @@ node_types:
tosca.nodes.Root:
_extensions:
+ normative: true
shorthand_name: Root
type_qualified_name: tosca:Root
specification: tosca-simple-1.0
@@ -55,6 +56,7 @@ node_types:
tosca.nodes.Compute:
_extensions:
+ normative: true
shorthand_name: Compute
type_qualified_name: tosca:Compute
specification: tosca-simple-1.0
@@ -106,6 +108,7 @@ node_types:
tosca.nodes.LoadBalancer:
_extensions:
+ normative: true
shorthand_name: LoadBalancer
type_qualified_name: tosca:LoadBalancer
specification: tosca-simple-1.0
@@ -140,6 +143,7 @@ node_types:
tosca.nodes.SoftwareComponent:
_extensions:
+ normative: true
shorthand_name: SoftwareComponent
type_qualified_name: tosca:SoftwareComponent
specification: tosca-simple-1.0
@@ -168,6 +172,7 @@ node_types:
tosca.nodes.WebServer:
_extensions:
+ normative: true
shorthand_name: WebServer
type_qualified_name: tosca:WebServer
specification: tosca-simple-1.0
@@ -188,6 +193,7 @@ node_types:
tosca.nodes.WebApplication:
_extensions:
+ normative: true
shorthand_name: WebApplication
type_qualified_name: tosca:WebApplication
specification: tosca-simple-1.0
@@ -214,6 +220,7 @@ node_types:
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 @@ node_types:
tosca.nodes.Database:
_extensions:
+ normative: true
shorthand_name: Database
type_qualified_name: tosca:Database
specification: tosca-simple-1.0
@@ -283,6 +291,7 @@ node_types:
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 @@ node_types:
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 @@ node_types:
tosca.nodes.ObjectStorage:
_extensions:
+ normative: true
shorthand_name: ObjectStorage
type_qualified_name: tosca:ObjectStorage
specification: tosca-simple-1.0
@@ -354,6 +365,7 @@ node_types:
tosca.nodes.BlockStorage:
_extensions:
+ normative: true
shorthand_name: BlockStorage
type_qualified_name: tosca:BlockStorage
specification: tosca-simple-1.0
@@ -388,6 +400,7 @@ node_types:
tosca.nodes.network.Network:
_extensions:
+ normative: true
shorthand_name: Network
type_qualified_name: tosca:Network
specification: tosca-simple-1.0
@@ -466,6 +479,7 @@ node_types:
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 @@ policy_types:
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 @@ policy_types:
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 @@ policy_types:
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 @@ policy_types:
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 @@ policy_types:
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 @@ relationship_types:
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 @@ relationship_types:
tosca.relationships.DependsOn:
_extensions:
+ normative: true
shorthand_name: DependsOn
type_qualified_name: tosca:DependsOn
specification: tosca-simple-1.0
@@ -58,6 +60,7 @@ relationship_types:
tosca.relationships.HostedOn:
_extensions:
+ normative: true
shorthand_name: HostedOn
type_qualified_name: tosca:HostedOn
specification: tosca-simple-1.0
@@ -70,6 +73,7 @@ relationship_types:
tosca.relationships.ConnectsTo:
_extensions:
+ normative: true
shorthand_name: ConnectsTo
type_qualified_name: tosca:ConnectsTo
specification: tosca-simple-1.0
@@ -86,6 +90,7 @@ relationship_types:
tosca.relationships.AttachesTo:
_extensions:
+ normative: true
shorthand_name: AttachesTo
type_qualified_name: tosca:AttachesTo
specification: tosca-simple-1.0
@@ -119,6 +124,7 @@ relationship_types:
tosca.relationships.RoutesTo:
_extensions:
+ normative: true
shorthand_name: RoutesTo
type_qualified_name: tosca:RoutesTo
specification: tosca-simple-1.0
@@ -135,6 +141,7 @@ relationship_types:
tosca.relationships.network.LinksTo:
_extensions:
+ normative: true
shorthand_name: LinksTo
type_qualified_name: tosca:LinksTo
specification: tosca-simple-1.0
@@ -147,6 +154,7 @@ relationship_types:
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 @@ artifact_types:
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 @@ capability_types:
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 @@ capability_types:
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 @@ capability_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ data_types:
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 @@ node_types:
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 @@ node_types:
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 @@ node_types:
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 @@ node_types:
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 @@ node_types:
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 @@ relationship_types:
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 @@ relationship_types:
# 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 aria.utils.caching import cachedmethod
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 @@ class ToscaSimpleNfvPresenter1_0(ToscaSimplePresenter1_0): # pylint: disable=inv
@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 @@ Definitions
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 .presenter import ToscaSimplePresenter1_0
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 @@ __all__ = (
'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 @@ class InterfaceAssignment(ExtensiblePresentation):
# 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 @@ class InterfaceAssignment(ExtensiblePresentation):
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 @@ class RelationshipAssignment(ExtensiblePresentation):
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 @@ class RequirementAssignment(ExtensiblePresentation):
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 @@ class ArtifactAssignmentForType(ExtensiblePresentation):
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 @@ class Timezone(tzinfo):
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 @@ class Timestamp(object):
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 @@ class Timestamp(object):
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 @@ class Timestamp(object):
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 @@ class Timestamp(object):
@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 @@ class Version(object):
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 @@ class Version(object):
"""
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 @@ class Version(object):
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 @@ class Version(object):
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 @@ class Version(object):
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 @@ class Range(object):
#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 @@ class List(list):
"""
@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 @@ class Map(StrictDict):
"""
@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 @@ class Map(StrictDict):
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 @@ class Scalar(object):
"""
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 @@ class Scalar(object):
('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 @@ class Scalar(object):
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 @@ class ScalarFrequency(Scalar):
# 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 @@ def coerce_range(context, presentation, the_type, entry_schema, constraints, val
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 @@ class AttributeDefinition(ExtensiblePresentation):
@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 @@ class ParameterDefinition(PropertyDefinition):
#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 @@ class ParameterDefinition(PropertyDefinition):
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 @@ class InterfaceDefinition(ExtensiblePresentation):
@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 @@ class InterfaceDefinition(ExtensiblePresentation):
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 @@ class RelationshipDefinition(ExtensiblePresentation):
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 @@ class RequirementDefinition(ExtensiblePresentation):
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 @@ class CapabilityDefinition(ExtensiblePresentation):
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 import implements_specification
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 @@ class Description(AsIsPresentation):
#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 @@ class MetaData(ExtensiblePresentation):
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 @@ class MetaData(ExtensiblePresentation):
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 @@ class Repository(ExtensiblePresentation):
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 @@ class ConstraintClause(ExtensiblePresentation):
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 datetime import datetime
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 @@ from ..data_types import coerce_value
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 @@ def create_relationship_template_model(context, service_template, relationship):
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 @@ def create_capability_template_model(context, service_template, capability):
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 @@ def create_operation_template_model(context, service_template, operation):
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 @@ def create_workflow_operation_template_model(context, service_template, policy):
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 @@ def create_node_filter_constraints(context, node_filter, target_node_template_co
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 @@ def create_node_filter_constraints(context, node_filter, target_node_template_co
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 @@ def create_constraint(context, node_filter, constraint_clause, property_name, ca
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 @@ def split_prefix(string):
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 @@ def extract_implementation_primary(context, service_template, presentation, mode
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 @@ def get_inherited_capability_definitions(context, presentation, for_presentation
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 @@ def get_template_capabilities(context, presentation):
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 @@ def convert_capability_from_definition_to_assignment(context, presentation, cont
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(context, presentation, capability_definition,
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 @@ def get_default_raw_from_copy(presentation, field_name):
"""
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 @@ def get_inherited_constraints(context, presentation):
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 @@ def coerce_data_type_value(context, presentation, data_type, entry_schema, const
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 @@ def coerce_data_type_value(context, presentation, data_type, entry_schema, const
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 @@ def validate_data_type_name(context, presentation):
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 @@ def get_property_constraints(context, presentation):
# 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 @@ def apply_constraint_to_value(context, presentation, constraint_clause, value):
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 apply_constraint_to_value(context, presentation, constraint_clause, value):
#
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 @@ def get_data_type_name(the_type):
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 @@ def coerce_value(context, presentation, the_type, entry_schema, constraints, val
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 @@ def report_issue_for_bad_format(context, presentation, the_type, value, aspect,
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.exceptions import InvalidValueError
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 @@ class Concat(Function):
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 @@ class Token(Function):
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 @@ class Token(Function):
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 @@ class GetInput(Function):
'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 @@ class GetInput(Function):
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 @@ class GetProperty(Function):
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 @@ class GetProperty(Function):
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 @@ class GetAttribute(Function):
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 @@ class GetAttribute(Function):
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 @@ class GetAttribute(Function):
# 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 @@ class GetOperationOutput(Function):
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 @@ class GetOperationOutput(Function):
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 @@ class GetNodesOfType(Function):
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 @@ class GetNodesOfType(Function):
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 @@ class GetArtifact(Function):
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 @@ class GetArtifact(Function):
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 @@ class GetArtifact(Function):
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 @@ def get_function(context, presentation, value):
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 @@ def parse_int(context, presentation, name, index, explanation, value): # pylint:
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 @@ def parse_modelable_entity_name(context, presentation, name, index, value):
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 @@ def get_modelable_entities(container_holder, name, locator, modelable_entity_nam
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 @@ def get_self(container_holder, name, locator):
(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 @@ def get_hosts(container_holder, name, locator):
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 @@ def get_source(container_holder, name, locator):
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 @@ def get_target(container_holder, name, locator):
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 @@ def get_target(container_holder, name, locator):
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 get_modelable_entity_parameter(modelable_entity, parameters, nested_paramete
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.presentation import get_locator
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 @@ def get_template_interfaces(context, presentation, type_name):
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 @@ def get_template_interfaces(context, presentation, type_name):
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 @@ def convert_interface_definition_from_type_to_template(context, presentation, co
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 @@ def convert_interface_definition_from_type_to_raw_template(context, presentation
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 @@ def merge_interface(context, presentation, interface_assignment, our_interface_a
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)
+
+ # 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 our_implementation is not None:
- interface_assignment._raw[operation_name]['implementation'] = \
- deepcopy_with_locators(our_implementation._raw)
+ 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)
+ # 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 @@ def merge_interface_definition(context, interface, our_source, presentation, typ
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 @@ def merge_interface_definitions_from_their_types(context, interfaces, presentati
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 @@ def validate_required_inputs(context, presentation, assignment, definition, orig
# (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 @@ def validate_required_inputs(context, presentation, assignment, definition, orig
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 @@ def get_assigned_and_defined_parameter_values(context, presentation, field_name)
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 @@ def get_assigned_and_defined_parameter_values(context, presentation, field_name)
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 @@ def get_parameter_values(context, presentation, field_name):
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 @@ def validate_required_values(context, presentation, values, definitions):
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 @@ def merge_raw_parameter_definition(context, presentation, raw_property_definitio
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 coerce_parameter_value(context, presentation, definition, value, aspect=None
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 @@ def get_template_requirements(context, presentation):
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 @@ def get_template_requirements(context, presentation):
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 @@ def get_template_requirements(context, presentation):
# 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 @@ def convert_requirement_from_definition_to_assignment(context, requirement_defin
# 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 @@ def add_requirement_assignments(context, presentation, requirement_assignments,
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 @@ def merge_requirement_assignment(context, relationship_property_definitions,
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(context, relationship_property_definitions,
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 @@ def merge_requirement_assignment_relationship(context, presentation, property_de
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 @@ def merge_requirement_assignment_relationship(context, presentation, property_de
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_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 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
- if relationship is None:
- return
- validate_required_values(context, presentation, relationship.properties,
- relationship_property_definitions)
+ 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 @@ from aria.parser.validation import Issue
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 @@ def validate_substitution_mappings_requirement(context, presentation):
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 @@ def validate_substitution_mappings_requirement(context, presentation):
_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 @@ def validate_substitution_mappings_requirement(context, presentation):
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_requirement(context, presentation):
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 @@ def validate_substitution_mappings_capability(context, presentation):
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 @@ def validate_substitution_mappings_capability(context, presentation):
_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 @@ def validate_substitution_mappings_capability(context, presentation):
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_substitution_mappings_capability(context, presentation):
#
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 _get_node_template(context, presentation):
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_missing_node_template(context, presentation, field):
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 @@ class ExtensiblePresentation(Presentation):
@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 data_type_class_getter(cls):
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 @@ def copy_validator(template_type_name, templates_dict_name):
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 @@ def data_type_validator(type_name='data type'):
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 @@ def entry_schema_validator(field, presentation, context):
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 @@ def data_type_constraints_validator(field, presentation, context):
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 @@ def data_type_properties_validator(field, presentation, context):
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 @@ def constraint_clause_in_range_validator(field, presentation, context):
# 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 @@ def constraint_clause_pattern_validator(field, presentation, context):
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 @@ def capability_definition_or_type_validator(field, presentation, context):
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 @@ def node_filter_validator(field, presentation, context):
_, 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 @@ def list_node_type_or_group_type_validator(field, presentation, context):
#
+# 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 @@ def policy_targets_validator(field, presentation, context):
values = getattr(presentation, field.name)
if values is not None:
- 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') \
+ 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:
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 @@ def policy_targets_validator(field, presentation, context):
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 @@ def node_filter_properties_validator(field, presentation, context):
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 @@ def node_filter_capabilities_validator(field, presentation, context):
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 @@ def node_filter_capabilities_validator(field, presentation, context):
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 .modeling.functions import (Concat, Token, GetInput, GetProperty, GetAttrib
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 @@ class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstrac
@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 @@ class ToscaSimplePresenter1_0(Presenter): # pylint: disable=invalid-name,abstrac
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 import implements_specification
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.artifacts import get_inherited_artifact_definitions
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 @@ class NodeTemplate(ExtensiblePresentation):
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 @@ class RelationshipTemplate(ExtensiblePresentation):
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 @@ class GroupTemplate(ExtensiblePresentation):
: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 @@ class TopologyTemplate(ExtensiblePresentation):
: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 @@ class TopologyTemplate(ExtensiblePresentation):
: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.capabilities import (get_inherited_valid_source_types,
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 @@ class ArtifactType(ExtensiblePresentation):
"""
@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 @@ class DataType(ExtensiblePresentation):
: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 @@ class DataType(ExtensiblePresentation):
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 @@ class CapabilityType(ExtensiblePresentation):
: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 @@ class CapabilityType(ExtensiblePresentation):
@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 @@ class CapabilityType(ExtensiblePresentation):
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 @@ class InterfaceType(ExtensiblePresentation):
: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 @@ class InterfaceType(ExtensiblePresentation):
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 @@ class RelationshipType(ExtensiblePresentation):
: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 @@ class RelationshipType(ExtensiblePresentation):
@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 @@ class NodeType(ExtensiblePresentation):
: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 @@ class NodeType(ExtensiblePresentation):
@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 @@ class GroupType(ExtensiblePresentation):
: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 @@ class GroupType(ExtensiblePresentation):
@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 @@ class GroupType(ExtensiblePresentation):
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 @@ class PolicyType(ExtensiblePresentation):
: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 @@ backports.shutil_get_terminal_size>=1, <2
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 @@ psutil>=5.4, <5.5
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 @@ extras_require = {
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 @@ with open(os.path.join(root_dir, 'requirements.in')) as requirements:
# 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 mock import ANY, MagicMock
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 @@ import mock
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.core import Core
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.exceptions import DependentActiveExecutionsError, DependentAvailableNo
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 @@ def _verify_deployed_service_in_storage(service_name, model_storage):
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/test_tosca_simple_v1_0/__init__.py b/tests/extensions/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/__init__.py
diff --git a/tests/parser/__init__.py b/tests/extensions/aria_extension_tosca/__init__.py
similarity index 100%
rename from tests/parser/__init__.py
rename to tests/extensions/aria_extension_tosca/__init__.py
diff --git a/tests/instantiation/__init__.py b/tests/extensions/aria_extension_tosca/aria_v1_0/__init__.py
similarity index 100%
rename from tests/instantiation/__init__.py
rename 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 75%
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/__init__.py b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
similarity index 80%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
index ae1e83e..fb756bc 100644
--- a/tests/parser/test_tosca_simple_v1_0/__init__.py
+++ b/tests/extensions/aria_extension_tosca/simple_nfv_v1_0/test_profile.py
@@ -12,3 +12,9 @@
# 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_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%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy 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/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/functions/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/templates/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/common/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/templates/node_template/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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/aria/parser/loading/loader.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
similarity index 50%
copy from aria/parser/loading/loader.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.py
index e1abfbf..3defb70 100644
--- a/aria/parser/loading/loader.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_dsl_definitions.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,22 +14,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import pytest
-class Loader(object):
- """
- Base class for ARIA loaders.
+from . import data
- 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.
- """
+@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 open(self):
- pass
- def close(self):
- pass
+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()
- def load(self):
- raise NotImplementedError
+
+# 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/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
similarity index 81%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
index ae1e83e..922f9dc 100644
--- a/tests/parser/test_tosca_simple_v1_0/__init__.py
+++ b/tests/extensions/aria_extension_tosca/simple_v1_0/test_profile.py
@@ -12,3 +12,9 @@
# 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_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/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
similarity index 79%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/test_service_template.py
index ae1e83e..2bcd018 100644
--- a/tests/parser/test_tosca_simple_v1_0/__init__.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.
@@ -12,3 +13,10 @@
# 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_service_template_syntax_unsupported(parser):
+ parser.parse_literal("""
+tosca_definitions_version: tosca_simple_yaml_1_0
+unsupported: {}
+""").assert_failure()
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__init__.py
copy to tests/extensions/aria_extension_tosca/simple_v1_0/types/__init__.py
diff --git a/tests/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/common/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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/parser/test_tosca_simple_v1_0/__init__.py b/tests/extensions/aria_extension_tosca/simple_v1_0/types/node_type/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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/test_tosca_simple_v1_0/__init__.py b/tests/mechanisms/__init__.py
similarity index 100%
copy from tests/parser/test_tosca_simple_v1_0/__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 @@ from aria.orchestrator.decorators import workflow
@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 @@ from aria.modeling import (
)
-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 @@ def test_node_operation_task_execution(ctx, thread_executor, dataholder):
)
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 @@ def test_relationship_operation_task_execution(ctx, thread_executor, dataholder)
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 @@ import tests
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 @@ def test_serialize_operation_context(context, executor, tmpdir):
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 @@ class TestWorkflowContext(object):
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 @@ from tests import (
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 @@ if __name__ == '__main__':
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 @@ class TestWithActualSSHServer(object):
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 @@ from aria.orchestrator.workflows.executor import process
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 @@ def test_decorate_extension(context, executor):
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 @@ def test_apply_tracked_changes_during_an_operation(context, executor):
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 @@ def _run_workflow(context, executor, op_func, arguments=None):
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
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 @@ tosca_definitions_version: tosca_simple_profile_for_nfv_1_0
description: >-
Node Cellar TOSCA blueprint.
- Here is some Unicode: 中國.
+ Here is some Unicode: 詠嘆調.
metadata:
template_name: node-cellar
@@ -215,6 +215,10 @@ topology_template:
os_users: # map of os.UserInfo
root:
password: admin123
+ capabilities:
+ scalable:
+ properties:
+ max_instances: 2
interfaces:
Standard:
inputs:
@@ -277,7 +281,7 @@ topology_template:
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 @@ def storage():
@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 @@ def test_mapi_include(context):
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/test_tosca_simple_v1_0/__init__.py b/tests/topology/__init__.py
similarity index 100%
rename from tests/parser/test_tosca_simple_v1_0/__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 @@ def consume_use_case(use_case_name, consumer_class_name='instance', cache=True):
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 @@ def create_context(uri,
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 @@ import pytest
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 @@ def test_version_string():
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 @@ def test_version_string():
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 @@ deps=
tests/requirements.txt
basepython=
core: python2.7
+ extensions: python2.7
e2e: python2.7
ssh: python2.7
windows: {env:PYTHON:}\python.exe
@@ -39,46 +41,59 @@ basepython=
[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} \
+ --numprocesses={[tox]pytest_processes} \
--cov-report term-missing \
--cov aria
+[testenv:ssh]
+install_command=
+ pip install {opts} {packages} .[ssh]
+commands=
+ pytest tests/orchestrator/execution_plugin/test_ssh.py \
+ --numprocesses={[tox]pytest_processes}
+
[testenv:windows]
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:ssh]
-install_command=
- pip install {opts} {packages} .[ssh]
-commands=
- pytest tests/orchestrator/execution_plugin/test_ssh.py \
- --numprocesses={[tox]processes}
-
[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=
--
To stop receiving notification emails like this one, please contact
"commits@ariatosca.apache.org" <co...@ariatosca.apache.org>.