diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index c07286980..000000000 --- a/.coveragerc +++ /dev/null @@ -1,12 +0,0 @@ -[run] -branch = True -source = murano -omit = - .tox/* - murano/tests/* - -[paths] -source = murano - -[report] -ignore_errors = True diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 9e25b4cb8..000000000 --- a/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -#swap file -*.swp - -#IntelJ Idea -.idea/ - -#virtualenv -.venv/ - -#Build results -build/ -dist/ -*.egg-info/ -*.egg -eggs/ -.eggs/ -develop-eggs/ -.tox -AUTHORS -ChangeLog -.testrepository -.coverage -cover -api-ref/build/ - -!/.stestr.conf -.stestr/ - -#Python -*.pyc - -#Translation build -*.mo - -#SQLite Database files -*.sqlite - -#Autogenerated Documentation -doc/source/api -doc/source/_static/murano.conf.sample -doc/source/_static/murano.policy.yaml.sample - -#Config file for functional tests -murano/tests/functional/engine/config.conf - -#Autogenerated sample config file and policy file -etc/murano/murano.conf.sample -etc/murano/murano-cfapi.conf.sample -etc/murano.policy.yaml.sample - -#User Config file for Murano -etc/murano/murano.conf -etc/murano/murano-cfapi.conf -etc/murano/logging.conf - -# pylint autogenerated support files -tools/lintstack.head.py -tools/pylint_exceptions - -# Files created by releasenotes build -releasenotes/build diff --git a/.stestr.conf b/.stestr.conf deleted file mode 100644 index 2e0d10ce8..000000000 --- a/.stestr.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -test_path=${OS_TEST_PATH:-./murano/tests/unit} -top_dir=./ diff --git a/.zuul.yaml b/.zuul.yaml deleted file mode 100644 index 0c1e15e1d..000000000 --- a/.zuul.yaml +++ /dev/null @@ -1,128 +0,0 @@ -- project: - queue: murano - templates: - - check-requirements - - openstack-cover-jobs - - openstack-python3-jobs - - periodic-stable-jobs - - publish-openstack-docs-pti - - release-notes-jobs-python3 - check: - jobs: - - murano-rally-task - - murano-tempest-api - - murano-tempest-cfapi - - murano-grenade - - murano-tempest-api-ipv6-only - gate: - jobs: - - murano-tempest-api - - murano-tempest-api-ipv6-only - -- job: - name: murano-rally-task - voting: false - parent: rally-task-murano - irrelevant-files: &murano-irrelevant-files - - ^(test-|)requirements.txt$ - - ^setup.cfg$ - - ^doc/.*$ - - ^.*\.rst$ - - ^releasenotes/.*$ - - ^murano/tests/.*$ - - ^contrib/.*$ - - ^tools/.*$ - timeout: 7800 - vars: - devstack_plugins: - rally-openstack: https://opendev.org/openstack/rally-openstack - rally_task: rally-jobs/task-murano.yaml - required-projects: - - openstack/rally-openstack - -- job: - name: murano-tempest-base - parent: devstack-tempest - irrelevant-files: *murano-irrelevant-files - timeout: 7800 - required-projects: &base_required_projects - - openstack/heat - - openstack/murano - - openstack/murano-dashboard - - openstack/python-heatclient - - openstack/python-muranoclient - - openstack/tempest - - openstack/murano-tempest-plugin - vars: &base_vars - devstack_plugins: - murano: https://opendev.org/openstack/murano - heat: https://opendev.org/openstack/heat - devstack_services: - tempest: true - s-account: false - s-container: false - s-object: false - s-proxy: false - devstack_localrc: - TEMPEST_PLUGINS: "/opt/stack/murano-tempest-plugin" - KEYSTONE_ADMIN_ENDPOINT: true - tempest_test_regex: application_catalog - tox_envlist: all - -- job: - name: murano-tempest-api - parent: murano-tempest-base - -- job: - name: murano-tempest-api-ipv6-only - parent: devstack-tempest-ipv6 - description: | - Murano devstack tempest tests job for IPv6-only deployment - timeout: 7800 - irrelevant-files: *murano-irrelevant-files - required-projects: *base_required_projects - vars: *base_vars - -- job: - name: murano-tempest-cfapi - parent: murano-tempest-base - voting: false - vars: - devstack_services: - murano-cfapi: true - tempest_test_regex: service_broker - -- job: - name: murano-grenade - parent: grenade - voting: false - irrelevant-files: *murano-irrelevant-files - required-projects: - - opendev.org/openstack/grenade - - opendev.org/openstack/heat - - opendev.org/openstack/murano - - opendev.org/openstack/murano-dashboard - - opendev.org/openstack/python-heatclient - - opendev.org/openstack/python-muranoclient - - opendev.org/openstack/murano-tempest-plugin - - opendev.org/openstack/heat-tempest-plugin - vars: - grenade_localrc: - RUN_HEAT_INTEGRATION_TESTS: False - devstack_plugins: - murano: https://opendev.org/openstack/murano - heat: https://opendev.org/openstack/heat - devstack_services: - tempest: true - s-account: false - s-container: false - s-object: false - s-proxy: false - h-api: true - h-api-cfn: true - h-eng: true - heat: true - tempest_plugins: - - murano-tempest-plugin - tempest_test_regex: ^murano_tempest_tests\.tests\.scenario\.application_catalog\.test_deployment - tox_envlist: all diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index ff79b79c6..000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,19 +0,0 @@ -The source repository for this project can be found at: - - https://opendev.org/openstack/murano - -Pull requests submitted through GitHub are not monitored. - -To start contributing to OpenStack, follow the steps in the contribution guide -to set up and use Gerrit: - - https://docs.openstack.org/contributors/code-and-documentation/quick-start.html - -Bugs should be filed on Launchpad: - - https://bugs.launchpad.net/murano - -For more specific information about contributing to this repository, see the -murano contributor guide: - - https://docs.openstack.org/murano/latest/contributor/contributing.html diff --git a/HACKING.rst b/HACKING.rst deleted file mode 100644 index f28ff60ca..000000000 --- a/HACKING.rst +++ /dev/null @@ -1,15 +0,0 @@ -Style Commandments -================== - -Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ - -Murano Specific Commandments ----------------------------- - -- [M318] Change assertEqual(A, None) or assertEqual(None, A) by optimal assert - like assertIsNone(A) -- [M322] Method's default argument shouldn't be mutable. -- [M323] Python 3: do not use dict.iteritems. -- [M324] Python 3: do not use dict.iterkeys. -- [M325] Python 3: do not use dict.itervalues. -- [M326] Python 3: do not use basestring. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 68c771a09..000000000 --- a/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - diff --git a/README.rst b/README.rst index 57042c436..4ee2c5f13 100644 --- a/README.rst +++ b/README.rst @@ -1,42 +1,10 @@ -======================== -Team and repository tags -======================== +This project is no longer maintained. -.. image:: https://governance.openstack.org/tc/badges/murano.svg - :target: https://governance.openstack.org/tc/reference/tags/index.html +The contents of this repository are still available in the Git +source code management system. To see the contents of this +repository before it reached its end of life, please check out the +previous commit with "git checkout HEAD^1". -.. Change things from this point on - -Murano -====== - -Murano Project introduces an application catalog, which allows application -developers and cloud administrators to publish various cloud-ready -applications in a browsable categorised catalog. Cloud users --- including inexperienced ones -- can then use the catalog to -compose reliable application environments with the push of a button. - - -Project Resources ------------------ - -* `Murano Official Documentation `_ - -* Project status, bugs, and blueprints are tracked on - `Launchpad `_ - -* Additional resources are linked from the project - `Wiki `_ page - -* `Python client `_ - -License -------- - -Apache License Version 2.0 http://www.apache.org/licenses/LICENSE-2.0 - - -Release Notes -------------- - -Release Notes may be found here: https://docs.openstack.org/releasenotes/murano +For any further questions, please email +openstack-discuss@lists.openstack.org or join #openstack-dev on +OFTC. diff --git a/api-ref/source/conf.py b/api-ref/source/conf.py deleted file mode 100644 index eaa197210..000000000 --- a/api-ref/source/conf.py +++ /dev/null @@ -1,208 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Licensed 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. -# -# murano documentation build configuration file, created by -# sphinx-quickstart on Sat May 1 15:17:47 2010. -# -# This file is execfile()d with the current directory set to -# its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import os -import sys - -extensions = [ - 'os_api_ref', - 'openstackdocstheme' -] - - -html_theme = 'openstackdocs' -html_theme_options = { - "sidebar_mode": "toc", -} - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/murano' -openstackdocs_bug_project = 'murano' -openstackdocs_bug_tag = 'api-ref' - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../')) -sys.path.insert(0, os.path.abspath('../')) -sys.path.insert(0, os.path.abspath('./')) - -# -- General configuration ---------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# -# source_encoding = 'utf-8' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -copyright = u'2016-present, OpenStack Foundation' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# The reST default role (used for this markup: `text`) to use -# for all documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -add_module_names = False - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'native' - -# -- Options for man page output ---------------------------------------------- - -# Grouping the document tree for man pages. -# List of tuples 'sourcefile', 'target', u'title', u'Authors name', 'manual' - - -# -- Options for HTML output -------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -# html_theme_path = ["."] -# html_theme = '_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_use_modindex = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'muranodoc' - - -# -- Options for LaTeX output ------------------------------------------------- - -# The paper size ('letter' or 'a4'). -# latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -# latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', 'Murano.tex', u'OpenStack Application Catalog API Documentation', - u'OpenStack Foundation', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -# latex_preamble = '' - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_use_modindex = True diff --git a/api-ref/source/index.rst b/api-ref/source/index.rst deleted file mode 100644 index ec1a689f6..000000000 --- a/api-ref/source/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -================================== -OpenStack Application Catalog APIs -================================== - -.. toctree:: - :maxdepth: 1 - - v1/index diff --git a/api-ref/source/v1/actions.inc b/api-ref/source/v1/actions.inc deleted file mode 100644 index f4ebd9896..000000000 --- a/api-ref/source/v1/actions.inc +++ /dev/null @@ -1,129 +0,0 @@ -.. -*- rst -*- - -========================== -Actions and Static Actions -========================== - -A Murano action is a type of MuranoPL method. The differences between a regular -MuranoPL method are: - -* Action is executed on deployed objects. -* Action execution is initiated by API request: you do not have to call the - method manually. - -Thus, Murano actions allow performing any operations on objects, like: - -* Getting information from the VM, like a config that is generated during the - deployment -* VM rebooting -* Scaling - -A list of available actions is formed during the environment deployment. -Following deployment completion, you can call the action asynchronously. Murano -engine generates a task for every action thereby allowing the action status to -be tracked. - -Execute action -============== - -.. rest_method:: POST /environments/{environment_id}/actions/{action_id} - -Execute action on deployed environment. - -Request Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - environment_id: env_id_url - - action_id: action_id_url - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - task_id: task_id - -Response Example ----------------- - -.. literalinclude:: samples/execute-action-response.json - :language: javascript - -Get Action Result -================= - -.. rest_method:: GET /environments/{environment_id}/actions/{task_id} - -Retrieve action result for action executed on deployed environment. - -Request Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - environment_id: env_id_url - - task_id: task_id_url - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Execute static action -===================== - -.. rest_method:: POST /actions - -Execute static action. - -Static methods can be called if they are exposed by specifying Scope: Public -in the MuranoPL object and the result of its execution will be returned. - -Request Example ---------------- - -.. literalinclude:: samples/static-action-request.json - :language: javascript - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Response Example ----------------- - -.. literalinclude:: samples/static-action-response.json - :language: javascript diff --git a/api-ref/source/v1/categories.inc b/api-ref/source/v1/categories.inc deleted file mode 100644 index 112dbf157..000000000 --- a/api-ref/source/v1/categories.inc +++ /dev/null @@ -1,162 +0,0 @@ -.. -*- rst -*- - -========== -Categories -========== - -In Murano, applications can belong to a category or multiple categories. -Administrative users can create and delete categories as well as list -available categories and view details for a particular category. - -List categories -=============== - -.. rest_method:: GET /catalog/categories - -Retrieve list of all available categories in the Application Catalog. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - categories: all_categories - - id: category_id - - name: category_name - - updated: updated - - created: created - - package_count: package_count - -Response Example ----------------- - -.. literalinclude:: samples/category-list-response.json - :language: javascript - -Show category details -===================== - -.. rest_method:: GET /catalog/categories/{category_id} - -Show details for a category. - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - category_id: category_id_url - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: category_id - - name: category_name - - updated: updated - - created: created - - packages: category_packages - - package_count: package_count - -Response Example ----------------- - -.. literalinclude:: samples/category-show-response.json - :language: javascript - -Create Category -=============== - -.. rest_method:: POST /catalog/categories - -Add a new category to the Application Catalog. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - name: category_name - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: category_id - - name: category_name - - updated: updated - - created: created - - package_count: package_count - -Response Example ----------------- - -.. literalinclude:: samples/category-create-response.json - :language: javascript - -Delete Category -=============== - -.. rest_method:: DELETE /catalog/categories/{category_id} - -Remove an existing category from the Application Catalog. - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - category_id: category_id_url - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 diff --git a/api-ref/source/v1/deployments.inc b/api-ref/source/v1/deployments.inc deleted file mode 100644 index 78e61df52..000000000 --- a/api-ref/source/v1/deployments.inc +++ /dev/null @@ -1,50 +0,0 @@ -.. -*- rst -*- - -=========== -Deployments -=========== - -Deployments track environments that have been deployed, either successfully -or otherwise. Each deployment contains the following information: - -* A "Class: Environment" object (io.murano.Environment) with a name. Each - "Class: Environment" object defines an environment in terms of the deployment - process and groups all Applications and their related infrastructures together. -* An object (or objects) referring to networks that exist. -* A list of Applications (e.g. io.murano.apps.linux.Telnet). Each Application - contains, or otherwise references, anything it requires. The Telnet example - has a property called ``instance`` whose contract states it must be of type - ``io.murano.resources.Instance``. In turn, the Instance has properties it - requires (like a ``name``, a ``flavor``, or a keypair name, ``keyname``). - -List deployments -================ - -.. rest_method:: GET /deployments - -List deployments for all environments for the current tenant (project). - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - deployments: deployments - -Response Example ----------------- - -.. literalinclude:: samples/deployments-list-response.json - :language: javascript diff --git a/api-ref/source/v1/environments.inc b/api-ref/source/v1/environments.inc deleted file mode 100644 index 996c24d8f..000000000 --- a/api-ref/source/v1/environments.inc +++ /dev/null @@ -1,403 +0,0 @@ -.. -*- rst -*- - -============ -Environments -============ - -An environment is a set of logically connected applications that are grouped -together for easy management. By default, each environment has a single -network for all its applications, and the deployment of the environment is -defined in a single heat stack. Applications in different environments are -always independent from one another. - -An environment is a single unit of deployment. This means that you can not only -deploy an environment that contains a single application but an environment -that contains multiple applications. - -List environments -================= - -.. rest_method:: GET /environments - -Get a list of existing Environments - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - all_tenants: all_tenants - - tenant: tenant - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - environments: environments - - status: env_status - - created: created - - updated: updated - - name: env_name - - description_text: env_description - - tenant_id: tenant_id - - version: env_version - - id: env_id - -Response Example ----------------- - -.. literalinclude:: samples/environments-list-response.json - :language: javascript - -Create environment -================== - -.. rest_method:: POST /environments - -Creates an environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - name: env_name_request - -Request Example ---------------- - -.. literalinclude:: samples/environment-create-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: env_id - - name: env_name - - description_text: env_description - - created: created - - updated: updated - - tenant_id: tenant_id - - version: env_version - - services: services - - acquired_by: acquired_by - -Response Example ----------------- - -.. literalinclude:: samples/environment-create-response.json - :language: javascript - -Rename environment -================== - -.. rest_method:: PUT /environments/{env_id} - -Renames an environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - name: env_name_update - -Request Example ---------------- - -.. literalinclude:: samples/environment-update-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: env_id - - name: env_name - - description_text: env_description - - created: created - - updated: updated - - tenant_id: tenant_id - - version: env_version - - services: services - - acquired_by: acquired_by - -Response Example ----------------- - -.. literalinclude:: samples/environment-update-response.json - :language: javascript - -Show environment details -======================== - -.. rest_method:: GET /environments/{env_id} - -Shows details for an environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: env_id - - name: env_name - - description_text: env_description - - created: created - - updated: updated - - tenant_id: tenant_id - - version: env_version - - services: services - - acquired_by: acquired_by - -Response Example ----------------- - -.. literalinclude:: samples/environment-show-response.json - :language: javascript - -Delete environment -================== - -.. rest_method:: DELETE /environments/{env_id} - -Remove specified Environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - abandon: abandon - -Response Parameters -------------------- - -This request does not return anything in the response body. - -Get environment model -===================== - -.. rest_method:: GET /environments/{env_id}/model/{path} - -Get an Environment model. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - path: env_model_path - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - defaultNetworks: env_default_networks - - region: env_region - - regions: regions - - name: env_name - - services: services - - ?: env_model - -Response Example ----------------- - -.. literalinclude:: samples/environments-model-response.json - :language: javascript - -Update environment model -======================== - -.. rest_method:: PATCH /environments/{env_id}/model/ - -Update an environment model. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 202 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - -Request Example ---------------- - -.. literalinclude:: samples/environment-model-update-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - defaultNetworks: env_default_networks - - region: env_region - - regions: regions - - name: env_name - - services: services - - ?: env_model - -Response Example ----------------- - -.. literalinclude:: samples/environments-model-response.json - :language: javascript - -Get environment last status -=========================== - -.. rest_method:: GET /environments/{env_id}/lastStatus - -Get the last status for the environment for each service in the environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - lastStatuses: env_last_status - -Response Example ----------------- - -.. literalinclude:: samples/environment-last-status-response.json - :language: javascript diff --git a/api-ref/source/v1/index.rst b/api-ref/source/v1/index.rst deleted file mode 100644 index 0c985424a..000000000 --- a/api-ref/source/v1/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -:tocdepth: 2 - -#################################### -OpenStack Application Catalog API v1 -#################################### - -.. rest_expand_all:: - -.. include:: actions.inc -.. include:: categories.inc -.. include:: deployments.inc -.. include:: environments.inc -.. include:: packages.inc -.. include:: sessions.inc -.. include:: templates.inc diff --git a/api-ref/source/v1/packages.inc b/api-ref/source/v1/packages.inc deleted file mode 100644 index a904d56fc..000000000 --- a/api-ref/source/v1/packages.inc +++ /dev/null @@ -1,488 +0,0 @@ -.. -*- rst -*- - -======== -Packages -======== - -In Murano, each application, as well as the UI form for application data entry, -is defined by packages. - -Package Structure -================= - -The structure of the Murano application package is predefined. The application package root folder -should contain the following: - -* ``manifest.yaml`` file is the application entry point. - - .. note:: - - The filename is fixed, so do not use any custom names. - -* ``Classes`` folder contains MuranoPL class definitions. - -* ``Resources`` folder contaisn execution plan templates as well as the - ``scripts`` folder with all the files required for an application - deployment located inside it. - -* ``UI`` folder contains the dynamic UI YAML definitions. - -* ``logo.png`` file (optional) is an image file associated with your - application. The logo appears in the Application Catalog within - Murano Dasboard. - - .. note:: - - There are no special limitations regarding an image filename. - However, if it differs from the default ``logo.png``, specify it - in an application manifest file. - -* ``images.lst`` file (optional) contains a list of images required by an - application. - -.. note:: - - A bundle is a collection of packages. In the Community App Catalog, you can - find such bundles as ``container-based-apps``, ``app-servers``, and so on. - The packages in the Application Catalog are sorted by usage. - -List Packages -============= - -.. rest_method:: GET /v1/catalog/packages - -Get a list of packages - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - catalog: catalog - - marker: marker - - limit: limit - - order_by: order_by - - type: pkg_type_query - - category: category - - fqn: fqn - - owned: owned - - id: pkg_id_query - - include_disabled: include_disabled - - search: search - - class_name: class_name - - name: pkg_name_query - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - packages: packages - - updated: updated - - class_definitions: class_definitions - - id: pkg_id - - fully_qualified_name: fully_qualified_name - - is_public: is_public - - name: pkg_name - - type: pkg_type - - supplier: pkg_supplier - - description: description - - author: author - - created: created - - enabled: enabled - - tags: tags - - categories: package_categories - - owner_id: owner_id - -Response Example ----------------- - -.. literalinclude:: samples/packages-list-response.json - :language: javascript - -Upload package -============== - -.. rest_method:: POST /v1/catalog/packages - -Upload a package to the application catalog. - -.. note:: - - Though specifying categories is optional, it is recommended that you - specify at least one. It helps to filter applications in the catalog. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - categories: package_categories - - is_public: is_public - - file: pkg_file - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - updated: updated - - class_definitions: class_definitions - - id: pkg_id - - fully_qualified_name: fully_qualified_name - - is_public: is_public - - name: pkg_name - - type: pkg_type - - supplier: pkg_supplier - - description: description - - author: author - - created: created - - enabled: enabled - - tags: tags - - categories: package_categories - - owner_id: owner_id - -Response Example ----------------- - -.. literalinclude:: samples/package-create-response.json - :language: javascript - -Download package -================ - -.. rest_method:: GET /v1/catalog/packages/{package_id}/download - -Download a package. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Response Parameters -------------------- - -This request does not return anything in the response body. - :language: javascript - -Show package details -==================== - -.. rest_method:: GET /v1/catalog/packages/{package_id} - -Shows details for a package. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - updated: updated - - class_definitions: class_definitions - - id: pkg_id - - fully_qualified_name: fully_qualified_name - - is_public: is_public - - name: pkg_name - - type: pkg_type - - supplier: pkg_supplier - - description: description - - author: author - - created: created - - enabled: enabled - - tags: tags - - categories: package_categories - - owner_id: owner_id - -Response Example ----------------- - -.. literalinclude:: samples/package-show-response.json - :language: javascript - -Update package -============== - -.. rest_method:: PATCH /v1/catalog/packages/{package_id} - -Update a package. - -List of allowed changes:: - - { "op": "add", "path": "/tags", "value": [ "foo", "bar" ] } - { "op": "add", "path": "/categories", "value": [ "foo", "bar" ] } - { "op": "remove", "path": "/tags" } - { "op": "remove", "path": "/categories" } - { "op": "replace", "path": "/tags", "value": ["foo", "bar"] } - { "op": "replace", "path": "/is_public", "value": true } - { "op": "replace", "path": "/description", - "value":"New description" } - { "op": "replace", "path": "/name", "value": "New name" } - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 202 - -.. rest_status_code:: error status.yaml - - - 400 - - 403 - - 404 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Request Example ---------------- - -.. literalinclude:: samples/package-update-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - updated: updated - - class_definitions: class_definitions - - id: pkg_id - - fully_qualified_name: fully_qualified_name - - is_public: is_public - - name: pkg_name - - type: pkg_type - - supplier: pkg_supplier - - description: description - - author: author - - created: created - - enabled: enabled - - tags: tags - - categories: package_categories - - owner_id: owner_id - -Response Example ----------------- - -.. literalinclude:: samples/package-update-response.json - -Delete package -============== - -.. rest_method:: DELETE /v1/catalog/packages/{package_id} - -Remove specified Environment. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Response Parameters -------------------- - -This request does not return anything in the response body. - :language: javascript - -Search for packages -=================== - -.. rest_method:: GET /v1/catalog/packages - -Search for packages in application catalog. Non-admins, by default, can view -packages that belong to their project as well as public packages: packages -which belong to other projects but which have been tagged as public by an -admin. Admins can search for packages across all projects. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - filters: pkg_filters - -Response Parameters -------------------- - -Returns the list of packages matching the search criteria. - -Get UI definition -================= - -.. rest_method:: GET /v1/catalog/packages/{package_id}/ui - -Retrieve UI definition for an application. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Response Parameters -------------------- - -Returns the entire UI definition for the package, if the logo has a -UI definition. - -Below is an example of a very basic UI definition:: - - Version: 2.2 - - Forms: - - appConfiguration: - fields: - - name: license - type: string - description: Apache License, Version 2.0 - hidden: false - required: false - -Get logo -======== - -.. rest_method:: GET /v1/catalog/packages/{package_id}/logo - -Retrieve application logo. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 400 - - 401 - - 403 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - package_id: pkg_id_url - -Response Parameters -------------------- - -Returns the binary logo data for the package, if the package has a logo. diff --git a/api-ref/source/v1/parameters.yaml b/api-ref/source/v1/parameters.yaml deleted file mode 100644 index 4c6d0f472..000000000 --- a/api-ref/source/v1/parameters.yaml +++ /dev/null @@ -1,660 +0,0 @@ -# variables in header -request_id: - description: | - A unique ID for tracking service request. The request ID associated - with the request by default appears in the service logs. - in: header - required: true - type: UUID - -# variables in path -abandon: - description: | - Indicates how to delete environment. - ``True`` is used when just database must be cleaned. - ``False`` is used if all resources used by environment must be destroyed. - in: path - required: false - default: false - type: boolean -action_id_url: - description: | - The UUID of the action to be executed on the deployed environment. - in: path - required: true - type: string -category_id_url: - description: | - The UUID of the category. - in: path - required: true - type: string -env_id_url: - description: | - The UUID of the environment. - in: path - required: true - type: string -env_model_path: - description: | - Allows to get a specific section of the model, for example - ``defaultNetworks``, ``region`` or ``?`` or any of the subsections. - in: path - required: false - type: string -env_name_update: - description: | - A name for the environment. Name must be at least - one non-white space symbol. - in: path - required: true - type: string -pkg_id_url: - description: | - The UUID of the package. - in: path - required: true - type: string -service_id_url: - description: - The UUID of a service belonging to an environment template. - in: path - required: true - type: string -session_id_url: - description: | - The UUID of the session. - in: path - required: true - type: string -task_id_url: - description: | - The UUID of the task associated with an action executed on a deployed - environment. - in: path - required: true - type: string -template_id_url: - description: | - The UUID of the environment template. - in: path - required: true - type: string -template_is_public_url: - description: | - Indicates whether public environment templates are listed or not. The - following options are possible: - - - ``True``. Public environments templates from all projects are listed. - - ``False``. Private environments templates from current project are - listed. - - ``empty``. All project templates plus public templates from all projects. - are listed - in: path - required: false - default: false - type: boolean - -# variables in query -all_tenants: - description: | - Indicates whether environments from all projects are listed. - ``True`` environments from all projects are listed. Admin user required. - ``False`` environments only from current project are listed (default like - option unspecified). - in: query - required: false - default: false - type: boolean -catalog: - description: | - If ``false`` (default) - search packages, that current user can edit - (own for non-admin, all for admin). If ``true`` - search packages, - that current user can deploy (i.e. his own + public). - in: query - required: false - default: false - type: boolean -category: - description: | - Allows to filter by categories. - in: query - required: false - type: string -class_name: - description: | - Search only for packages, that use specified class. - in: query - required: false - type: string -fqn: - description: | - Allows to filter by fully qualified name. - in: query - required: false - type: string -include_disabled: - description: | - Include disabled packages in the result. - in: query - required: false - default: false - type: boolean -limit: - description: | - When present the maximum number of results returned will not exceed - the specified value. The typical pattern of limit and marker is to - make an initial limited request and then to use the ID of the last - package from the response as the marker parameter in a - subsequent limited request. - in: query - required: false - type: string -marker: - description: | - A package identifier marker may be specified. When present only - packages which occur after the identifier ID will be listed - in: query - required: false - type: string -order_by: - description: | - Allows to sort packages by ``fqn``, ``name``, ``created``. - Created is default value. - in: query - required: false - type: string -owned: - description: | - Search only from packages owned by current project. - in: query - required: false - default: false - type: boolean -pkg_filters: - description: | - The filters that you want to use to search for packages in the - application catalog. If no filters query parameter is specified, the - application catalog API returns all packages allowed by the policy - settings. By using filters parameter, the API returns only the requested - set of packages that meet the filters. The list of filters includes: - - * limit: the maximum number of packages to return - * type: the package type - * id: the package id - * category: the package category - * tag: the package tag - * class_name: the package class name - * fqn: the package fully qualified name - * name: the package name - in: query - required: false - type: string -pkg_id_query: - description: | - Allows to filter by package id. - in: query - required: false - type: string -pkg_name_query: - description: | - Allows to filter by package name. - in: query - required: false - type: string -pkg_type_query: - description: | - Allows to filter package by type, e.g. ``application``, ``library``. - in: query - required: false - type: string -search: - description: | - Gives opportunity to search specified data by all the - package parameters and order packages. - in: query - required: false - type: string -tenant: - description: | - Indicates environments from specified tenant are listed. Admin user required. - in: query - required: false - type: string - -# variables in body -acquired_by: - description: | - The session that is currently `deploying` the environment. Returns the - `first` session id that is in ``DEPLOYING`` state for the environment. - in: body - required: true - type: string -all_categories: - description: | - All categories available in the application catalog. - in: body - required: true - type: array -author: - description: | - The author of the package. - in: body - required: true - type: string -category_id: - description: | - The UUID of the category. - in: body - required: true - type: string -category_name: - description: | - The name of the category. - in: body - required: true - type: string -category_packages: - description: | - The list of packages associated with a package. Each package returned - includes its ``id``, ``fully_qualified_name``, and ``name``. - in: body - required: true - type: array -class_definitions: - description: | - The class_definitions of the package. - in: body - required: true - type: array -created: - description: | - The date and time when the resource was created. The date and time stamp - format is `ISO 8601 `_: - - :: - - CCYY-MM-DDThh:mm:ss±hh:mm - - For example, ``2015-08-27T09:49:58-05:00``. - - The ``±hh:mm`` value, if included, is the time zone as an offset - from UTC. - in: body - required: true - type: string -deployments: - description: | - The list of deployments for either the current environment or all - environments for the current tenant (project). - - The following APIs control whether deployments by environment or by - project are returned: - - * ``/deployments``: Returns all deployments for a project. - * ``/environments/{env_id}/deployments``: Returns all deployments for an - environment in a project. - in: body - required: true - type: array -description: - description: | - The description of the package. - in: body - required: true - type: string -enabled: - description: | - Whether the package is browsed in the Application Catalog. - in: body - required: true - type: boolean -env_default_networks: - description: | - The default networking information of the environment. The information - includes the ``name`` of the network, along with the ``type`` and ``id`` - of the network, contained in the ``?`` property. - - An example ``defaultNetworks`` object looks like:: - - "defaultNetworks": { - "environment": { - "internalNetworkName": "net_two", - "?": { - "type": "io.murano.resources.ExistingNeutronNetwork", - "id": "594e94fcfe4c48ef8f9b55edb3b9f177" - } - }, - "flat": null - } - in: body - required: true - type: object -env_description: - description: | - The description of the environment. - in: body - required: true - type: string -env_id: - description: | - The UUID of the environment. - in: body - required: true - type: string -env_last_status: - description: | - Shows the most recent status of the environment for each service in the - environment. The response object includes detailed information - by ``service_id``. - in: body - required: true - type: object -env_model: - description: | - The ``?`` section of the environment, containing information about the - environment model, including its ``type``, ``id`` and associated - ``metadata``. - in: body - required: true - type: object -env_name: - description: | - A name for the environment. Name must be at least one non-white space - symbol and less than 256 characters long. - in: body - required: true - type: string -env_name_request: - description: | - A name for the environment. Name must be at least - one non-white space symbol. - in: body - required: true - type: string -env_region: - description: | - Current region of the environment. - in: body - required: true - type: string -env_status: - description: | - Current status of the environment. The available statuses are: - - * **Ready to configure**. When the environment is new and contains no - components. - * **Ready to deploy**. When the environment contains a component or multiple - components and is ready for deployment. - * **Ready**. When the environment has been successfully deployed. - * **Deploying**. When the deploying is in progress. - * **Deploy FAILURE**. When the deployment finished with errors. - * **Deleting**. When deleting of an environment is in progress. - * **Delete FAILURE**. You can abandon the environment in this case. - in: body - required: true - type: string -env_version: - description: | - Current version. - in: body - required: true - type: int -environments: - description: | - A list of ``environment`` object. - in: body - required: true - type: array -fully_qualified_name: - description: | - The fqn of the package. - in: body - required: true - type: string -is_public: - description: | - Whether the package is shared for other projects. - in: body - required: true - type: boolean -networking: - description: | - Current network of the environment. - in: body - required: true - type: string -owner_id: - description: | - The owner id of the package. - in: body - required: true - type: string -package_categories: - description: | - The categories associated with the package. - in: body - required: true - type: array -package_count: - description: | - The number of packages associated with the category. - in: body - required: true - type: integer -packages: - description: | - A list of ``package`` object. - in: body - required: true - type: array -pkg_file: - description: | - The upload package file. - in: body - required: true - type: object -pkg_id: - description: | - The UUID of the package. - in: body - required: true - type: string -pkg_name: - description: | - The name of the package. - in: body - required: true - type: string -pkg_supplier: - description: | - The supplier info of the package. - in: body - required: true - type: object -pkg_type: - description: | - The type of the package. - in: body - required: true - type: string -regions: - description: | - Detailed region information for the cloud environment. - in: body - required: true - type: object -services: - description: | - A list of ``service`` objects. - in: body - required: true - type: array -session_id: - description: | - The UUID of the session. - in: body - required: true - type: string -session_state: - description: | - The current state of the environment. When a session is first - opened for the environment the state is ``opened``. - in: body - required: true - type: string -session_user_id: - description: | - The UUID of the session owner. - in: body - required: true - type: string -session_version: - description: | - The version of the session. It is tied to the version of the environment, - so that only sessions whose version matches that of the environment can - be deployed. - in: body - required: true - type: integer -tags: - description: | - The tags of the package. - in: body - required: true - type: array -task_id: - description: | - The UUID of the task associated with an action executed on a deployed - environment. - in: body - required: true - type: string -template_description: - description: | - The environment template description. - in: body - required: true - type: string -template_id: - description: | - The UUID of the environment template. - in: body - required: true - type: string -template_is_public: - description: | - Indicates whether an environment template is public or not. - - - ``True``. The environment template is public. Can be cloned. - - ``False``. The environment template is private. - in: body - required: true - type: boolean -template_name: - description: | - The name of the environment template. Only alphanumeric characters are - allowed. - in: body - required: true - type: string -template_service: - description: | - Detailed information about the ``service`` to be added to the environment - template. The ``service`` includes virtual resources and application - information. The virtual resources information is specified inside the - ``instance`` object property. Application information is specified - inside the body of the ``service`` object. - - The ``instance`` object properties include: - - - ``assignFloatingIp``. Whether to assign a floating IP to the VM. - - ``keyname``. The key name of a key pair for the VM. - - ``image``. The image to be used to provision the VM. - - ``flavor``. The flavor to be used to provision the VM. - - ``?``. An object which includes the ``type`` of the server. - - An example ``instance`` looks like:: - - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - } - - In addition, the ``service`` should also include the following: - - - ``name``. The ``name`` of the application. - - ``?``. An object that includes the ``type`` and ``id`` of the - application. An example ``type`` is: - "io.murano.resources.LinuxMuranoInstance". - - ``port``: The port to be used by the application. The value must be - greater than 0 and less than 65536 (although formatted as a string). - - The entire ``service`` looks like:: - - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - } - in: body - required: true - type: object -template_services: - description: | - The list of environment template ``service`` objects. - in: body - required: true - type: array -template_version: - description: | - The current version of the environment template. - in: body - required: true - type: integer -templates: - description: | - The list of templates. - in: body - required: true - type: array -tenant_id: - description: | - The UUID of the tenant. A tenant is also known as a project. - in: body - required: true - type: string -updated: - description: | - The date and time when the object was updated. The date and time stamp - format is `ISO 8601 `_: - - :: - - CCYY-MM-DDThh:mm:ss±hh:mm - - For example, ``2015-08-27T09:49:58-05:00``. - - The ``±hh:mm`` value, if included, is the time zone as an offset from UTC. - in: body - required: true - type: string diff --git a/api-ref/source/v1/samples/category-create-response.json b/api-ref/source/v1/samples/category-create-response.json deleted file mode 100644 index 853e40baa..000000000 --- a/api-ref/source/v1/samples/category-create-response.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "ce373a477f211e187a55404a662f968", - "name": "category_name", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:44Z", - "package_count": 0 -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/category-list-response.json b/api-ref/source/v1/samples/category-list-response.json deleted file mode 100644 index 5eebb8b3c..000000000 --- a/api-ref/source/v1/samples/category-list-response.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "categories": [ - { - "id": "0420045dce7445fabae7e5e61fff9e2f", - "updated": "2014-12-26T13:57:04", - "name": "Web", - "created": "2014-12-26T13:57:04", - "package_count": 1 - }, - { - "id": "3dd486b1e26f40ac8f35416b63f52042", - "updated": "2014-12-26T13:57:04", - "name": "Databases", - "created": "2014-12-26T13:57:04", - "package_count": 0 - } - ] -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/category-show-response.json b/api-ref/source/v1/samples/category-show-response.json deleted file mode 100644 index eb69273b4..000000000 --- a/api-ref/source/v1/samples/category-show-response.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "id": "b308f7fa8a2f4a5eb419970c827f4466", - "updated": "2015-01-28T17:00:19", - "packages": [ - { - "fully_qualified_name": "io.murano.apps.ZabbixServer", - "id": "4dfb566e69e6445fbd4aea5099fe95e9", - "name": "Zabbix Server" - } - ], - "name": "Web", - "created": "2015-01-28T17:00:19", - "package_count": 1 -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/deployments-list-response.json b/api-ref/source/v1/samples/deployments-list-response.json deleted file mode 100644 index f18f121f7..000000000 --- a/api-ref/source/v1/samples/deployments-list-response.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "deployments": [ - { - "updated": "2014-05-15T07:24:21", - "environment_id": "744e44812da84e858946f5d817de4f72", - "description": { - "services": [ - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "?": { - "type": "io.murano.resources.Instance", - "id": "ef729199-c71e-4a4c-a314-0340e279add8" - }, - "name": "xkaduhv7qeg4m7" - }, - "name": "teslnet1", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "6e437be2-b5bc-4263-8814-6fd57d6ddbd5" - } - } - ], - "defaultNetworks": { - "environment": { - "name": "test2-network", - "?": { - "type": "io.murano.lib.networks.neutron.NewNetwork", - "id": "b6a1d515434047d5b4678a803646d556" - } - }, - "flat": null - }, - "name": "test2", - "?": { - "type": "io.murano.Environment", - "id": "744e44812da84e858946f5d817de4f72" - } - }, - "created": "2014-05-15T07:24:21", - "started": "2014-05-15T07:24:21", - "finished": null, - "state": "running", - "id": "327c81e0e34a4c93ad9b9052ef42b752" - } - ] -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/environment-create-request.json b/api-ref/source/v1/samples/environment-create-request.json deleted file mode 100644 index 77fda7b0c..000000000 --- a/api-ref/source/v1/samples/environment-create-request.json +++ /dev/null @@ -1 +0,0 @@ -{"name": "env_name"} diff --git a/api-ref/source/v1/samples/environment-create-response.json b/api-ref/source/v1/samples/environment-create-response.json deleted file mode 100644 index 1b5ba0629..000000000 --- a/api-ref/source/v1/samples/environment-create-response.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "status": "ready", - "updated": "2017-04-27T15:36:02", - "created": "2017-04-27T15:36:02", - "tenant_id": "cca37eef752244d99945a4123f30ff79", - "acquired_by": null, - "services": [], - "version": 0, - "description_text": "", - "id": "a2977db57398401aba5804ef2211a2a3", - "name": "env_name" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/environment-last-status-response.json b/api-ref/source/v1/samples/environment-last-status-response.json deleted file mode 100644 index 80564f971..000000000 --- a/api-ref/source/v1/samples/environment-last-status-response.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "lastStatuses": { - "66563e45-4d0a-451e-8138-7bc773b0607d": { - "updated": "2017-03-09T07:31:51", - "task_id": "1267d8dfcf2144f9a31f0f033defa0fd", - "level": "info", - "text": "Unable to install ApacheHttpServer on node-1 due to The murano-agent did not respond within 3600 seconds", - "created": "2017-03-09T07:31:51", - "entity_id": "66563e45-4d0a-451e-8138-7bc773b0607d", - "entity": null, - "details": null, - "id": "4f93ae1f73294bf1a58cbc59fffe6238" - } - } -} diff --git a/api-ref/source/v1/samples/environment-model-update-request.json b/api-ref/source/v1/samples/environment-model-update-request.json deleted file mode 100644 index 013bdf862..000000000 --- a/api-ref/source/v1/samples/environment-model-update-request.json +++ /dev/null @@ -1,5 +0,0 @@ -[{ - "op": "replace", - "path": "/defaultNetworks/flat", - "value": true -}] diff --git a/api-ref/source/v1/samples/environment-show-response.json b/api-ref/source/v1/samples/environment-show-response.json deleted file mode 100644 index c6d04c178..000000000 --- a/api-ref/source/v1/samples/environment-show-response.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "status": "ready", - "updated": "2017-04-27T15:36:02", - "created": "2017-04-27T15:36:02", - "tenant_id": "cca37eef752244d99945a4123f30ff79", - "acquired_by": null, - "services": [ - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "name": "exgchhv6nbika2", - "ipAddresses": [ - "10.0.0.200" - ], - "?": { - "type": "io.murano.resources.Instance", - "id": "14cce9d9-aaa1-4f09-84a9-c4bb859edaff" - } - }, - "name": "rewt4w56", - "?": { - "status": "ready", - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "446373ef-03b5-4925-b095-6c56568fa518" - } - } - ], - "version": 0, - "description_text": "", - "id": "a2977db57398401aba5804ef2211a2a3", - "name": "env_name" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/environment-update-request.json b/api-ref/source/v1/samples/environment-update-request.json deleted file mode 100644 index c691c9c35..000000000 --- a/api-ref/source/v1/samples/environment-update-request.json +++ /dev/null @@ -1 +0,0 @@ -{"name": "env_name_changed"} diff --git a/api-ref/source/v1/samples/environment-update-response.json b/api-ref/source/v1/samples/environment-update-response.json deleted file mode 100644 index a88132ac9..000000000 --- a/api-ref/source/v1/samples/environment-update-response.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "status": "ready", - "updated": "2017-04-27T16:01:29", - "created": "2017-04-27T15:33:55", - "tenant_id": "cca37eef752244d99945a4123f30ff79", - "acquired_by": null, - "services": [], - "version": 0, - "description_text": "", - "id": "f199275420ff4e938e0307b0cf68374d", - "name": "env_name_changed" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/environments-list-response.json b/api-ref/source/v1/samples/environments-list-response.json deleted file mode 100644 index e28bc1bde..000000000 --- a/api-ref/source/v1/samples/environments-list-response.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "environments": [ - { - "status": "ready", - "updated": "2014-05-14T13:02:54", - "networking": {}, - "name": "test1", - "created": "2014-05-14T13:02:46", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "id": "2fa5ab704749444bbeafe7991b412c33" - }, - { - "status": "ready", - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "id": "744e44812da84e858946f5d817de4f72" - } - ] -} diff --git a/api-ref/source/v1/samples/environments-model-response.json b/api-ref/source/v1/samples/environments-model-response.json deleted file mode 100644 index 835b15e92..000000000 --- a/api-ref/source/v1/samples/environments-model-response.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "defaultNetworks": { - "environment": { - "internalNetworkName": "net_two", - "?": { - "type": "io.murano.resources.ExistingNeutronNetwork", - "id": "594e94fcfe4c48ef8f9b55edb3b9f177" - } - }, - "flat": null - }, - "region": "RegionTwo", - "name": "new_env", - "regions": { - "": { - "defaultNetworks": { - "environment": { - "autoUplink": true, - "name": "new_env-network", - "externalRouterId": null, - "dnsNameservers": [], - "autogenerateSubnet": true, - "subnetCidr": null, - "openstackId": null, - "?": { - "dependencies": { - "onDestruction": [{ - "subscriber": "c80e33dd67a44f489b2f04818b72f404", - "handler": null - }] - }, - "type": "io.murano.resources.NeutronNetwork/0.0.0@io.murano", - "id": "e145b50623c04a68956e3e656a0568d3", - "name": null - }, - "regionName": "RegionOne" - }, - "flat": null - }, - "name": "RegionOne", - "?": { - "type": "io.murano.CloudRegion/0.0.0@io.murano", - "id": "c80e33dd67a44f489b2f04818b72f404", - "name": null - } - }, - "RegionOne": "c80e33dd67a44f489b2f04818b72f404", - "RegionTwo": { - "defaultNetworks": { - "environment": { - "autoUplink": true, - "name": "new_env-network", - "externalRouterId": "e449bdd5-228c-4747-a925-18cda80fbd6b", - "dnsNameservers": ["8.8.8.8"], - "autogenerateSubnet": true, - "subnetCidr": "10.0.198.0/24", - "openstackId": "00a695c1-60ff-42ec-acb9-b916165413da", - "?": { - "dependencies": { - "onDestruction": [{ - "subscriber": "f8cb28d147914850978edb35eca156e1", - "handler": null - }] - }, - "type": "io.murano.resources.NeutronNetwork/0.0.0@io.murano", - "id": "72d2c13c600247c98e09e2e3c1cd9d70", - "name": null - }, - "regionName": "RegionTwo" - }, - "flat": null - }, - "name": "RegionTwo", - "?": { - "type": "io.murano.CloudRegion/0.0.0@io.murano", - "id": "f8cb28d147914850978edb35eca156e1", - "name": null - } - } - }, - "services": [], - "?": { - "type": "io.murano.Environment/0.0.0@io.murano", - "_actions": { - "f7f22c174070455c9cafc59391402bdc_deploy": { - "enabled": true, - "name": "deploy", - "title": "deploy" - } - }, - "id": "f7f22c174070455c9cafc59391402bdc", - "name": null - } -} diff --git a/api-ref/source/v1/samples/execute-action-response.json b/api-ref/source/v1/samples/execute-action-response.json deleted file mode 100644 index e351286e1..000000000 --- a/api-ref/source/v1/samples/execute-action-response.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "task_id": "9e60318629ef47378b583825e7d282b7" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/package-create-response.json b/api-ref/source/v1/samples/package-create-response.json deleted file mode 100644 index ae63cdd61..000000000 --- a/api-ref/source/v1/samples/package-create-response.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "class_definitions": [ - "com.example.apache.ApacheHttpServer" - ], - "description": "The Apache HTTP Server Project is an effort to develop and maintain an\nopen-source HTTP server for modern operating systems including UNIX and\nWindows NT. The goal of this project is to provide a secure, efficient and\nextensible server that provides HTTP services in sync with the current HTTP\nstandards.\nApache httpd has been the most popular web server on the Internet since\nApril 1996, and celebrated its 17th birthday as a project this February.\n", - "tags": [ - "HTTP", - "Server", - "WebServer", - "HTML", - "Apache" - ], - "updated": "2017-04-06T07:54:40", - "is_public": false, - "id": "10f3e349bca9432abd673319195eed2b", - "categories": [], - "name": "Apache HTTP Server", - "created": "2017-04-06T07:54:40", - "author": "Mirantis, Inc", - "enabled": true, - "supplier": {}, - "fully_qualified_name": "com.example.apache.ApacheHttpServer", - "type": "Application", - "owner_id": "c0f6e4cf1bfc48aba587e709b58c9f28" -} diff --git a/api-ref/source/v1/samples/package-show-response.json b/api-ref/source/v1/samples/package-show-response.json deleted file mode 100644 index e4ff8c530..000000000 --- a/api-ref/source/v1/samples/package-show-response.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "updated": "2017-04-06T08:22:11", - "description": "The Apache HTTP Server Project is an effort to develop and maintain an\nopen-source HTTP server for modern operating systems including UNIX and\nWindows NT. The goal of this project is to provide a secure, efficient and\nextensible server that provides HTTP services in sync with the current HTTP\nstandards.\nApache httpd has been the most popular web server on the Internet since\nApril 1996, and celebrated its 17th birthday as a project this February.\n", - "tags": [ - "HTTP", - "Server", - "WebServer", - "HTML", - "Apache" - ], - "class_definitions": [ - "com.example.apache.ApacheHttpServer" - ], - "is_public": false, - "categories": [], - "name": "Apache HTTP Server", - "created": "2017-04-06T08:22:11", - "author": "Mirantis, Inc", - "enabled": true, - "id": "979637f39a7245cebeabc99e6aa01666", - "supplier": {}, - "fully_qualified_name": "com.example.apache.ApacheHttpServer", - "type": "Application", - "owner_id": "c0f6e4cf1bfc48aba587e709b58c9f28" -} diff --git a/api-ref/source/v1/samples/package-update-request.json b/api-ref/source/v1/samples/package-update-request.json deleted file mode 100644 index 33fb203ae..000000000 --- a/api-ref/source/v1/samples/package-update-request.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "path": "/is_public", - "value": true, - "op": "replace" - } -] diff --git a/api-ref/source/v1/samples/package-update-response.json b/api-ref/source/v1/samples/package-update-response.json deleted file mode 100644 index 912e41636..000000000 --- a/api-ref/source/v1/samples/package-update-response.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "updated": "2017-04-06T08:28:22", - "description": "The Apache HTTP Server Project is an effort to develop and maintain an\nopen-source HTTP server for modern operating systems including UNIX and\nWindows NT. The goal of this project is to provide a secure, efficient and\nextensible server that provides HTTP services in sync with the current HTTP\nstandards.\nApache httpd has been the most popular web server on the Internet since\nApril 1996, and celebrated its 17th birthday as a project this February.\n", - "tags": [ - "HTTP", - "Server", - "WebServer", - "HTML", - "Apache" - ], - "class_definitions": [ - "com.example.apache.ApacheHttpServer" - ], - "is_public": true, - "categories": [], - "name": "Apache HTTP Server", - "created": "2017-04-06T08:22:11", - "author": "Mirantis, Inc", - "enabled": true, - "id": "979637f39a7245cebeabc99e6aa01666", - "supplier": {}, - "fully_qualified_name": "com.example.apache.ApacheHttpServer", - "type": "Application", - "owner_id": "c0f6e4cf1bfc48aba587e709b58c9f28" -} diff --git a/api-ref/source/v1/samples/packages-list-response.json b/api-ref/source/v1/samples/packages-list-response.json deleted file mode 100644 index 8f9d9cdc5..000000000 --- a/api-ref/source/v1/samples/packages-list-response.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "packages": [ - { - "updated": "2017-03-30T08:35:03", - "description": "Library of base class to develop scalable Applications with MuranoPL\n", - "tags": [], - "class_definitions": [ - "io.murano.applications.tests.TestPoolReplicaProvider", - "io.murano.applications.SingleServerApplication", - "io.murano.applications.tests.TestSoftwareComponent", - "io.murano.applications.SoftwareComponent", - "io.murano.applications.tests.TestEvents", - "io.murano.applications.CloneReplicaProvider", - "io.murano.applications.PoolReplicaProvider", - "io.murano.applications.Event", - "io.murano.applications.SingleServerGroup", - "io.murano.applications.TemplateServerProvider", - "io.murano.applications.MultiServerApplication", - "io.murano.applications.ReplicationGroup", - "io.murano.applications.OpenStackSecurityConfigurable", - "io.murano.applications.Configurable", - "io.murano.applications.tests.TestMockedServerFactory", - "io.murano.applications.tests.TestCompositeReplicaProvider", - "io.murano.applications.tests.TestRoundrobinReplicaProvider", - "io.murano.applications.ServerReplicationGroup", - "io.murano.applications.CompositeReplicaProvider", - "io.murano.applications.tests.TestReplication", - "io.murano.applications.CompositeServerGroup", - "io.murano.applications.RoundrobinReplicaProvider", - "io.murano.applications.ServerGroup", - "io.murano.applications.ServerList", - "io.murano.applications.Installable", - "io.murano.applications.ReplicaProvider", - "io.murano.applications.MultiServerApplicationWithScaling" - ], - "is_public": true, - "categories": [], - "name": "Application Development Library", - "created": "2017-03-30T08:35:03", - "author": "Mirantis, Inc.", - "enabled": true, - "id": "b0298c205235410fba047f4af8df0eb0", - "supplier": {}, - "fully_qualified_name": "io.murano.applications", - "type": "Library", - "owner_id": "c0f6e4cf1bfc48aba587e709b58c9f28" - }, - { - "updated": "2017-03-30T08:35:07", - "description": "Core MuranoPL library\n", - "tags": [ - "MuranoPL" - ], - "class_definitions": [ - "io.murano.Exception", - "io.murano.system.MetadefBrowser", - "io.murano.metadata.forms.Hidden", - "io.murano.system.NeutronSecurityGroupManager", - "io.murano.system.AgentListener", - "io.murano.Environment", - "io.murano.system.SecurityGroupManager", - "io.murano.resources.ConfLangInstance", - "io.murano.resources.HeatSWConfigLinuxInstance", - "io.murano.test.TestFixture", - "io.murano.resources.MetadataAware", - "io.murano.SharedIp", - "io.murano.File", - "io.murano.resources.LinuxUDInstance", - "io.murano.configuration.Linux", - "io.murano.resources.ExistingNeutronNetwork", - "io.murano.resources.LinuxMuranoInstance", - "io.murano.Object", - "io.murano.system.Logger", - "io.murano.metadata.engine.Synchronize", - "io.murano.test.DummyNetwork", - "io.murano.resources.CinderVolume", - "io.murano.metadata.Title", - "io.murano.Project", - "io.murano.system.Resources", - "io.murano.metadata.forms.Section", - "io.murano.resources.Network", - "io.murano.system.MistralClient", - "io.murano.resources.CinderVolumeBackup", - "io.murano.system.NetworkExplorer", - "io.murano.system.DummySecurityGroupManager", - "io.murano.resources.WindowsInstance", - "io.murano.CloudResource", - "io.murano.CloudRegion", - "io.murano.system.Agent", - "io.murano.resources.Instance", - "io.murano.resources.Volume", - "io.murano.system.InstanceNotifier", - "io.murano.metadata.ModelBuilder", - "io.murano.system.HeatStack", - "io.murano.resources.LinuxInstance", - "io.murano.metadata.Description", - "io.murano.metadata.engine.Serialize", - "io.murano.resources.ExistingCinderVolume", - "io.murano.resources.HeatSWConfigInstance", - "io.murano.system.StatusReporter", - "io.murano.Application", - "io.murano.test.TestFixtureWithEnvironment", - "io.murano.system.AwsSecurityGroupManager", - "io.murano.StackTrace", - "io.murano.resources.NovaNetwork", - "io.murano.metadata.forms.Position", - "io.murano.metadata.HelpText", - "io.murano.resources.NeutronNetworkBase", - "io.murano.User", - "io.murano.resources.InstanceAffinityGroup", - "io.murano.resources.NeutronNetwork", - "io.murano.resources.CinderVolumeSnapshot" - ], - "is_public": true, - "categories": [], - "name": "Core library", - "created": "2017-03-30T08:35:07", - "author": "murano.io", - "enabled": true, - "id": "5b6c8d7cd0694a7ebb7525ae62357740", - "supplier": {}, - "fully_qualified_name": "io.murano", - "type": "Library", - "owner_id": "c0f6e4cf1bfc48aba587e709b58c9f28" - } - ] -} diff --git a/api-ref/source/v1/samples/session-create-response.json b/api-ref/source/v1/samples/session-create-response.json deleted file mode 100644 index 0fd944de0..000000000 --- a/api-ref/source/v1/samples/session-create-response.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "created": "2017-04-06T07:54:40", - "updated": "2017-04-06T07:54:40", - "environment_id": "744e44812da84e858946f5d817de4f72", - "state": "opened", - "version": 0, - "id": "257bef44a9d848daa5b2563779714820" - } \ No newline at end of file diff --git a/api-ref/source/v1/samples/session-show-response.json b/api-ref/source/v1/samples/session-show-response.json deleted file mode 100644 index 4680e9638..000000000 --- a/api-ref/source/v1/samples/session-show-response.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "version": 0, - "state": "deploying" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/static-action-request.json b/api-ref/source/v1/samples/static-action-request.json deleted file mode 100644 index fa8e1fd0a..000000000 --- a/api-ref/source/v1/samples/static-action-request.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "className": "ns.Bar", - "methodName": "staticAction", - "parameters": {"myName": "John"} -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/static-action-response.json b/api-ref/source/v1/samples/static-action-response.json deleted file mode 100644 index 2414fae54..000000000 --- a/api-ref/source/v1/samples/static-action-response.json +++ /dev/null @@ -1 +0,0 @@ -"Hello, John" \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-add-app-request.json b/api-ref/source/v1/samples/template-add-app-request.json deleted file mode 100644 index 37c9e1582..000000000 --- a/api-ref/source/v1/samples/template-add-app-request.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-add-app-response.json b/api-ref/source/v1/samples/template-add-app-response.json deleted file mode 100644 index 22a8e77e5..000000000 --- a/api-ref/source/v1/samples/template-add-app-response.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "updated": "2017-04-26T19:41:58", - "created": "2017-04-26T19:33:10", - "tenant_id": "cca37eef752244d99945a4123f30ff79", - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - } - ], - "version": 0, - "description_text": "", - "is_public": false, - "id": "64670f5ada0848408734b2985f5cbb92", - "name": "test_application" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-clone-request.json b/api-ref/source/v1/samples/template-clone-request.json deleted file mode 100644 index 4da52938f..000000000 --- a/api-ref/source/v1/samples/template-clone-request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "cloned_env_template_name" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-clone-response.json b/api-ref/source/v1/samples/template-clone-response.json deleted file mode 100644 index b107d9676..000000000 --- a/api-ref/source/v1/samples/template-clone-response.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "updated": "2015-01-26T09:12:51", - "name": "cloned_env_template_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "is_public": false, - "id": "aa9033ca7ce245fca10e38e1c8c4bbf7", -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-create-env-request.json b/api-ref/source/v1/samples/template-create-env-request.json deleted file mode 100644 index 85f6962bc..000000000 --- a/api-ref/source/v1/samples/template-create-env-request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "environment_name" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-create-env-response.json b/api-ref/source/v1/samples/template-create-env-response.json deleted file mode 100644 index 72db44524..000000000 --- a/api-ref/source/v1/samples/template-create-env-response.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "environment_id": "aa90fadfafca10e38e1c8c4bbf7", - "name": "environment_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "session_id": "adf4dadfaa9033ca7ce245fca10e38e1c8c4bbf7", -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-create-request.json b/api-ref/source/v1/samples/template-create-request.json deleted file mode 100644 index fe54e28b9..000000000 --- a/api-ref/source/v1/samples/template-create-request.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "env_template_name" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-create-response.json b/api-ref/source/v1/samples/template-create-response.json deleted file mode 100644 index 338005c0a..000000000 --- a/api-ref/source/v1/samples/template-create-response.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "123452452345346345634563456345346", - "version": 0, - "is_public": true, - "description_text": "", - "id": "744e44812da84e858946f5d817de4f72" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-list-apps-response.json b/api-ref/source/v1/samples/template-list-apps-response.json deleted file mode 100644 index a80b9a620..000000000 --- a/api-ref/source/v1/samples/template-list-apps-response.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - { - "instance": - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": - { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "tomcat", - "?": - { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - }, - { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "password": "XXX", - "name": "mysql", - "?": - { - "type": "io.murano.apps.database.MySQL", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } -] \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-show-response.json b/api-ref/source/v1/samples/template-show-response.json deleted file mode 100644 index cbc030679..000000000 --- a/api-ref/source/v1/samples/template-show-response.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "123452452345346345634563456345346", - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - } - ], - "version": 0, - "is_public": true, - "description_text": "", - "id": "744e44812da84e858946f5d817de4f72" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-update-app-request.json b/api-ref/source/v1/samples/template-update-app-request.json deleted file mode 100644 index 9fc3ebc52..000000000 --- a/api-ref/source/v1/samples/template-update-app-request.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/template-update-app-response.json b/api-ref/source/v1/samples/template-update-app-response.json deleted file mode 100644 index 4d084278e..000000000 --- a/api-ref/source/v1/samples/template-update-app-response.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "instance": - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": - { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" -} \ No newline at end of file diff --git a/api-ref/source/v1/samples/templates-list-response.json b/api-ref/source/v1/samples/templates-list-response.json deleted file mode 100644 index 3c6b45215..000000000 --- a/api-ref/source/v1/samples/templates-list-response.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "templates": [ - { - "updated": "2014-05-14T13:02:54", - "networking": {}, - "name": "test1", - "created": "2014-05-14T13:02:46", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "is_public": false, - "description_text": "", - "id": "2fa5ab704749444bbeafe7991b412c33" - }, - { - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "123452452345346345634563456345346", - "version": 0, - "is_public": true, - "description_text": "", - "id": "744e44812da84e858946f5d817de4f72" - } - ] -} \ No newline at end of file diff --git a/api-ref/source/v1/sessions.inc b/api-ref/source/v1/sessions.inc deleted file mode 100644 index 33b1ab0ab..000000000 --- a/api-ref/source/v1/sessions.inc +++ /dev/null @@ -1,165 +0,0 @@ -.. -*- rst -*- - -============================= -Environment Configuration API -============================= - -Since Murano environments are available for local modification by different -users and from different locations, it's therefore necessary to store local -modifications somewhere. Thus, sessions were created to satisfy this -requirement. After a user adds applications to an environment, a new session -can be created. A session can be deployed only once. - -.. note:: - - Multiple sessions can be opened for one environment simultaneously, but only - one session can be deployed at a time. Only the first session that is deployed - will be deployed, while the other ones will become invalid, no longer - capable of being deploying. Once an environment is in ``deploying`` or - ``deleting`` status, a new session for the environment cannot be opened. - -Configure Environment / Open Session -==================================== - -.. rest_method:: POST /environments/{env_id}/configure - -Creates a new configuration session for environment ``env_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - created: created - - updated: updated - - environment_id: env_id - - state: session_state - - version: session_version - - id: session_id - -Response Example ----------------- - -.. literalinclude:: samples/session-create-response.json - :language: javascript - -Deploy session -============== - -.. rest_method:: POST /environments/{env_id}/sessions/{session_id}/deploy - -Start deployment of a murano environment session. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - session_id: session_id_url - -Get Session Details -=================== - -.. rest_method:: GET /environments/{env_id}/sessions/{session_id} - -Start deployment of a murano environment session. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - session_id: session_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - id: session_id - - environment_id: env_id - - created: created - - updated: updated - - user_id: session_user_id - - version: session_version - - state: session_state - -Response Example ----------------- - -.. literalinclude:: samples/session-show-response.json - :language: javascript - -Delete Session -============== - -.. rest_method:: DELETE /environments/{env_id}/sessions/{session_id} - -Delete the session ``session_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 403 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_id: env_id_url - - session_id: session_id_url diff --git a/api-ref/source/v1/status.yaml b/api-ref/source/v1/status.yaml deleted file mode 100644 index b4d27fed5..000000000 --- a/api-ref/source/v1/status.yaml +++ /dev/null @@ -1,62 +0,0 @@ -################# -# Success Codes # -################# -200: - default: | - Request was successful. -201: - default: | - Resource was created and is ready to use. -202: - default: | - Request was accepted for processing, but the processing has not been - completed. A 'location' header is included in the response which contains - a link to check the progress of the request. -204: - default: | - The server has fulfilled the request by deleting the resource. -300: - default: | - There are multiple choices for resources. The request has to be more - specific to successfully retrieve one of these resources. -302: - default: | - The response is about a redirection hint. The header of the response - usually contains a 'location' value where requesters can check to track - the real location of the resource. - -################# -# Error Codes # -################# - -400: - default: | - Some content in the request was invalid. - resource_signal: | - The target resource doesn't support receiving a signal. -401: - default: | - User must authenticate before making a request. -403: - default: | - Policy does not allow current user to do this operation. -404: - default: | - The requested resource could not be found. -405: - default: | - Method is not valid for this endpoint. -409: - default: | - This operation conflicted with another operation on this resource. - duplicate_zone: | - There is already a zone with this name. -500: - default: | - Something went wrong inside the service. This should not happen usually. - If it does happen, it means the server has experienced some serious - problems. -503: - default: | - Service is not available. This is mostly caused by service configuration - errors which prevents the service from successful start up. diff --git a/api-ref/source/v1/templates.inc b/api-ref/source/v1/templates.inc deleted file mode 100644 index 39e983b09..000000000 --- a/api-ref/source/v1/templates.inc +++ /dev/null @@ -1,517 +0,0 @@ -.. -*- rst -*- - -===================== -Environment Templates -===================== - -An environment template specifies a set of virtual resources and application -information that can be deployed on top of OpenStack by translation this -information into an application-ready environment. Environment templates can -be customized, created, deleted and modified by users. Environment templates -can be instantied as many times as the user desires. For example, the user can -have different deployments from the same environment template: one for testing -and another for production. - -The workflow for the creation and the instantiation of the environment template -is as follows: - -#. Creation of the environment template (including application information) -#. Transformation of the environment template into the environment - (creation of the environment and session and adding applications to the - environment) -#. Deployment of the environment on top of Openstack - -Each environment template consists of services, which specify the application -information. Each service includes information about the applications that -will be installed (e.g. Tomcat), including application properties like -the Tomcat port. Additional information pertaining to the virtual server -may be specified, if applicable, such as keyname, flavor, image, etc. - -The following is an example of an environment template:: - - { - "name": "env_template_name", - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "tomcat", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - } - -List environment templates -========================== - -.. rest_method:: GET /templates - -Get a list of environment templates. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - is_public: template_is_public_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - templates: templates - - created: created - - updated: updated - - name: template_name - - tenant_id: tenant_id - - version: template_version - - description_text: template_description - - is_public: template_is_public - - id: template_id - -Response Example ----------------- - -.. literalinclude:: samples/templates-list-response.json - :language: javascript - -Create environment template -=========================== - -.. rest_method:: POST /templates - -Create an environment template. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - name: template_name - - is_public: template_is_public - -Request Example ---------------- - -.. literalinclude:: samples/template-create-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - created: created - - updated: updated - - name: template_name - - tenant_id: tenant_id - - version: template_version - - description_text: template_description - - is_public: template_is_public - - id: template_id - -Response Example ----------------- - -.. literalinclude:: samples/template-create-response.json - :language: javascript - -Get environment template details -================================ - -.. rest_method:: GET /templates/{env_temp_id} - -Get details for the environment template ``env_temp_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - created: created - - updated: updated - - name: template_name - - services: template_services - - tenant_id: tenant_id - - version: template_version - - description_text: template_description - - is_public: template_is_public - - id: template_id - -Response Example ----------------- - -.. literalinclude:: samples/template-show-response.json - :language: javascript - -Delete environment template -=========================== - -.. rest_method:: DELETE /templates/{env_temp_id} - -Delete the environment template ``env_temp_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - -Add application to environment template -======================================= - -.. rest_method:: POST /templates/{env_temp_id}/services - -Create a new application for environment template ``env_temp_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - - service: template_service - -Request Example ---------------- - -.. literalinclude:: samples/template-add-app-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - updated: updated - - created: created - - tenant_id: tenant_id - - services: template_services - - version: template_version - - description_text: template_description - - is_public: template_is_public - - id: template_id - - name: template_name - -Response Example ----------------- - -.. literalinclude:: samples/template-add-app-response.json - :language: javascript - -Delete application from an environment template -=============================================== - -.. rest_method:: DELETE /templates/{env_temp_id}/services/{service_id} - -Delete an application from an environment template. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - - service_id: service_id_url - -List application details for environment template -================================================= - -.. rest_method:: GET /templates/{env_temp_id}/services - -List all the applications for the specified environment template -``env_temp_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - -Response Parameters -------------------- - - - X-Openstack-Request-Id: request_id - - updated: updated - - created: created - - tenant_id: tenant_id - - services: template_services - - version: template_version - - description_text: template_description - - is_public: template_is_public - - id: template_id - - name: template_name - -Response Example ----------------- - -.. literalinclude:: samples/template-list-apps-response.json - :language: javascript - -Update application for an environment template -============================================== - -.. rest_method:: PUT /templates/{env_temp_id}/services/{service_id} - -Delete an application from an environment template. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - - service_id: service_id_url - - service: template_service - -Request Example ---------------- - -.. literalinclude:: samples/template-update-app-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - service: template_service - -Response Example ----------------- - -.. literalinclude:: samples/template-update-app-response.json - :language: javascript - -Create environment from environment template -============================================ - -.. rest_method:: GET /templates/{env_temp_id}/create-environment - -Create an environment from the environment template ``env_temp_id``. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - - name: env_name - -Request Example ---------------- - -.. literalinclude:: samples/template-create-env-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - environment_id: env_id - - name: env_name - - created: created - - tenant_id: tenant_id - - version: env_version - - session_id: session_id - -Response Example ----------------- - -.. literalinclude:: samples/template-create-env-response.json - :language: javascript - -Clone environment template -========================== - -.. rest_method:: GET /templates/{env_temp_id}/clone - -Clones an environment template from one tenant into another. - -.. note: - - In order to clone an environment template, the template *must* be - public. - -Response Codes --------------- - -.. rest_status_code:: success status.yaml - - - 200 - -.. rest_status_code:: error status.yaml - - - 401 - - 404 - - 409 - -Request Parameters ------------------- - -.. rest_parameters:: parameters.yaml - - - env_temp_id: template_id_url - - name: template_name - -Request Example ---------------- - -.. literalinclude:: samples/template-clone-request.json - :language: javascript - -Response Parameters -------------------- - -.. rest_parameters:: parameters.yaml - - - X-Openstack-Request-Id: request_id - - environment_id: env_id - - name: env_name - - created: created - - tenant_id: tenant_id - - version: env_version - - session_id: session_id - -Response Example ----------------- - -.. literalinclude:: samples/template-clone-response.json - :language: javascript diff --git a/bandit.yaml b/bandit.yaml deleted file mode 100644 index 3ca88f651..000000000 --- a/bandit.yaml +++ /dev/null @@ -1,157 +0,0 @@ - -### This config may optionally select a subset of tests to run or skip by -### filling out the 'tests' and 'skips' lists given below. If no tests are -### specified for inclusion then it is assumed all tests are desired. The skips -### set will remove specific tests from the include set. This can be controlled -### using the -t/-s CLI options. Note that the same test ID should not appear -### in both 'tests' and 'skips', this would be nonsensical and is detected by -### Bandit at runtime. - -# Available tests: -# B101 : assert_used -# B102 : exec_used -# B103 : set_bad_file_permissions -# B104 : hardcoded_bind_all_interfaces -# B105 : hardcoded_password_string -# B106 : hardcoded_password_funcarg -# B107 : hardcoded_password_default -# B108 : hardcoded_tmp_directory -# B109 : password_config_option_not_marked_secret -# B110 : try_except_pass -# B111 : execute_with_run_as_root_equals_true -# B112 : try_except_continue -# B201 : flask_debug_true -# B301 : pickle -# B302 : marshal -# B303 : md5 -# B304 : ciphers -# B305 : cipher_modes -# B306 : mktemp_q -# B307 : eval -# B308 : mark_safe -# B309 : httpsconnection -# B310 : urllib_urlopen -# B311 : random -# B312 : telnetlib -# B313 : xml_bad_cElementTree -# B314 : xml_bad_ElementTree -# B315 : xml_bad_expatreader -# B316 : xml_bad_expatbuilder -# B317 : xml_bad_sax -# B318 : xml_bad_minidom -# B319 : xml_bad_pulldom -# B320 : xml_bad_etree -# B321 : ftplib -# B401 : import_telnetlib -# B402 : import_ftplib -# B403 : import_pickle -# B404 : import_subprocess -# B405 : import_xml_etree -# B406 : import_xml_sax -# B407 : import_xml_expat -# B408 : import_xml_minidom -# B409 : import_xml_pulldom -# B410 : import_lxml -# B411 : import_xmlrpclib -# B412 : import_httpoxy -# B501 : request_with_no_cert_validation -# B502 : ssl_with_bad_version -# B503 : ssl_with_bad_defaults -# B504 : ssl_with_no_version -# B505 : weak_cryptographic_key -# B506 : yaml_load -# B601 : paramiko_calls -# B602 : subprocess_popen_with_shell_equals_true -# B603 : subprocess_without_shell_equals_true -# B604 : any_other_function_with_shell_equals_true -# B605 : start_process_with_a_shell -# B606 : start_process_with_no_shell -# B607 : start_process_with_partial_path -# B608 : hardcoded_sql_expressions -# B609 : linux_commands_wildcard_injection -# B701 : jinja2_autoescape_false -# B702 : use_of_mako_templates - -# (optional) list included test IDs here, eg '[B101, B406]': -tests: - -# (optional) list skipped test IDs here, eg '[B101, B406]': -skips: [B104] - -### (optional) plugin settings - some test plugins require configuration data -### that may be given here, per-plugin. All bandit test plugins have a built in -### set of sensible defaults and these will be used if no configuration is -### provided. It is not necessary to provide settings for every (or any) plugin -### if the defaults are acceptable. - -#any_other_function_with_shell_equals_true: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#execute_with_run_as_root_equals_true: -# function_names: [ceilometer.utils.execute, cinder.utils.execute, neutron.agent.linux.utils.execute, -# nova.utils.execute, nova.utils.trycmd] -#hardcoded_tmp_directory: -# tmp_dirs: [/tmp, /var/tmp, /dev/shm] -#linux_commands_wildcard_injection: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#password_config_option_not_marked_secret: -# function_names: [oslo.config.cfg.StrOpt, oslo_config.cfg.StrOpt] -#ssl_with_bad_defaults: -# bad_protocol_versions: [PROTOCOL_SSLv2, SSLv2_METHOD, SSLv23_METHOD, PROTOCOL_SSLv3, -# PROTOCOL_TLSv1, SSLv3_METHOD, TLSv1_METHOD] -#ssl_with_bad_version: -# bad_protocol_versions: [PROTOCOL_SSLv2, SSLv2_METHOD, SSLv23_METHOD, PROTOCOL_SSLv3, -# PROTOCOL_TLSv1, SSLv3_METHOD, TLSv1_METHOD] -#start_process_with_a_shell: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#start_process_with_no_shell: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#start_process_with_partial_path: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#subprocess_popen_with_shell_equals_true: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#subprocess_without_shell_equals_true: -# no_shell: [os.execl, os.execle, os.execlp, os.execlpe, os.execv, os.execve, os.execvp, -# os.execvpe, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe, os.spawnv, os.spawnve, -# os.spawnvp, os.spawnvpe, os.startfile] -# shell: [os.system, os.popen, os.popen2, os.popen3, os.popen4, popen2.popen2, popen2.popen3, -# popen2.popen4, popen2.Popen3, popen2.Popen4, commands.getoutput, commands.getstatusoutput] -# subprocess: [subprocess.Popen, subprocess.call, subprocess.check_call, subprocess.check_output, -# utils.execute, utils.execute_with_timeout] -#try_except_continue: {check_typed_exception: false} -#try_except_pass: {check_typed_exception: false} diff --git a/bindep.txt b/bindep.txt deleted file mode 100644 index 64c6e0042..000000000 --- a/bindep.txt +++ /dev/null @@ -1,11 +0,0 @@ -# This is a cross-platform list tracking distribution packages needed for install and tests; -# see https://docs.openstack.org/infra/bindep/ for additional information. - -libpq-dev [platform:dpkg] -mysql-client [platform:dpkg] -mysql-server [platform:dpkg] -postgresql -postgresql-client [platform:dpkg] - -# PDF Docs package dependencies -tex-gyre [platform:dpkg doc] diff --git a/contrib/elements/docker/README.md b/contrib/elements/docker/README.md deleted file mode 100644 index 1b3b9a4f6..000000000 --- a/contrib/elements/docker/README.md +++ /dev/null @@ -1 +0,0 @@ -This element install Docker on Ubuntu/CentOS \ No newline at end of file diff --git a/contrib/elements/docker/install.d/56-docker b/contrib/elements/docker/install.d/56-docker deleted file mode 100755 index e0d943c8b..000000000 --- a/contrib/elements/docker/install.d/56-docker +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -eu - -if [ -e /etc/lsb-release ]; then - if [ -e /usr/lib/apt/methods/https ]; then - apt-get update - apt-get install apt-transport-https - fi - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9 - echo "deb https://get.docker.com/ubuntu docker main" > /etc/apt/sources.list.d/docker.list - apt-get update - apt-get -y install lxc-docker -else - yum -y install docker -fi \ No newline at end of file diff --git a/contrib/elements/kubernetes/README.md b/contrib/elements/kubernetes/README.md deleted file mode 100644 index 97a8bd088..000000000 --- a/contrib/elements/kubernetes/README.md +++ /dev/null @@ -1 +0,0 @@ -This element installs Kubernetes on Ubuntu/CentOS \ No newline at end of file diff --git a/contrib/elements/kubernetes/element-deps b/contrib/elements/kubernetes/element-deps deleted file mode 100644 index 6d0eac4b3..000000000 --- a/contrib/elements/kubernetes/element-deps +++ /dev/null @@ -1 +0,0 @@ -docker \ No newline at end of file diff --git a/contrib/elements/kubernetes/install.d/57-kubernetes b/contrib/elements/kubernetes/install.d/57-kubernetes deleted file mode 100755 index 094788d5c..000000000 --- a/contrib/elements/kubernetes/install.d/57-kubernetes +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -install-packages curl wget linux-libc-dev git gcc libc6-dev bridge-utils haproxy - -SVC_ROOT=/opt/bin - -ETCD_LATEST_VERSION=$(curl https://github.com/coreos/etcd/releases/latest | awk -F'"' '{ print $2 }' | awk -F'/' '{ print $8 }') -ETCD_LATEST_URL="https://github.com/coreos/etcd/releases/download/${ETCD_LATEST_VERSION}/etcd-${ETCD_LATEST_VERSION}-linux-amd64.tar.gz" -KUBE_LATEST_VERSION=$(curl https://github.com/GoogleCloudPlatform/kubernetes/releases/latest | awk -F'"' '{ print $2 }' | awk -F'/' '{ print $8 }') -KUBE_LATEST_URL="https://github.com/GoogleCloudPlatform/kubernetes/releases/download/${KUBE_LATEST_VERSION}/kubernetes.tar.gz" - -mkdir -p ${SVC_ROOT} -pushd ${SVC_ROOT} - -# Install latest etcd -wget -O ${SVC_ROOT}/etcd-latest.tar.gz $ETCD_LATEST_URL -tar xzvf ${SVC_ROOT}/etcd-latest.tar.gz -rm -f ${SVC_ROOT}/etcd-latest.tar.gz - -mv ${SVC_ROOT}/etcd-${ETCD_LATEST_VERSION}-linux-amd64/etcd ${SVC_ROOT}/ -mv ${SVC_ROOT}/etcd-${ETCD_LATEST_VERSION}-linux-amd64/etcdctl ${SVC_ROOT}/ - -rm -rf ${SVC_ROOT}/etcd-${ETCD_LATEST_VERSION}-linux-amd64 - -# Install latest kubernetes -wget -O ${SVC_ROOT}/kubernetes-latest.tar.gz $KUBE_LATEST_URL -tar xzvf ${SVC_ROOT}/kubernetes-latest.tar.gz -rm -f ${SVC_ROOT}/kubernetes-latest.tar.gz - -tar xzvf ${SVC_ROOT}/kubernetes/server/kubernetes-server-linux-amd64.tar.gz -mv ${SVC_ROOT}/kubernetes ${SVC_ROOT}/kubernetes-latest - -cp ${SVC_ROOT}/kubernetes-latest/server/bin/* ${SVC_ROOT}/ - -rm -rf ${SVC_ROOT}/kubernetes-latest - -# Install Go -wget -O go.tar.gz https://storage.googleapis.com/golang/go1.4.2.linux-amd64.tar.gz -tar xzvf go.tar.gz -mv ${SVC_ROOT}/go /usr/local/go -export PATH=$PATH:/usr/local/go/bin - -# Build flannel -git clone https://github.com/coreos/flannel flannel -pushd ${SVC_ROOT}/flannel -${SVC_ROOT}/flannel/build -popd - -cp ${SVC_ROOT}/flannel/bin/flanneld ${SVC_ROOT}/flanneld - -rm -rf ${SVC_ROOT}/flannel - - -# Update system PATH -sed -i 's/PATH="/PATH="\/opt\/bin:\/opt\/go\/bin:/g' /etc/environment - - -wget -O confd https://github.com/kelseyhightower/confd/releases/download/v0.7.1/confd-0.7.1-linux-amd64 -mv confd /usr/local/bin/confd -chmod +x /usr/local/bin/confd -mkdir -p /etc/confd/{conf.d,templates} - -popd \ No newline at end of file diff --git a/contrib/glance/muranoartifact/__init__.py b/contrib/glance/muranoartifact/__init__.py deleted file mode 100644 index 7c7a7b6c0..000000000 --- a/contrib/glance/muranoartifact/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 muranoartifact.v1 import package - -VERSIONS = [package.MuranoPackage] diff --git a/contrib/glance/muranoartifact/v1/__init__.py b/contrib/glance/muranoartifact/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/glance/muranoartifact/v1/package.py b/contrib/glance/muranoartifact/v1/package.py deleted file mode 100644 index 97c066a5c..000000000 --- a/contrib/glance/muranoartifact/v1/package.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 glance.common.glare import definitions - - -class MuranoPackage(definitions.ArtifactType): - __endpoint__ = 'murano' - - type = definitions.String(allowed_values=['Application', 'Library'], - required=True, - mutable=False) - - author = definitions.String(required=False, mutable=False) - display_name = definitions.String(required=True, mutable=True) - enabled = definitions.Boolean(default=True) - - categories = definitions.Array(default=[], mutable=True) - class_definitions = definitions.Array(unique=True, default=[], - mutable=False) - inherits = definitions.Dict(default={}, properties=definitions.Array(), - mutable=False) - keywords = definitions.Array(default=[], mutable=True) - logo = definitions.BinaryObject() - archive = definitions.BinaryObject() - ui_definition = definitions.BinaryObject() diff --git a/contrib/glance/setup.cfg b/contrib/glance/setup.cfg deleted file mode 100644 index 3ab134e84..000000000 --- a/contrib/glance/setup.cfg +++ /dev/null @@ -1,21 +0,0 @@ -[metadata] -name = murano_artifact_plugin -description = An artifact plugin for murano packages -author = Alexander Tivelkov -author-email = openstack-discuss@lists.openstack.org -python-requires = >=3.6 -classifier = - Development Status :: 3 - Alpha - License :: OSI Approved :: Apache Software License - Programming Language :: Python - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.6 - Programming Language :: Python :: 3.7 - Intended Audience :: Developers - Environment :: Console - -[entry_points] -glance.artifacts.types = - MuranoPackage = muranoartifact:VERSIONS diff --git a/contrib/glance/setup.py b/contrib/glance/setup.py deleted file mode 100644 index 2a3ea51e7..000000000 --- a/contrib/glance/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 setuptools - -# all other params will be taken from setup.cfg -setuptools.setup(packages=setuptools.find_packages(), - setup_requires=['pbr'], pbr=True) diff --git a/contrib/packages/EncryptionDemo/Classes/EncryptionDemo.yaml b/contrib/packages/EncryptionDemo/Classes/EncryptionDemo.yaml deleted file mode 100644 index 89e939bd9..000000000 --- a/contrib/packages/EncryptionDemo/Classes/EncryptionDemo.yaml +++ /dev/null @@ -1,18 +0,0 @@ -Namespaces: - =: com.paul - std: io.murano - res: io.murano.resources - -Name: EncryptionDemo - -Extends: std:Application - -Properties: - my_password: - Contract: $.string() - -Methods: - deploy: - Body: - - $reporter: $this.find(std:Environment).reporter - - $reporter.report($this, decryptData($.my_password)) diff --git a/contrib/packages/EncryptionDemo/UI/ui.yaml b/contrib/packages/EncryptionDemo/UI/ui.yaml deleted file mode 100644 index 1d296aec2..000000000 --- a/contrib/packages/EncryptionDemo/UI/ui.yaml +++ /dev/null @@ -1,10 +0,0 @@ -Application: - ?: - type: com.paul.EncryptionDemo - my_password: encryptData($.instanceConfiguration.my_password) - -Forms: - - instanceConfiguration: - fields: - - name: my_password - type: string diff --git a/contrib/packages/EncryptionDemo/manifest.yaml b/contrib/packages/EncryptionDemo/manifest.yaml deleted file mode 100644 index 66942dc8d..000000000 --- a/contrib/packages/EncryptionDemo/manifest.yaml +++ /dev/null @@ -1,6 +0,0 @@ -FullName: com.paul.EncryptionDemo -Type: Application -Description: Simple app to demonstrate Murano encryption -Author: Paul Bourke -Classes: - com.paul.EncryptionDemo: EncryptionDemo.yaml diff --git a/contrib/plugins/cloudify_plugin/LICENSE b/contrib/plugins/cloudify_plugin/LICENSE deleted file mode 100644 index 67db85882..000000000 --- a/contrib/plugins/cloudify_plugin/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/contrib/plugins/cloudify_plugin/README.rst b/contrib/plugins/cloudify_plugin/README.rst deleted file mode 100644 index f364690c7..000000000 --- a/contrib/plugins/cloudify_plugin/README.rst +++ /dev/null @@ -1,49 +0,0 @@ -Murano Plugin for Cloudify -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cloudify is a TOSCA-based open-source cloud orchestration engine by GigaSpaces -Technologies. - -This plugin extends Murano with support of Cloudify TOSCA package format. -TOSCA packages can be deployed on Cloudify Manager deployed at configurable -location. - -Plugin registers `Cloudify.TOSCA/1.0` format identifier. - -Installation ------------- - -Installation of the plugin is done using any of Python package management -tools. The most simple way is by saying `pip install .` from the plugin's -directory (or `pip install -e .` for development) - -Also location of Cloudify Manager (engine server) must be configured -in murano config file. This is done in `[cloudify]` section of murano.conf -via cloudify_manager setting. For example: - -.. code-block:: ini - - [cloudify] - cloudify_manager = 10.10.1.10 - - -Murano engine must be restarted after installation of the plugin. - - -Requirements ------------- - -All Cloudify TOSCA application require `org.getcloudify.murano` library package -to be present in Murano catalog. The package can be found in -`cloudify_applications_library` subfolder. - - -Demo application ----------------- - -There is a demo application that can be used to test the plugin. -It is located in `nodecellar_example_application` subfolder. Follow -instructions at `nodecellar_example_application/README.rst` to build -the demo package. - - diff --git a/contrib/plugins/cloudify_plugin/cloudify_applications_library/Classes/CloudifyApplication.yaml b/contrib/plugins/cloudify_plugin/cloudify_applications_library/Classes/CloudifyApplication.yaml deleted file mode 100644 index 629724339..000000000 --- a/contrib/plugins/cloudify_plugin/cloudify_applications_library/Classes/CloudifyApplication.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# Licensed 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. - -Namespaces: - =: org.getcloudify.murano - std: io.murano - csys: io.murano.extensions.cloudify - -Name: CloudifyApplication - -Extends: std:Application - -Methods: - .init: - Body: - - $._client: new(csys:CloudifyClient, app => $this) - - $._environment: $.find(std:Environment).require() - - describe: - - updateOutputs: - Arguments: - - outputs: - Contract: - $.string().notNull(): $ - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $info: $.describe() - - $._environment.reporter.report($this, 'Checking for TOSCA package') - - $._client.publishBlueprint($info.entryPoint) - - $._client.createDeployment($info.inputs) - - $._environment.reporter.report($this, 'Waiting for deployment initialization') - - $._client.waitDeploymentReady() - - $._environment.reporter.report($this, 'Installing {0}'.format(name($this))) - - $._client.executeWorkflow(install) - - $outputs: $._client.waitDeploymentReady() - - For: outputName - In: $outputs.keys() - Do: - - $output: $outputs[$outputName] - - $._environment.reporter.report($this, $output) - - $label: $output.get(description, $outputName) - - $value: $output.value - - $msg: '{0}: {1}' - - $._environment.reporter.report($this, $msg.format($label, $value)) - - $.updateOutputs($outputs) - - $._environment.reporter.report($this, 'Installation complete') - - $.setAttr(deployed, true) - - .destroy: - Body: - - If: $.getAttr(deployed, false) - Then: - - $info: $.describe() - - $._client.waitDeploymentReady() - - $._environment.reporter.report($this, 'Uninstalling {0}'.format(name($this))) - - $._client.executeWorkflow(uninstall) - - $._client.waitDeploymentReady() - - $._client.deleteDeployment() - - $._environment.reporter.report($this, 'Uninstallation complete') - - $.setAttr(deployed, false) diff --git a/contrib/plugins/cloudify_plugin/cloudify_applications_library/manifest.yaml b/contrib/plugins/cloudify_plugin/cloudify_applications_library/manifest.yaml deleted file mode 100644 index e0dcec634..000000000 --- a/contrib/plugins/cloudify_plugin/cloudify_applications_library/manifest.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed 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. - -Format: MuranoPL/1.2 - -Type: Library -FullName: org.getcloudify.murano - -Name: Cloudify applications - -Description: > - Cloudify Murano integration support library - -Author: Trammell - -Tags: - - Cloudify - -Classes: - org.getcloudify.murano.CloudifyApplication: CloudifyApplication.yaml - -Require: - io.murano.plugins.cloudify: 0 \ No newline at end of file diff --git a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/__init__.py b/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cfg.py b/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cfg.py deleted file mode 100644 index d395263f7..000000000 --- a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cfg.py +++ /dev/null @@ -1,21 +0,0 @@ -# Licensed 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 oslo_config import cfg - - -def init_config(conf): - opts = [ - cfg.StrOpt('cloudify_manager', required=True) - ] - conf.register_opts(opts, group='cloudify') - return conf.cloudify diff --git a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_client.py b/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_client.py deleted file mode 100644 index f708744ed..000000000 --- a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_client.py +++ /dev/null @@ -1,88 +0,0 @@ -# Licensed 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 threading -import time - -import cloudify_rest_client -import cloudify_rest_client.exceptions as cloudify_exceptions -from murano.dsl import dsl -from oslo_config import cfg as config -from yaql.language import specs -from yaql.language import yaqltypes - -import cfg - - -CONF = config.CONF -archive_upload_lock = threading.Lock() - - -class CloudifyClient(object): - @specs.parameter('app', dsl.MuranoObjectParameter('io.murano.Application')) - def __init__(self, app): - cloudify_manager = self.CONF.cloudify_manager - self._client = cloudify_rest_client.CloudifyClient(cloudify_manager) - self._blueprint_id = '{0}-{1}'.format(app.type.name, app.type.version) - self._deployment_id = app.id - self._application_package = app.package - - @specs.parameter('entry_point', yaqltypes.String()) - def publish_blueprint(self, entry_point): - global archive_upload_lock - - if self._check_blueprint_exists(): - return - path = self._application_package.get_resource(entry_point) - with archive_upload_lock: - try: - self._client.blueprints.upload( - path, self._blueprint_id) - except cloudify_exceptions.CloudifyClientError as e: - if e.status_code != 409: - raise - - def _check_blueprint_exists(self): - try: - self._client.blueprints.get(self._blueprint_id) - return True - except cloudify_exceptions.CloudifyClientError as e: - if e.status_code == 404: - return False - raise - - @specs.parameter('parameters', dict) - def create_deployment(self, parameters=None): - self._client.deployments.create( - self._blueprint_id, self._deployment_id, parameters) - - def delete_deployment(self): - self._client.deployments.delete(self._deployment_id) - - def wait_deployment_ready(self): - while True: - executions = self._client.executions.list(self._deployment_id) - if any(t.status in ('pending', 'started') for t in executions): - time.sleep(3) - else: - deployment = self._client.deployments.get(self._deployment_id) - return deployment.outputs - - @specs.parameter('name', yaqltypes.String()) - @specs.parameter('parameters', dict) - def execute_workflow(self, name, parameters=None): - self._client.executions.start(self._deployment_id, name, parameters) - - @classmethod - def init_plugin(cls): - cls.CONF = cfg.init_config(CONF) diff --git a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_tosca_package.py b/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_tosca_package.py deleted file mode 100644 index fe04e8fc8..000000000 --- a/contrib/plugins/cloudify_plugin/murano_cloudify_plugin/cloudify_tosca_package.py +++ /dev/null @@ -1,186 +0,0 @@ -# Licensed 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 yaml - -from murano.common.helpers import path -from murano.packages import exceptions -from murano.packages import package_base - -RESOURCES_DIR_NAME = 'Resources/' - - -class YAQL(object): - def __init__(self, expr): - self.expr = expr - - -class Dumper(yaml.SafeDumper): - pass - - -def yaql_representer(dumper, data): - return dumper.represent_scalar(u'!yaql', data.expr) - - -Dumper.add_representer(YAQL, yaql_representer) - - -class CloudifyToscaPackage(package_base.PackageBase): - def __init__(self, format_name, runtime_version, source_directory, - manifest): - super(CloudifyToscaPackage, self).__init__( - format_name, runtime_version, source_directory, manifest) - - self._entry_point = manifest.get('EntryPoint', 'main.yaml') - self._generated_class = None - self._generated_ui = None - - @property - def classes(self): - return self.full_name, - - @property - def requirements(self): - return { - 'org.getcloudify.murano': '0' - } - - @property - def ui(self): - if not self._generated_ui: - self._generated_ui = self._generate_ui() - return self._generated_ui - - def get_class(self, name): - if name != self.full_name: - raise exceptions.PackageClassLoadError( - name, 'Class not defined in this package') - if not self._generated_class: - self._generated_class = self._generate_class() - return self._generated_class, '' - - def _generate_class(self): - inputs, outputs = self._get_inputs_outputs() - class_code = { - 'Name': self.full_name, - 'Extends': 'org.getcloudify.murano.CloudifyApplication', - 'Properties': self._generate_properties(inputs, outputs), - 'Methods': { - 'describe': self._generate_describe_method(inputs), - 'updateOutputs': self._generate_update_outputs_method(outputs) - } - } - return yaml.dump(class_code, Dumper=Dumper, default_style='"') - - @staticmethod - def _generate_properties(inputs, outputs): - contracts = {} - for name, value in inputs.items(): - prop = { - 'Contract': YAQL('$.string().notNull()'), - 'Usage': 'In' - } - if 'default' in value: - prop['Default'] = value['default'] - contracts[name] = prop - - for name in outputs.keys(): - contracts[name] = { - 'Contract': YAQL('$.string()'), - 'Usage': 'Out' - } - - return contracts - - def _generate_describe_method(self, inputs): - input_values = { - name: YAQL('$.' + name) - for name in inputs.keys() - } - - return { - 'Body': [{ - 'Return': { - 'entryPoint': self._entry_point, - 'inputs': input_values - } - }] - } - - @staticmethod - def _generate_update_outputs_method(outputs): - assignments = [ - {YAQL('$.' + name): YAQL('$outputs.get({0})'.format(name))} - for name in outputs.keys() - ] - return { - 'Arguments': [{ - 'outputs': { - 'Contract': { - YAQL('$.string().notNull()'): YAQL('$') - } - } - }], - 'Body': assignments - } - - def _get_inputs_outputs(self): - entry_point_path = path.secure_join( - self.source_directory, RESOURCES_DIR_NAME, self._entry_point) - with open(entry_point_path) as blueprint: - data = yaml.safe_load(blueprint) - return data.get('inputs') or {}, data.get('outputs') or {} - - def _generate_application_ui_section(self, inputs, package_name=None, - package_version=None): - section = { - key: YAQL( - '$.appConfiguration.' + key) for key in inputs.keys() - } - section.update({ - '?': { - 'type': self.full_name - } - }) - if package_name: - section['?']['package'] = package_name - if package_version: - section['?']['classVersion'] = package_version - return section - - @staticmethod - def _generate_form_ui_section(inputs): - fields = [ - { - 'name': key, - 'label': key.title().replace('_', ' '), - 'type': 'string', - 'required': True, - 'description': value.get('description', key) - } for key, value in inputs.items() - ] - return [{ - 'appConfiguration': { - 'fields': fields - } - }] - - def _generate_ui(self): - inputs, outputs = self._get_inputs_outputs() - ui = { - 'Version': '2.2', - 'Application': self._generate_application_ui_section( - inputs, self.full_name, str(self.version)), - 'Forms': self._generate_form_ui_section(inputs) - } - return yaml.dump(ui, Dumper=Dumper, default_style='"') diff --git a/contrib/plugins/cloudify_plugin/nodecellar_example_application/LICENSE b/contrib/plugins/cloudify_plugin/nodecellar_example_application/LICENSE deleted file mode 100644 index 67db85882..000000000 --- a/contrib/plugins/cloudify_plugin/nodecellar_example_application/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/contrib/plugins/cloudify_plugin/nodecellar_example_application/README.rst b/contrib/plugins/cloudify_plugin/nodecellar_example_application/README.rst deleted file mode 100644 index cf5ccd32a..000000000 --- a/contrib/plugins/cloudify_plugin/nodecellar_example_application/README.rst +++ /dev/null @@ -1,18 +0,0 @@ -Nodecellar Example Application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Nodecellar is an example application with a Node front end and Mongo -database backend. - -To test this application with the Murano Cloudify plugin, the following steps need to -be executed: - -`git clone https://github.com/cloudify-cosmo/cloudify-nodecellar-example.git Resources` -`cd Resources` -`git checkout tags/3.2.1` - -After the above steps are completed, the packages need to be zipped and uploaded to -the Murano catalog as normally done for Murano applications. - -You can follow instructions from `here `_ -to quickly bring up the environment for the application. diff --git a/contrib/plugins/cloudify_plugin/nodecellar_example_application/logo.png b/contrib/plugins/cloudify_plugin/nodecellar_example_application/logo.png deleted file mode 100644 index f47201d28..000000000 Binary files a/contrib/plugins/cloudify_plugin/nodecellar_example_application/logo.png and /dev/null differ diff --git a/contrib/plugins/cloudify_plugin/nodecellar_example_application/manifest.yaml b/contrib/plugins/cloudify_plugin/nodecellar_example_application/manifest.yaml deleted file mode 100644 index 64181924b..000000000 --- a/contrib/plugins/cloudify_plugin/nodecellar_example_application/manifest.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed 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. - -Format: Cloudify.TOSCA/1.0 -Type: Application -FullName: org.getcloudify.muranoapps.examples.NodeCellar -EntryPoint: singlehost-blueprint.yaml - -Name: Node Cellar sample app -Description: > - A sample application built with Backbone.js, Twitter Bootstrap, - Node.js, Express, and MongoDB - -Author: Trammell - -Tags: - - TOSCA - - Cloudify - - Sample diff --git a/contrib/plugins/cloudify_plugin/requirements.txt b/contrib/plugins/cloudify_plugin/requirements.txt deleted file mode 100644 index 4f11ddc00..000000000 --- a/contrib/plugins/cloudify_plugin/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cloudify-rest-client>=3.2 diff --git a/contrib/plugins/cloudify_plugin/setup.cfg b/contrib/plugins/cloudify_plugin/setup.cfg deleted file mode 100644 index e70ecfe85..000000000 --- a/contrib/plugins/cloudify_plugin/setup.cfg +++ /dev/null @@ -1,16 +0,0 @@ -[metadata] -name = io.murano.plugins.cloudify -description = Murano-Cloudify integration plugin -summary = Plugin to deploy Tosca packages via Cloudify Manager with Murano -author = Trammell -author-email = trammell@gigaspaces.com - -[files] -packages = murano_cloudify_plugin - -[entry_points] -io.murano.plugins.packages = - Cloudify.TOSCA/1.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage - -io.murano.extensions = - cloudify.CloudifyClient = murano_cloudify_plugin.cloudify_client:CloudifyClient diff --git a/contrib/plugins/cloudify_plugin/setup.py b/contrib/plugins/cloudify_plugin/setup.py deleted file mode 100644 index 2a3ea51e7..000000000 --- a/contrib/plugins/cloudify_plugin/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 setuptools - -# all other params will be taken from setup.cfg -setuptools.setup(packages=setuptools.find_packages(), - setup_requires=['pbr'], pbr=True) diff --git a/contrib/plugins/magnum_plugin/LICENSE b/contrib/plugins/magnum_plugin/LICENSE deleted file mode 100644 index 67db85882..000000000 --- a/contrib/plugins/magnum_plugin/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBayApp.yaml b/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBayApp.yaml deleted file mode 100644 index 62a41f9e4..000000000 --- a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBayApp.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# Licensed 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. - -Namespaces: - =: com.intel.magnum.plugin - std: io.murano - -Name: MagnumBayApp - -Extends: std:Application - -Properties: - name: - Contract: $.string().notNull() - - baymodel: - Contract: $.class(MagnumBaymodel).notNull() - - nodeCount: - Contract: $.int().check($ > 0) - - masterCount: - Contract: $.int().check($ > 0) - - discoveryUrl: - Contract: $.string() - - timeout: - Contract: $.int().check($ >= 0) - - -Methods: - .init: - Body: - - $._environment: $.find(std:Environment).require() - - Try: - - $._magnum: new('io.murano.extensions.mirantis.magnum.Magnum', $._environment) - Catch: - With: 'murano.dsl.exceptions.NoPackageForClassFound' - Do: - Throw: PluginNotFoundException - Message: 'Plugin for interaction with Magnum is not installed' - - .destroy: - Body: - - $bayId: $.getAttr(bayId, null) - - $._magnum.deleteBay($bayId) - - $msg: format('Magnum bay {0} is deleted', $.name) - - $._environment.reporter.report($this, $msg) - - $.baymodel.delete() - - deploy: - Body: - - $baymodelId: $.baymodel.create() - - $msg: format('Creating Magnum bay {0}', $.name) - - $._environment.reporter.report($this, $msg) - - $params: - name: $.name - baymodel_id: $baymodelId - node_count: $.nodeCount - master_count: $.masterCount - discovery_url: $.discoveryUrl - bay_create_timeout: $.timeout - - Try: - - $bayId: $._magnum.createBay($params) - Catch: - - As: e - Do: - - $formatString: 'Error: {0}' - - $._environment.reporter.report_error($, $formatString.format($e.message)) - - Rethrow: - - $.setAttr(bayId, $bayId) - - $bayStatus: $._magnum.getBayStatus($bayId) - - If: $bayStatus = "CREATE_FAILED" - Then: - - $msg: 'Magnum bay create failed' - - $._environment.reporter.report_error($this, $msg) - - Throw: MagnumBayCreateFailed - Message: $msg - - $msg: format('Magnum bay {0} is created', $.name) - - $._environment.reporter.report($this, $msg) diff --git a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBaymodel.yaml b/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBaymodel.yaml deleted file mode 100644 index e9a2dc749..000000000 --- a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/Classes/MagnumBaymodel.yaml +++ /dev/null @@ -1,131 +0,0 @@ -# Licensed 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. - -Namespaces: - =: com.intel.magnum.plugin - std: io.murano - -Name: MagnumBaymodel - -Properties: - name: - Contract: $.string().notNull() - - imageId: - Contract: $.string().notNull() - - flavorId: - Contract: $.string() - - masterFlavorId: - Contract: $.string() - - keypairId: - Contract: $.string().notNull() - - externalNetworkId: - Contract: $.string().notNull() - - fixedNetwork: - Contract: $.string() - - coe: - Contract: $.string().notNull().check($ in list(kubernetes, swarm, mesos)) - - dnsNameServer: - Contract: $.string() - - dockerVolumeSize: - Contract: $.string() - - labels: - Contract: $.string() - - httpProxy: - Contract: $.string() - - httpsProxy: - Contract: $.string() - - noProxy: - Contract: $.string() - - networkDriver: - Contract: $.string() - - volumeDriver: - Contract: $.string() - - tlsDisabled: - Contract: $.bool() - - public: - Contract: $.bool() - - registryEnabled: - Contract: $.bool() - -Methods: - .init: - Body: - - $._environment: $.find(std:Environment).require() - - Try: - - $._magnum: new('io.murano.extensions.mirantis.magnum.Magnum', $._environment) - Catch: - With: 'murano.dsl.exceptions.NoPackageForClassFound' - Do: - Throw: PluginNotFoundException - Message: 'Plugin for interaction with Magnum is not installed' - - create: - Body: - - $msg: format('Creating Magnum baymodel {0}', $.name) - - $._environment.reporter.report($this, $msg) - - $params: - name: $.name - image_id: $.imageId - keypair_id: $.keypairId - external_network_id: $.externalNetworkId - coe: $.coe - flavor_id: $.flavorId - master_flavor_id: $.masterFlavorId - fixed_network: $.fixedNetwork - dns_nameserver: $.dnsNameServer - network_driver: $.networkDriver - docker_volume_size: $.dockerVolumeSize - labels: $.labels - http_proxy: $.httpProxy - https_proxy: $.httpsProxy - no_proxy: $.noProxy - volume_driver: $.volumeDriver - tls_disabled: $.tlsDisabled - public: $.public - registry_enabled: $.registryEnabled - - Try: - - $baymodelId: $._magnum.createBaymodel($params) - Catch: - - As: e - Do: - - $formatString: 'Error: {0}' - - $._environment.reporter.report_error($, $formatString.format($e.message)) - - Rethrow: - - $.setAttr(baymodelId, $baymodeId) - - $msg: format('Magnum baymodel is created {0}', $.name) - - $._environment.reporter.report($this, $msg) - - Return: $baymodelId - - delete: - Body: - - $baymodelId: $.getAttr(baymodelId, null) - - $._magnum.deleteBaymodel($baymodelId) - - $msg: format('Magnum baymodel {0} is deleted', $.name) - - $._environment.reporter.report($this, $msg) diff --git a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/UI/ui.yaml b/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/UI/ui.yaml deleted file mode 100644 index 8c0021745..000000000 --- a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/UI/ui.yaml +++ /dev/null @@ -1,212 +0,0 @@ -# Licensed 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. - -Version: 2 - -Templates: - baymodel: - ?: - type: com.intel.magnum.plugin.MagnumBaymodel - name: $.baymodelConfiguration.name - imageId: $.baymodelConfiguration.imageId - keypairId: $.baymodelConfiguration.keyPair - externalNetworkId: $.baymodelConfiguration.externalNetworkId - coe: $.baymodelConfiguration.coe - flavorId: $.baymodelConfiguration.flavorId.norm() - masterFlavorId: $.baymodelConfiguration.masterFlavorId.norm() - networkDriver: $.baymodelConfiguration.networkDriver.norm() - fixedNetwork: $.baymodelConfiguration.fixedNetwork.norm() - dnsNameServer: $.baymodelConfiguration.dnsNameServer.norm() - dockerVolumeSize: $.baymodelConfiguration.dockerVolumeSize - labels: $.baymodelConfiguration.labels.norm() - volumeDriver: $.baymodelConfiguration.volumeDriver.norm() - httpProxy: $.baymodelConfiguration.httpProxy.norm() - httpsProxy: $.baymodelConfiguration.httpsProxy.norm() - noProxy: $.baymodelConfiguration.noProxy.norm() - tlsDisabled: $.baymodelConfiguration.tlsDisabled - public: $.baymodelConfiguration.public - registryEnabled: $.baymodelConfiguration.registryEnabled - -Application: - ?: - type: com.intel.magnum.plugin.MagnumBayApp - name: $.appConfiguration.name - nodeCount: $.appConfiguration.nodeCount - masterCount: $.appConfiguration.masterCount - discoveryUrl: $.appConfiguration.discoveryUrl.norm() - timeout: $.appConfiguration.timeout - baymodel: $baymodel - -Forms: - - appConfiguration: - fields: - - name: name - type: string - label: Bay Name - description: >- - Enter a desired name for the application. Just A-Z, a-z, 0-9. - - name: nodeCount - type: integer - label: Node Count - initial: 1 - required: false - description: >- - Enter desired no. of node counts. This node count specifies no. of - minion node created in bay. - - name: masterCount - type: integer - label: Master Node Count - initial: 1 - required: false - description: >- - Enter desired no. of master node counts. This master node count specifies - no. of master node created in bay. - - name: discoveryUrl - type: string - label: Discovery URL - required: false - description: >- - Specifies custom discovery url for node discovery. - - name: timeout - type: integer - label: Timeout - initial: 0 - required: false - description: >- - The timeout for bay creation in minutes. Set to 0 for no timeout. - The default is no timeout. - - - baymodelConfiguration: - fields: - - name: name - type: string - label: Baymodel Name - description: >- - Enter a desired name for the application. Just A-Z, a-z, 0-9. - - name: imageId - type: image - imageType: linux - label: Instance Image - initial: linux - description: >- - Select a valid image for the application. Image should already be prepared and - registered in glance. - - name: keyPair - type: keypair - label: Key Pair - description: >- - Select a Key Pair to control access to instances. You can login to - instances using this KeyPair after the deployment of application. - - name: externalNetworkId - type: string - label: External Network - description: >- - Select an External Network to assign IPs to bay nodes. - - name: coe - type: string - label: Container Orchestration Engine - initial: kubernetes - description: >- - Select Container Orchestration Engine type to be created. - - name: flavorId - type: flavor - label: Bay Flavor - required: false - description: >- - Specify the nova flavor id to use when launching the bay. - - name: masterFlavorId - type: flavor - label: Master Flavor - required: false - description: >- - Specify the nova flavor id to use when launching the master node of - the bay. - - name: networkDriver - type: string - label: Network Driver - initial: flannel - required: false - description: >- - Specify the network driver name for instantiating container - networks. - - name: fixedNetwork - type: string - label: Fixed Network - required: false - description: >- - Specify the private Neutron network name to connect to this bay. - - name: dnsNameServer - type: string - label: DNS Name Server - initial: 8.8.8.8 - required: false - description: >- - Specify the DNS nameserver to use for this bay. - - name: dockerVolumeSize - type: integer - label: Docker Volume Size - required: false - description: >- - Specify the number of size in GB for the docker volume to use. - - name: labels - type: string - label: Labels - required: false - description: >- - Arbitrary labels in the form of key=value pairs to associate - with a bay. Specify in format . - - name: volumeDriver - type: string - label: Volume Driver - required: false - description: >- - Specify the volume driver name for instantiating container - volume. - - name: httpProxy - type: string - label: HTTP Proxy - required: false - description: >- - Specify the http_proxy address to use for nodes in bay. - - name: httpsProxy - type: string - label: HTTPS Proxy - required: false - description: >- - Specify the https_proxy address to use for nodes in bay. - - name: noProxy - type: string - label: No Proxy - required: false - description: >- - Specify the no_proxy address to use for nodes in bay. - - name: tlsDisabled - type: boolean - label: TLS Disabled - required: false - initial: false - description: >- - Specify true to disable TLS in the bay. - - name: public - type: boolean - label: Public - required: false - initial: false - description: >- - Specify true to make bay public. - - name: registryEnabled - type: boolean - label: Registry Enabled - required: false - initial: false - description: >- - Specify true to enable docker registry in the bay. diff --git a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/logo.png b/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/logo.png deleted file mode 100644 index bf09f0df5..000000000 Binary files a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/logo.png and /dev/null differ diff --git a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/manifest.yaml b/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/manifest.yaml deleted file mode 100644 index 319797a5b..000000000 --- a/contrib/plugins/magnum_plugin/magnum-app/com.intel.magnum.plugin.MagnumApp/manifest.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Licensed 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. - -Format: 1.0 -Type: Application -FullName: com.intel.magnum.plugin.MagnumBayApp -Name: Magnum App -Description: | - App to deploy Kubernetes cluster using Magnum -Author: 'Intel, Inc' -Tags: [Magnum, Kubernetes, Docker] -Classes: - com.intel.magnum.plugin.MagnumBaymodel: MagnumBaymodel.yaml - com.intel.magnum.plugin.MagnumBayApp: MagnumBayApp.yaml - -Require: - murano.plugins.magnum: diff --git a/contrib/plugins/magnum_plugin/magnum_plugin/__init__.py b/contrib/plugins/magnum_plugin/magnum_plugin/__init__.py deleted file mode 100644 index 10c76d4c0..000000000 --- a/contrib/plugins/magnum_plugin/magnum_plugin/__init__.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2016 Intel, Inc. -# -# Licensed 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 cfg -import time - -from magnumclient import client -from murano.common import auth_utils -from murano.dsl import session_local_storage -from oslo_config import cfg as config - -from magnumclient import exceptions - -CONF = config.CONF - - -class MagnumClient(object): - def __init__(self, this, region_name=None): - self._region_name = region_name - self._owner = this.find_owner('io.murano.Environment') - - @property - def _client(self): - region = self._region_name or ( - None if self._owner is None else self._owner['region']) - return self._create_magnum_client(region) - - @classmethod - def init_plugin(cls): - cls.CONF = cfg.init_config(CONF) - - def _wait_on_status(self, bays, bay_id, wait_status, finish_status): - while True: - # sleep 1s to wait bay status changes, this will be useful for - # the first time we wait for the status, to avoid another 30s - time.sleep(1) - status = bays.get(bay_id).status - if status in wait_status: - time.sleep(30) - elif status in finish_status: - break - else: - raise RuntimeError("Unexpected Status: {}".format(status)) - - @staticmethod - @session_local_storage.execution_session_memoize - def _create_magnum_client(region): - session = auth_utils.get_token_client_session(conf=CONF) - params = auth_utils.get_session_client_parameters( - service_type='container-infra', region=region, conf=CONF, - session=session) - return client.Client(**params) - - def create_baymodel(self, args): - baymodel = self._client.baymodels.create(**args) - return baymodel.uuid - - def delete_baymodel(self, baymodel_id): - self._client.baymodels.delete(baymodel_id) - - def get_bay_status(self, bay_id): - bays = self._client.bays - bay = bays.get(bay_id) - return bay.status - - def create_bay(self, args): - bays = self._client.bays - bay = bays.create(**args) - self._wait_on_status(bays, bay.uuid, [None, "CREATE_IN_PROGRESS"], - ["CREATE_COMPLETE", "CREATE_FAILED"]) - return bay.uuid - - def delete_bay(self, bay_id): - bays = self._client.bays - bays.delete(bay_id) - try: - self._wait_on_status(bays, bay_id, ["CREATE_COMPLETE", - "DELETE_IN_PROGRESS", "CREATE_FAILED"], - ["DELETE_COMPLETE"]) - except exceptions.NotFound: - pass diff --git a/contrib/plugins/magnum_plugin/magnum_plugin/cfg.py b/contrib/plugins/magnum_plugin/magnum_plugin/cfg.py deleted file mode 100644 index 1fc711508..000000000 --- a/contrib/plugins/magnum_plugin/magnum_plugin/cfg.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2016 Intel, Inc. -# -# Licensed 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 oslo_config import cfg - - -def init_config(conf): - opts = [ - cfg.IntOpt('api_version', default=2), - cfg.StrOpt('endpoint_type', default='publicURL') - ] - conf.register_opts(opts, group="magnum") - return conf.magnum diff --git a/contrib/plugins/magnum_plugin/requirements.txt b/contrib/plugins/magnum_plugin/requirements.txt deleted file mode 100644 index 8e337ffa9..000000000 --- a/contrib/plugins/magnum_plugin/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -python-magnumclient>=3.0.0 # Apache-2.0 diff --git a/contrib/plugins/magnum_plugin/setup.cfg b/contrib/plugins/magnum_plugin/setup.cfg deleted file mode 100644 index de478c4dc..000000000 --- a/contrib/plugins/magnum_plugin/setup.cfg +++ /dev/null @@ -1,15 +0,0 @@ -[metadata] -name = murano.plugins.magnum -description = Plugin to deploy a Magnum Bay to run docker containers on it. -summary = This plugin uses python-magnumclient to deploy a Magnum Bay. You can - deploy any of Kubernetes, Swarm and Mesos cluster with it. Just specify - 'coe' to deploy cluster of your choice and run containers on it. -author = Madhuri Kumari -author-email = madhuri.kumari@intel.com - -[files] -packages = magnum_plugin - -[entry_points] -io.murano.extensions = - mirantis.magnum.Magnum = magnum_plugin:MagnumClient diff --git a/contrib/plugins/magnum_plugin/setup.py b/contrib/plugins/magnum_plugin/setup.py deleted file mode 100644 index 25b837ae1..000000000 --- a/contrib/plugins/magnum_plugin/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2016-2017 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 setuptools - -# all other params will be taken from setup.cfg -setuptools.setup(packages=setuptools.find_packages(), - setup_requires=['pbr'], pbr=True) diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoApp.yaml b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoApp.yaml deleted file mode 100644 index 73bb4a664..000000000 --- a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoApp.yaml +++ /dev/null @@ -1,40 +0,0 @@ -Namespaces: - =: io.murano.apps.example.plugin - std: io.murano - res: io.murano.resources - sys: io.murano.system - - -Name: DemoApp - -Extends: std:Application - -Properties: - name: - Contract: $.string().notNull() - - instance: - Contract: $.class(res:Instance).notNull() - -Methods: - initialize: - Body: - - $._environment: $.find(std:Environment).require() - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $._environment.reporter.report($this, 'Creating VM ') - - $securityGroupIngress: - - ToPort: 22 - FromPort: 22 - IpProtocol: tcp - External: true - - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) - - $.instance.deploy() - - $resources: new(sys:Resources) - - $._environment.reporter.report($this, 'Test VM is installed') - - $.host: $.instance.ipAddresses[0] - - $.user: 'root' - - $.setAttr(deployed, true) diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoInstance.yaml b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoInstance.yaml deleted file mode 100644 index a9ad414f1..000000000 --- a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/DemoInstance.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Namespaces: - =: io.murano.apps.example.plugin - res: io.murano.resources - -Name: DemoInstance - -Extends: - - res:LinuxMuranoInstance - - ImageValidatorMixin - -Methods: - deploy: - Body: - - $.validateImage() - - $.super($.deploy()) diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/ImageValidatorMixin.yaml b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/ImageValidatorMixin.yaml deleted file mode 100644 index bdd4cbe94..000000000 --- a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/Classes/ImageValidatorMixin.yaml +++ /dev/null @@ -1,38 +0,0 @@ -Namespaces: - =: io.murano.apps.example.plugin - res: io.murano.resources - std: io.murano - -Name: ImageValidatorMixin - -Extends: - - res:Instance - -Properties: - requiredType: - Contract: $.string().notNull() - -Methods: - validateImage: - Body: - - $environment: $.find(std:Environment).require() - - Try: - - $glance: new('io.murano.extensions.mirantis.example.Glance', $environment) - Catch: - With: 'murano.dsl.exceptions.NoPackageForClassFound' - Do: - Throw: PluginNotFoundException - Message: 'Plugin for interaction with Glance is not installed' - - $glanceImage: $glance.getById($.image) - - If: $glanceImage = null - Then: - Throw: ImageNotFoundException - Message: 'Image with specified Id was not found' - - If: $glanceImage.meta = null - Then: - Throw: InvalidImageException - Message: 'Image does not contain Murano metadata tag' - - If: $glanceImage.meta.type != $.requiredType - Then: - Throw: InvalidImageException - Message: 'Image has unappropriate Murano type' diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/UI/ui.yaml b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/UI/ui.yaml deleted file mode 100644 index 36acf6daf..000000000 --- a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/UI/ui.yaml +++ /dev/null @@ -1,79 +0,0 @@ -Version: 2 - -Application: - ?: - type: io.murano.apps.example.plugin.DemoApp - name: $.appConfiguration.name - instance: - ?: - type: io.murano.apps.example.plugin.DemoInstance - name: generateHostname($.instanceConfiguration.unitNamingPattern, 1) - flavor: $.instanceConfiguration.flavor - image: $.instanceConfiguration.osImage - requiredType: $.appConfiguration.requiredType - assignFloatingIp: $.appConfiguration.assignFloatingIP - keyname: $.instanceConfiguration.keyPair - -Forms: - - appConfiguration: - fields: - - name: name - type: string - label: Application Name - initial: Demo - description: >- - Enter a desired name for the application. Just A-Z, a-z, 0-9, dash and - underline are allowed - - name: requiredType - type: string - label: Required MuranoImage Type - initial: linux - description: >- - Enter a value to be matched against 'type' field of MuranoImage metadata - - name: assignFloatingIP - type: boolean - label: Assign Floating IP - description: >- - Select to true to assign floating IP automatically - initial: false - required: false - widgetMedia: - css: {all: ['muranodashboard/css/checkbox.css']} - - instanceConfiguration: - fields: - - name: title - type: string - required: false - hidden: true - description: Specify some instance parameters on which the application would be created - - name: flavor - type: flavor - label: Instance flavor - description: >- - Select registered in OpenStack flavor. Consider that application performance - depends on this parameter. - required: false - - name: osImage - type: image - imageType: linux - label: Instance image - description: >- - Select a valid image for the application. Image should already be prepared and - registered in glance. - - name: keyPair - type: keypair - label: Key Pair - description: >- - Select a Key Pair to control access to instances. You can login to - instances using this KeyPair after the deployment of application. - required: false - - name: availabilityZone - type: azone - label: Availability zone - description: Select availability zone where the application would be installed. - required: false - - name: unitNamingPattern - label: Hostname - type: string - required: false - diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/logo.png b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/logo.png deleted file mode 100644 index 165bf1eef..000000000 Binary files a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/logo.png and /dev/null differ diff --git a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/manifest.yaml b/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/manifest.yaml deleted file mode 100644 index 5a42afcf7..000000000 --- a/contrib/plugins/murano_exampleplugin/example-app/io.murano.apps.demo.DemoApp/manifest.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Format: 1.0 -Type: Application -FullName: io.murano.apps.example.plugin.DemoApp -Name: Plugin Demo App -Description: | - Demo App to validate Glance Images -Author: 'Mirantis, Inc' -Tags: [Demo, Images] -Classes: - io.murano.apps.example.plugin.ImageValidatorMixin: ImageValidatorMixin.yaml - io.murano.apps.example.plugin.DemoInstance: DemoInstance.yaml - io.murano.apps.example.plugin.DemoApp: DemoApp.yaml - -Require: - murano.plugins.example: 0 \ No newline at end of file diff --git a/contrib/plugins/murano_exampleplugin/murano_exampleplugin/__init__.py b/contrib/plugins/murano_exampleplugin/murano_exampleplugin/__init__.py deleted file mode 100644 index c9bae7c03..000000000 --- a/contrib/plugins/murano_exampleplugin/murano_exampleplugin/__init__.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 json - -import cfg -import glanceclient -from oslo_config import cfg as config -from oslo_log import log as logging - -from murano.common import auth_utils -from murano.dsl import session_local_storage - -CONF = config.CONF -LOG = logging.getLogger(__name__) - - -class GlanceClient(object): - def __init__(self, this): - self._owner = this.find_owner('io.murano.Environment') - - @property - def _client(self): - region = None if self._owner is None else self._owner['region'] - return self.create_glance_client(region) - - def list(self): - images = self._client.images.list() - while True: - try: - image = next(images) - yield GlanceClient._format(image) - except StopIteration: - break - - def get_by_name(self, name): - images = list(self._client.images.list(filters={"name": name})) - if len(images) > 1: - raise AmbiguousNameException(name) - elif len(images) == 0: - return None - else: - return GlanceClient._format(images[0]) - - def get_by_id(self, imageId): - image = self._client.images.get(imageId) - return GlanceClient._format(image) - - @staticmethod - def _format(image): - res = {"id": image.id, "name": image.name} - if hasattr(image, "murano_image_info"): - res["meta"] = json.loads(image.murano_image_info) - return res - - @classmethod - def init_plugin(cls): - cls.CONF = cfg.init_config(CONF) - - @staticmethod - @session_local_storage.execution_session_memoize - def create_glance_client(region): - LOG.debug("Creating a glance client") - params = auth_utils.get_session_client_parameters( - service_type='image', conf=CONF, region=region) - return glanceclient.Client(CONF.images.api_version, **params) - - -class AmbiguousNameException(Exception): - def __init__(self, name): - super(AmbiguousNameException, self).__init__("Image name '%s'" - " is ambiguous" % name) diff --git a/contrib/plugins/murano_exampleplugin/murano_exampleplugin/cfg.py b/contrib/plugins/murano_exampleplugin/murano_exampleplugin/cfg.py deleted file mode 100644 index b2b1e016f..000000000 --- a/contrib/plugins/murano_exampleplugin/murano_exampleplugin/cfg.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 oslo_config import cfg - - -def init_config(conf): - opts = [ - cfg.IntOpt('api_version', default=2), - cfg.StrOpt('endpoint_type', default='publicURL') - ] - conf.register_opts(opts, group="images") - return conf.images diff --git a/contrib/plugins/murano_exampleplugin/requirements.txt b/contrib/plugins/murano_exampleplugin/requirements.txt deleted file mode 100644 index 5d421eca4..000000000 --- a/contrib/plugins/murano_exampleplugin/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -python-glanceclient>=3.1.0 # Apache-2.0 diff --git a/contrib/plugins/murano_exampleplugin/setup.cfg b/contrib/plugins/murano_exampleplugin/setup.cfg deleted file mode 100644 index db17cf418..000000000 --- a/contrib/plugins/murano_exampleplugin/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[metadata] -name = murano.plugins.example -description = Example Plugin to extend collection of MuranoPL system classes -summary = An example Murano Plugin demonstrating extensibility of MuranoPL - classes with code written in Python. This particular plugin uses - python-glanceclient to call OpenStack Images API to list available - images and return their ids to caller. Anther available method allows - to get murano-related metadata from image with a given id. -author = Alexander Tivelkov -author-email = ativelkov@mirantis.com - -[files] -packages = murano_exampleplugin - -[entry_points] -io.murano.extensions = - mirantis.example.Glance = murano_exampleplugin:GlanceClient diff --git a/contrib/plugins/murano_exampleplugin/setup.py b/contrib/plugins/murano_exampleplugin/setup.py deleted file mode 100644 index 2a3ea51e7..000000000 --- a/contrib/plugins/murano_exampleplugin/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 setuptools - -# all other params will be taken from setup.cfg -setuptools.setup(packages=setuptools.find_packages(), - setup_requires=['pbr'], pbr=True) diff --git a/contrib/plugins/murano_heat-translator_plugin/README.rst b/contrib/plugins/murano_heat-translator_plugin/README.rst deleted file mode 100644 index 3167210b9..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/README.rst +++ /dev/null @@ -1,25 +0,0 @@ -============================= -OASIS TOSCA Plugin for Murano -============================= -This is a plugin for Murano to support the OASIS standard for TOSCA. The -feature currently supported by this plugin is importing Murano application -definition archives of TOSCA CSARs into Murano application catalog. - - -********** -How To Use -********** -In order to make use of this plugin it has to be installed first, in the same -Python environment that Murano is running, using the pip command (i.e., run -*pip install .* from inside the plugin folder). At a minimum, the plugin -requires version *0.2.0* of the *TOSCA-Parser PyPI package*. - -Two sample Murano application definition archives are provided in unzip format: - -* hello_world -* wordpress - -In order to import the corresponding archives refer to *README.rst* inside each -sample folder to generate the archives first. The archives then will be ready -to be imported into Murano application catalog via Murano command line or -Murano UI. diff --git a/contrib/plugins/murano_heat-translator_plugin/plugin/__init__.py b/contrib/plugins/murano_heat-translator_plugin/plugin/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/contrib/plugins/murano_heat-translator_plugin/plugin/cfg.py b/contrib/plugins/murano_heat-translator_plugin/plugin/cfg.py deleted file mode 100644 index 478af7756..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/plugin/cfg.py +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed 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 oslo_config import cfg - - -def init_config(conf): - opts = [ - cfg.IntOpt('api_version', default=2), - cfg.StrOpt('endpoint_type', default='publicURL') - ] - conf.register_opts(opts, group="heat_translator") - return conf.heat_translator diff --git a/contrib/plugins/murano_heat-translator_plugin/plugin/csar_package.py b/contrib/plugins/murano_heat-translator_plugin/plugin/csar_package.py deleted file mode 100644 index 6480e0c7e..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/plugin/csar_package.py +++ /dev/null @@ -1,535 +0,0 @@ -# Licensed 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 os -import shutil -import sys -import yaml -import zipfile - -from murano.packages import exceptions -from murano.packages import package_base -from toscaparser.common import exception as csar_exception -from toscaparser.prereq import csar -from toscaparser.tosca_template import ToscaTemplate -from translator.hot.tosca_translator import TOSCATranslator - -CSAR_RESOURCES_DIR_NAME = 'Resources/' -CSAR_FILES_DIR_NAME = 'CSARFiles/' -CSAR_ENV_DIR_NAME = 'CSAREnvironments/' - - -class YAQL(object): - def __init__(self, expr): - self.expr = expr - - -class Dumper(yaml.SafeDumper): - pass - - -def yaql_representer(dumper, data): - return dumper.represent_scalar(u'!yaql', data.expr) - - -Dumper.add_representer(YAQL, yaql_representer) - - -class CSARPackage(package_base.PackageBase): - def __init__(self, format_name, runtime_version, source_directory, - manifest): - super(CSARPackage, self).__init__( - format_name, runtime_version, source_directory, manifest) - - self._translated_class = None - self._source_directory = source_directory - self._translated_ui = None - - @property - def classes(self): - return self.full_name, - - @property - def requirements(self): - return {} - - @property - def ui(self): - if not self._translated_ui: - self._translated_ui = self._translate_ui() - return self._translated_ui - - def get_class(self, name): - if name != self.full_name: - raise exceptions.PackageClassLoadError( - name, 'Class not defined in this package') - if not self._translated_class: - self._translate_class() - return self._translated_class, '' - - def _translate_class(self): - csar_file = os.path.join(self._source_directory, 'csar.zip') - shutil.copy(csar_file, self.get_resource(self.full_name)) - - if not os.path.isfile(csar_file): - raise exceptions.PackageClassLoadError( - self.full_name, 'File with class definition not found') - - csar_obj = csar.CSAR(csar_file) - try: - csar_obj.validate() - except csar_exception.ValidationError as ve: - raise exceptions.PackageFormatError('Not a CSAR archive: ' + - str(ve)) - - translated = { - 'Name': self.full_name, - 'Extends': 'io.murano.Application' - } - - csar_envs_path = os.path.join(self._source_directory, - CSAR_RESOURCES_DIR_NAME, - CSAR_ENV_DIR_NAME) - - validate_csar_parameters = (not os.path.isdir(csar_envs_path) or - not os.listdir(csar_envs_path)) - - tosca = csar_obj.get_main_template_yaml() - parameters = CSARPackage._build_properties(tosca, - validate_csar_parameters) - parameters.update(CSARPackage._translate_outputs(tosca)) - translated['Properties'] = parameters - hot = yaml.load(self._translate('tosca', csar_obj.csar, - parameters, True)) - files = CSARPackage._translate_files(self._source_directory) - - template_file = os.path.join(self._source_directory, - CSAR_RESOURCES_DIR_NAME, 'template.yaml') - with open(template_file, 'w') as outfile: - outfile.write(yaml.safe_dump(hot)) - translated.update(CSARPackage._generate_workflow(hot, files)) - self._translated_class = yaml.dump(translated, Dumper=Dumper, - default_style='"') - - def _translate(self, sourcetype, path, parsed_params, a_file): - output = None - if sourcetype == "tosca": - tosca = ToscaTemplate(path, parsed_params, a_file) - translator = TOSCATranslator(tosca, parsed_params) - output = translator.translate() - return output - - @staticmethod - def _build_properties(csar, csar_parameters): - result = { - 'generatedHeatStackName': { - 'Contract': YAQL('$.string()'), - 'Usage': 'Out' - }, - 'hotEnvironment': { - 'Contract': YAQL('$.string()'), - 'Usage': 'In' - } - } - - if csar_parameters: - params_dict = {} - for key, value in (csar.get('parameters') or {}).items(): - param_contract = \ - CSARPackage._translate_param_to_contract(value) - params_dict[key] = param_contract - result['templateParameters'] = { - 'Contract': params_dict, - 'Default': {}, - 'Usage': 'In' - } - else: - result['templateParameters'] = { - 'Contract': {}, - 'Default': {}, - 'Usage': 'In' - } - - return result - - @staticmethod - def _translate_param_to_contract(value): - contract = '$' - - parameter_type = value['type'] - if parameter_type in ('string', 'comma_delimited_list', 'json'): - contract += '.string()' - elif parameter_type == 'integer': - contract += '.int()' - elif parameter_type == 'boolean': - contract += '.bool()' - else: - raise ValueError('Unsupported parameter type ' + parameter_type) - - constraints = value.get('constraints') or [] - for constraint in constraints: - translated = CSARPackage._translate_constraint(constraint) - if translated: - contract += translated - - result = YAQL(contract) - return result - - @staticmethod - def _translate_outputs(csar): - result = {} - for key in (csar.get('outputs') or {}).keys(): - result[key] = { - "Contract": YAQL("$.string()"), - "Usage": "Out" - } - return result - - @staticmethod - def _translate_files(source_directory): - source = os.path.join(source_directory, 'csar.zip') - dest_dir = os.path.join(source_directory, CSAR_RESOURCES_DIR_NAME, - CSAR_FILES_DIR_NAME) - with zipfile.ZipFile(source, "r") as z: - z.extractall(dest_dir) - csar_files_path = os.path.join(source_directory, - CSAR_RESOURCES_DIR_NAME, - CSAR_FILES_DIR_NAME) - return CSARPackage._build_csar_resources(csar_files_path) - - @staticmethod - def _build_csar_resources(basedir): - result = [] - if os.path.isdir(basedir): - for root, _, files in os.walk(os.path.abspath(basedir)): - for f in files: - full_path = os.path.join(root, f) - relative_path = os.path.relpath(full_path, basedir) - result.append(relative_path) - return result - - @staticmethod - def _translate_constraint(constraint): - if 'equal' in constraint: - return CSARPackage._translate_equal_constraint( - constraint['equal']) - elif 'valid_values' in constraint: - return CSARPackage._translate_valid_values_constraint( - constraint['valid_values']) - elif 'length' in constraint: - return CSARPackage._translate_length_constraint( - constraint['length']) - elif 'in_range' in constraint: - return CSARPackage._translate_range_constraint( - constraint['in_range']) - elif 'allowed_pattern' in constraint: - return CSARPackage._translate_allowed_pattern_constraint( - constraint['allowed_pattern']) - - @staticmethod - def _translate_equal_constraint(value): - return ".check($ == {0})".format(value) - - @staticmethod - def _translate_allowed_pattern_constraint(value): - return ".check(matches($, '{0}'))".format(value) - - @staticmethod - def _translate_valid_values_constraint(values): - return '.check($ in list({0}))'.format( - ', '.join([CSARPackage._format_value(v) for v in values])) - - @staticmethod - def _translate_length_constraint(value): - if 'min' in value and 'max' in value: - return '.check(len($) >= {0} and len($) <= {1})'.format( - int(value['min']), int(value['max'])) - elif 'min' in value: - return '.check(len($) >= {0})'.format(int(value['min'])) - elif 'max' in value: - return '.check(len($) <= {0})'.format(int(value['max'])) - - @staticmethod - def _translate_range_constraint(value): - if 'min' in value and 'max' in value: - return '.check($ >= {0} and $ <= {1})'.format( - int(value['min']), int(value['max'])) - elif 'min' in value: - return '.check($ >= {0})'.format(int(value['min'])) - elif 'max' in value: - return '.check($ <= {0})'.format(int(value['max'])) - - @staticmethod - def _format_value(value): - if isinstance(value, str): - return u"{}".format(value) - return str(value) - - @staticmethod - def _generate_workflow(csar, files): - hot_files_map = {} - for f in files: - file_path = "$resources.string('{0}{1}')".format( - CSAR_FILES_DIR_NAME, f) - hot_files_map['../{0}'.format(f)] = YAQL(file_path) - - hot_env = YAQL("$.hotEnvironment") - - copy_outputs = [] - for key in (csar.get('outputs') or {}).keys(): - copy_outputs.append({YAQL('$.' + key): YAQL('$outputs.' + key)}) - - deploy = [ - {YAQL('$environment'): YAQL( - "$.find('io.murano.Environment').require()" - )}, - {YAQL('$reporter'): YAQL( - "new('io.murano.system.StatusReporter', " - "environment => $environment)")}, - { - 'If': YAQL('$.getAttr(generatedHeatStackName) = null'), - 'Then': [ - YAQL("$.setAttr(generatedHeatStackName, " - "'{0}_{1}'.format(randomName(), id($environment)))") - ] - }, - {YAQL('$stack'): YAQL( - "new('io.murano.system.HeatStack', $environment, " - "name => $.getAttr(generatedHeatStackName))")}, - - YAQL("$reporter.report($this, " - "'Application deployment has started')"), - - {YAQL('$resources'): YAQL("new('io.murano.system.Resources')")}, - - {YAQL('$template'): YAQL("$resources.yaml('template.yaml')")}, - YAQL('$stack.setTemplate($template)'), - {YAQL('$parameters'): YAQL("$.templateParameters")}, - YAQL('$stack.setParameters($parameters)'), - {YAQL('$files'): hot_files_map}, - YAQL('$stack.setFiles($files)'), - {YAQL('$hotEnv'): hot_env}, - { - 'If': YAQL("bool($hotEnv)"), - 'Then': [ - {YAQL('$envRelPath'): YAQL("'{0}' + $hotEnv".format( - CSAR_ENV_DIR_NAME))}, - {YAQL('$hotEnvContent'): YAQL("$resources.string(" - "$envRelPath)")}, - YAQL('$stack.setHotEnvironment($hotEnvContent)') - ] - }, - - YAQL("$reporter.report($this, 'Stack creation has started')"), - { - 'Try': [YAQL('$stack.push()')], - 'Catch': [ - { - 'As': 'e', - 'Do': [ - YAQL("$reporter.report_error($this, $e.message)"), - {'Rethrow': None} - ] - } - ], - 'Else': [ - {YAQL('$outputs'): YAQL('$stack.output()')}, - {'Do': copy_outputs}, - YAQL("$reporter.report($this, " - "'Stack was successfully created')"), - - YAQL("$reporter.report($this, " - "'Application deployment has finished')"), - ] - } - ] - - destroy = [ - {YAQL('$environment'): YAQL( - "$.find('io.murano.Environment').require()" - )}, - {YAQL('$stack'): YAQL( - "new('io.murano.system.HeatStack', $environment, " - "name => $.getAttr(generatedHeatStackName))")}, - - YAQL('$stack.delete()') - ] - - return { - 'Workflow': { - 'deploy': { - 'Body': deploy - }, - 'destroy': { - 'Body': destroy - } - } - } - - @staticmethod - def _translate_ui_parameters(tosca, title): - result_groups = [] - - used_inputs = set() - tosca_inputs = tosca.get('topology_template').get('inputs') or {} - fields = [] - properties = [] - for input in tosca_inputs: - input_value = tosca_inputs.get(input) - if input_value: - fields.append(CSARPackage._translate_ui_parameter( - input, input_value)) - used_inputs.add(input) - properties.append(input) - if fields or properties: - result_groups.append((fields, properties)) - - rest_group = [] - properties = [] - for key, value in tosca_inputs.items(): - if key not in used_inputs: - rest_group.append(CSARPackage._translate_ui_parameter( - key, value)) - properties.append(key) - if rest_group: - result_groups.append((rest_group, properties)) - - return result_groups - - @staticmethod - def _translate_ui_parameter(name, parameter_spec): - translated = { - 'name': name, - 'label': name.title().replace('_', ' ') - } - parameter_type = parameter_spec['type'] - if parameter_type == 'integer': - translated['type'] = 'integer' - elif parameter_type == 'boolean': - translated['type'] = 'boolean' - else: - # string, json, and comma_delimited_list parameters are all - # displayed as strings in UI. Any unsupported parameter would also - # be displayed as strings. - translated['type'] = 'string' - - label = parameter_spec.get('label') - if label: - translated['label'] = label - - if 'description' in parameter_spec: - translated['description'] = parameter_spec['description'] - - if 'default' in parameter_spec: - translated['initial'] = parameter_spec['default'] - translated['required'] = False - else: - translated['required'] = True - - constraints = parameter_spec.get('constraints') or [] - translated_constraints = [] - - for constraint in constraints: - if 'length' in constraint: - spec = constraint['length'] - if 'min' in spec: - translated['minLength'] = max( - translated.get('minLength', -sys.maxsize - 1), - int(spec['min'])) - if 'max' in spec: - translated['maxLength'] = min( - translated.get('maxLength', sys.maxsize), - int(spec['max'])) - - elif 'range' in constraint: - spec = constraint['range'] - if 'min' in spec and 'max' in spec: - ui_constraint = { - 'expr': YAQL('$ >= {0} and $ <= {1}'.format( - spec['min'], spec['max'])) - } - elif 'min' in spec: - ui_constraint = { - 'expr': YAQL('$ >= {0}'.format(spec['min'])) - } - else: - ui_constraint = { - 'expr': YAQL('$ <= {0}'.format(spec['max'])) - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - elif 'valid_values' in constraint: - values = constraint['valid_values'] - ui_constraint = { - 'expr': YAQL('$ in list({0})'.format(', '.join( - [CSARPackage._format_value(v) for v in values]))) - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - elif 'allowed_pattern' in constraint: - pattern = constraint['allowed_pattern'] - ui_constraint = { - 'expr': { - 'regexpValidator': pattern - } - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - if translated_constraints: - translated['validators'] = translated_constraints - - return translated - - @staticmethod - def _generate_application_ui(groups, type_name, package_name=None, - package_version=None): - app = { - '?': { - 'type': type_name - } - } - if package_name: - app['?']['package'] = package_name - if package_version: - app['?']['classVersion'] = package_version - - for i, record in enumerate(groups): - section = app.setdefault('templateParameters', {}) - - for property_name in record[1]: - section[property_name] = YAQL( - '$.group{0}.{1}'.format(i, property_name)) - - return app - - def _translate_ui(self): - tosca = csar.CSAR(os.path.join(self._source_directory, 'csar.zip'))\ - .get_main_template_yaml() - - groups = CSARPackage._translate_ui_parameters(tosca, self.description) - forms = [] - for i, record in enumerate(groups): - forms.append({'group{0}'.format(i): {'fields': record[0]}}) - - translated = { - 'Version': 2.2, - 'Application': CSARPackage._generate_application_ui( - groups, self.full_name, self.full_name, str(self.version)), - 'Forms': forms - } - return yaml.dump(translated, Dumper=Dumper, default_style='"') diff --git a/contrib/plugins/murano_heat-translator_plugin/requirements.txt b/contrib/plugins/murano_heat-translator_plugin/requirements.txt deleted file mode 100644 index 06c5e4c7f..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -heat-translator>=2.0.0 # Apache-2.0 -tosca-parser>=2.0.0 # Apache-2.0 diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/README.rst b/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/README.rst deleted file mode 100644 index 35cc30256..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -================================================================= -Build Murano Application Definition Archive from hello_world CSAR -================================================================= -In order to build a Murano application definition archive from the hello_world -CSAR and the corresponding logo and manifest files, from inside the hello_world -folder run following commands: - -1. Download archive from https://github.com/openstack/heat-translator/raw/0.4.0/translator/tests/data/csar_hello_world.zip -2. Rename it to 'csar.zip' -3. *zip csar_helloworld_murano_package.zip csar.zip logo.png manifest.yaml* - -The resulting file *csar_helloworld_murano_package.zip* is the application -definition archive that can be imported into the Murano application catalog. diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/logo.png b/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/logo.png deleted file mode 100644 index 01b230dd5..000000000 Binary files a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/logo.png and /dev/null differ diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/manifest.yaml b/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/manifest.yaml deleted file mode 100644 index d946b07fb..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/sample/hello_world/manifest.yaml +++ /dev/null @@ -1,9 +0,0 @@ -Author: OASIS TOSCA TC -Description: Template for deploying a single server with predefined properties. -Format: TOSCA.CSAR/1.1.0 -FullName: io.murano.apps.generated.CsarHelloWorld -Name: csar_hello_world -Tags: -- TOSCA-CSAR-generated -Template: tosca_helloworld.yaml -Type: Application diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/README.rst b/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/README.rst deleted file mode 100644 index c79b4cc1c..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/README.rst +++ /dev/null @@ -1,13 +0,0 @@ -=============================================================== -Build Murano Application Definition Archive from wordpress CSAR -=============================================================== -In order to build a Murano application definition archive from the wordpress -CSAR and the corresponding logo and manifest files, from inside the wordpress -folder run this command: - -1. Download archive from https://github.com/openstack/heat-translator/raw/0.4.0/translator/tests/data/csar_single_instance_wordpress.zip -2. Rename it to 'csar.zip' -3. *zip csar_wordpress_murano_package.zip csar.zip logo.png manifest.yaml* - -The resulting file *csar_wordpress_murano_package.zip* is the application -definition archive that can be imported into the Murano application catalog. diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/logo.png b/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/logo.png deleted file mode 100644 index 01b230dd5..000000000 Binary files a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/logo.png and /dev/null differ diff --git a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/manifest.yaml b/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/manifest.yaml deleted file mode 100644 index ef942d7a5..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/sample/wordpress/manifest.yaml +++ /dev/null @@ -1,12 +0,0 @@ -Author: OASIS TOSCA TC -Description: 'TOSCA simple profile with wordpress, web server and mysql on the same - server. - - ' -Format: TOSCA.CSAR/1.1 -FullName: io.murano.apps.generated.CsarWordpress -Name: csar_wordpress -Tags: -- TOSCA-CSAR-generated -Template: Definitions/tosca_single_instance_wordpress.yaml -Type: Application diff --git a/contrib/plugins/murano_heat-translator_plugin/setup.cfg b/contrib/plugins/murano_heat-translator_plugin/setup.cfg deleted file mode 100644 index 3f9b6ce52..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/setup.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[metadata] -name = io.murano.plugins.oasis.tosca -description = Heat-Translator Plugin for Murano -summary = This plugin enables import and deployment of OASIS TOSCA - application specifications in Murano. The plugin makes use - of tosca-parser pypi library for package imports and - heat-translator pypi library for package deployment. - Deployment support is not yet added and will come soon. -author = Vahid Hashemian -author-email = vahidhashemian@us.ibm.com - -[files] -packages = murano_heat-translator_plugin - -[entry_points] -io.murano.plugins.packages = - TOSCA.CSAR/1.1.0 = plugin.csar_package:CSARPackage diff --git a/contrib/plugins/murano_heat-translator_plugin/setup.py b/contrib/plugins/murano_heat-translator_plugin/setup.py deleted file mode 100644 index 2a3ea51e7..000000000 --- a/contrib/plugins/murano_heat-translator_plugin/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2011-2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 setuptools - -# all other params will be taken from setup.cfg -setuptools.setup(packages=setuptools.find_packages(), - setup_requires=['pbr'], pbr=True) diff --git a/devstack/README.rst b/devstack/README.rst deleted file mode 100644 index e6c4288f2..000000000 --- a/devstack/README.rst +++ /dev/null @@ -1,52 +0,0 @@ -==================== -Enabling in Devstack -==================== - -#. Download DevStack_:: - - git clone https://opendev.org/openstack/devstack - cd devstack - -#. Edit ``local.conf`` to enable murano and heat devstack plugin:: - - > cat local.conf - [[local|localrc]] - enable_plugin murano https://opendev.org/openstack/murano - - #Enable heat plugin - enable_plugin heat https://opendev.org/openstack/heat - -#. If you want Murano Cloud Foundry Broker API service enabled, add the - following line to ``local.conf``:: - - enable_service murano-cfapi - -#. If you want to use Glare Artifact Repository as a storage for packages, - add the following line to ``local.conf``: - - .. code-block:: ini - - enable_service g-glare - - For more information on how to use Glare Artifact Repository, - see :ref:`glare_usage`. - -#. (Optional) To import Murano packages when DevStack is up, define an ordered - list of packages FQDNs in ``local.conf``. Make sure to list all package - dependencies. These packages will by default be imported from the murano-apps - git repository. - - Example:: - - MURANO_APPS=com.example.apache.Tomcat,org.openstack.Rally - - You can also use the variables ``MURANO_APPS_REPO`` and ``MURANO_APPS_BRANCH`` - to configure the git repository which will be used as the source for the - imported packages. - -#. Install DevStack:: - - ./stack.sh - - -.. _DevStack: https://docs.openstack.org/devstack/latest/ diff --git a/devstack/files/apache-murano-api.template b/devstack/files/apache-murano-api.template deleted file mode 100644 index 290a7552a..000000000 --- a/devstack/files/apache-murano-api.template +++ /dev/null @@ -1,25 +0,0 @@ -Listen %PUBLICPORT% - - - WSGIDaemonProcess murano-api processes=1 threads=10 user=%USER% display-name=%{GROUP} %VIRTUALENV% - WSGIProcessGroup murano-api - WSGIScriptAlias / %MURANO_BIN_DIR%/murano-wsgi-api - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - AllowEncodedSlashes On - = 2.4> - ErrorLogFormat "%{cu}t %M" - - ErrorLog /var/log/%APACHE_NAME%/murano_api.log - CustomLog /var/log/%APACHE_NAME%/murano_api_access.log combined - - - = 2.4> - Require all granted - - - Order allow,deny - Allow from all - - - diff --git a/devstack/files/debs/murano b/devstack/files/debs/murano deleted file mode 100644 index d0513b233..000000000 --- a/devstack/files/debs/murano +++ /dev/null @@ -1 +0,0 @@ -zip diff --git a/devstack/files/rpms/murano b/devstack/files/rpms/murano deleted file mode 100644 index d0513b233..000000000 --- a/devstack/files/rpms/murano +++ /dev/null @@ -1 +0,0 @@ -zip diff --git a/devstack/plugin.sh b/devstack/plugin.sh deleted file mode 100755 index 0e2b08dac..000000000 --- a/devstack/plugin.sh +++ /dev/null @@ -1,685 +0,0 @@ -#!/usr/bin/env bash -# Plugin file for Murano services -# ------------------------------- - -# Dependencies: -# ``functions`` file -# ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined - -# Save trace setting -XTRACE=$(set +o | grep xtrace) -set -o xtrace - - -# Support entry points installation of console scripts -if [[ -d $MURANO_DIR/bin ]]; then - MURANO_BIN_DIR=$MURANO_DIR/bin -else - MURANO_BIN_DIR=$(get_python_exec_prefix) -fi - -MURANO_AUTH_CACHE_DIR=${MURANO_AUTH_CACHE_DIR:-/var/cache/murano} - -# Toggle for deploying Murano-API under under a wsgi server -MURANO_USE_UWSGI=${MURANO_USE_UWSGI:-True} - -MURANO_UWSGI=$MURANO_BIN_DIR/murano-wsgi-api -MURANO_UWSGI_CONF=$MURANO_CONF_DIR/murano-api-uwsgi.ini - -if [[ "$MURANO_USE_UWSGI" == "True" ]]; then - MURANO_API_URL="$MURANO_SERVICE_PROTOCOL://$MURANO_SERVICE_HOST/application-catalog" -else - MURANO_API_URL="$MURANO_SERVICE_PROTOCOL://$MURANO_SERVICE_HOST:$MURANO_SERVICE_PORT" -fi - -HORIZON_URL="http://${KEYSTONE_SERVICE_HOST}/dashboard" - -DASHBOARD_FUNCTIONAL_CONFIG_DIR=$MURANO_DASHBOARD_DIR/muranodashboard/tests/functional/config - -# create_murano_accounts() - Set up common required murano accounts -# -# Tenant User Roles -# ------------------------------ -# service murano admin -function create_murano_accounts() { - if ! is_service_enabled key; then - return - fi - - create_service_user "murano" "admin" - - get_or_create_service "murano" "application-catalog" "Application Catalog Service" - get_or_create_endpoint "application-catalog" \ - "$REGION_NAME" \ - "$MURANO_API_URL" \ - "$MURANO_API_URL" \ - "$MURANO_API_URL" - - if is_service_enabled murano-cfapi; then - get_or_create_service "murano-cfapi" "service-broker" "Murano CloudFoundry Service Broker" - get_or_create_endpoint "service-broker" \ - "$REGION_NAME" \ - "$MURANO_SERVICE_PROTOCOL://$MURANO_SERVICE_HOST:$MURANO_CFAPI_SERVICE_PORT" \ - "$MURANO_SERVICE_PROTOCOL://$MURANO_SERVICE_HOST:$MURANO_CFAPI_SERVICE_PORT" \ - "$MURANO_SERVICE_PROTOCOL://$MURANO_SERVICE_HOST:$MURANO_CFAPI_SERVICE_PORT" - fi -} - - -function mkdir_chown_stack { - if [[ ! -d "$1" ]]; then - sudo mkdir -p "$1" - fi - sudo chown $STACK_USER "$1" -} - - -function configure_murano_rpc_backend() { - # Configure the rpc service. - iniset_rpc_backend muranoapi $MURANO_CONF_FILE DEFAULT - - # TODO(ruhe): get rid of this ugly workaround. - inicomment $MURANO_CONF_FILE DEFAULT rpc_backend - - if [[ $SERVICE_IP_VERSION == 6 ]]; then - iniset $MURANO_CONF_FILE rabbitmq host "$HOST_IPV6" - else - iniset $MURANO_CONF_FILE rabbitmq host "$HOST_IP" - fi - iniset $MURANO_CONF_FILE rabbitmq login $RABBIT_USERID - iniset $MURANO_CONF_FILE rabbitmq password $RABBIT_PASSWORD - - # Set non-default rabbit virtual host if required. - if [[ -n "$MURANO_RABBIT_VHOST" ]]; then - iniset $MURANO_CONF_FILE DEFAULT rabbit_virtual_host $MURANO_RABBIT_VHOST - iniset $MURANO_CONF_FILE rabbitmq virtual_host $MURANO_RABBIT_VHOST - fi -} - -function configure_murano_glare_backend() { - # Configure Murano to use GlARe application storage backend - iniset $MURANO_CONF_FILE engine packages_service 'glare' - if is_service_enabled murano-cfapi; then - iniset $MURANO_CFAPI_CONF_FILE cfapi packages_service 'glare' - fi - iniset $MURANO_CONF_FILE glare url $GLANCE_SERVICE_PROTOCOL://$GLANCE_GLARE_HOSTPORT - iniset $MURANO_CONF_FILE glare endpoint_type $GLARE_ENDPOINT_TYPE - echo -e $"\nexport MURANO_PACKAGES_SERVICE='glare'" | sudo tee -a $TOP_DIR/openrc - echo -e $"\nexport GLARE_URL='$GLANCE_SERVICE_PROTOCOL://$GLANCE_GLARE_HOSTPORT'" | sudo tee -a $TOP_DIR/openrc -} - -function restart_glare_service() { - # Restart GlARe service to apply Murano artifact plugin - if is_running glance-glare; then - echo_summary "Restarting GlARe to apply config changes" - stop_process g-glare - run_process g-glare "$GLANCE_BIN_DIR/glance-glare --config-file=$GLANCE_CONF_DIR/glance-glare.conf" - echo "Waiting for GlARe [g-glare] ($GLANCE_GLARE_HOSTPORT) to start..." - if ! wait_for_service $SERVICE_TIMEOUT $GLANCE_SERVICE_PROTOCOL://$GLANCE_GLARE_HOSTPORT; then - die $LINENO " GlARe [g-glare] did not start" - fi - else - echo_summary "GlARe service wasn't started yet. It will start in usual way." - fi -} - -function install_murano_artifact_plugin() { - # Provide support of Murano artifacts type to GlARe - setup_package $MURANO_DIR/contrib/glance -e -} - -function is_murano_backend_glare() { - is_service_enabled g-glare && [[ "$MURANO_USE_GLARE" == "True" ]] && return 0 - return 1 -} - -function configure_murano_networking { - # Use keyword 'public' if Murano external network was not set. - # If it was set but the network is not exist then - # first available external network will be selected. - local ext_net=${MURANO_EXTERNAL_NETWORK:-'public'} - local ext_net_id=$(openstack --os-cloud=devstack-admin \ - --os-region-name="$REGION_NAME" network list \ - --external | grep " $ext_net " | get_field 1) - - # Try to select first available external network if ext_net_id is null - if [[ ! -n "$ext_net_id" ]]; then - ext_net_id=$(openstack --os-cloud=devstack-admin \ - --os-region-name="$REGION_NAME" network list \ - --external -f csv -c ID | tail -n +2 | tail -n 1) - fi - - # Configure networking options for Murano - if [[ -n "$ext_net" ]] && [[ -n "$ext_net_id" ]]; then - iniset $MURANO_CONF_FILE networking external_network $ext_net_id - iniset $MURANO_CONF_FILE networking create_router 'true' - else - iniset $MURANO_CONF_FILE networking create_router 'false' - fi - - if [[ -n "$MURANO_DEFAULT_ROUTER" ]]; then - iniset $MURANO_CONF_FILE networking router_name $MURANO_DEFAULT_ROUTER - fi - - if [[ -n "$MURANO_DEFAULT_DNS" ]]; then - iniset $MURANO_CONF_FILE networking default_dns $MURANO_DEFAULT_DNS - fi -} - -# Entry points -# ------------ - -# configure_murano() - Set config files, create data dirs, etc -function configure_murano { - mkdir_chown_stack "$MURANO_CONF_DIR" - - # Generate Murano configuration file and configure common parameters. - oslo-config-generator --config-file $MURANO_DIR/etc/oslo-config-generator/murano.conf --output-file $MURANO_CONF_FILE - - cp $MURANO_DIR/etc/murano/murano-paste.ini $MURANO_CONF_DIR - - cleanup_murano - - iniset $MURANO_CONF_FILE DEFAULT debug $MURANO_DEBUG - iniset $MURANO_CONF_FILE DEFAULT use_syslog $SYSLOG - # Format logging - if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ] && [ "$MURANO_USE_UWSGI" == "False" ] ; then - setup_colorized_logging $MURANO_CONF_FILE DEFAULT - else - # Show user_name and project_name instead of user_id and project_id - iniset $MURANO_CONF_FILE DEFAULT logging_context_format_string "%(asctime)s.%(msecs)03d %(levelname)s %(name)s [%(request_id)s %(user_name)s %(project_name)s] %(instance)s%(message)s" - fi - - iniset $MURANO_CONF_FILE DEFAULT home_region $REGION_NAME - - # Murano Policy Enforcement Configuration - if [[ "$MURANO_ENABLE_MODEL_POLICY_ENFORCEMENT" == "True" ]]; then - iniset $MURANO_CONF_FILE engine enable_model_policy_enforcer $MURANO_ENABLE_MODEL_POLICY_ENFORCEMENT - fi - - # Murano Api Configuration - #------------------------- - - # Setup keystone_authtoken section - configure_auth_token_middleware $MURANO_CONF_FILE $MURANO_ADMIN_USER $MURANO_AUTH_CACHE_DIR - - # Setup murano_auth section - configure_auth_token_middleware $MURANO_CONF_FILE $MURANO_ADMIN_USER $MURANO_AUTH_CACHE_DIR murano_auth - iniset $MURANO_CONF_FILE murano_auth www_authenticate_uri $KEYSTONE_AUTH_URI - - configure_murano_rpc_backend - - # Configure notifications for status information during provisioning - iniset $MURANO_CONF_FILE oslo_messaging_notifications driver messagingv2 - - # configure the database. - iniset $MURANO_CONF_FILE database connection `database_connection_url murano` - - # Configure keystone auth url - iniset $MURANO_CONF_FILE keystone auth_url $KEYSTONE_SERVICE_URI - - # Configure Murano API URL - iniset $MURANO_CONF_FILE murano url "$MURANO_API_URL" - - # Configure the number of api workers - if [[ -n "$MURANO_API_WORKERS" ]]; then - iniset $MURANO_CONF_FILE murano api_workers $MURANO_API_WORKERS - fi - - # Configure the number of engine workers - if [[ -n "$MURANO_ENGINE_WORKERS" ]]; then - iniset $MURANO_CONF_FILE engine engine_workers $MURANO_ENGINE_WORKERS - fi - if is_murano_backend_glare; then - configure_murano_glare_backend - fi - - if [ "$MURANO_USE_UWSGI" == "True" ]; then - write_uwsgi_config "$MURANO_UWSGI_CONF" "$MURANO_UWSGI" "/application-catalog" - fi - -} - -# set the murano packages service backend -function set_packages_service_backend() { - if is_murano_backend_glare; then - MURANO_PACKAGES_SERVICE='glare' - else - MURANO_PACKAGES_SERVICE='murano' - fi -} - -# configure_murano_cfapi() - Set config files -function configure_murano_cfapi { - - # Generate Murano configuration file and configure common parameters. - oslo-config-generator --config-file $MURANO_DIR/etc/oslo-config-generator/murano-cfapi.conf --output-file $MURANO_CFAPI_CONF_FILE - - cp $MURANO_DIR/etc/murano/murano-cfapi-paste.ini $MURANO_CONF_DIR - - configure_service_broker - -} - -# install_murano_apps() - Install Murano apps from repository murano-apps, if required -function install_murano_apps() { - if [[ -z $MURANO_APPS ]]; then - return - fi - - # clone murano-apps only if app installation is required - git_clone $MURANO_APPS_REPO $MURANO_APPS_DIR $MURANO_APPS_BRANCH - - set_packages_service_backend - - # install Murano apps defined in the comma-separated list $MURANO_APPS - for murano_app in ${MURANO_APPS//,/ }; do - find $MURANO_APPS_DIR -type d -name "package" | while read package; do - full_name=$(grep "FullName" "$package/manifest.yaml" | awk -F ':' '{print $2}' | tr -d ' ') - if [[ $full_name = $murano_app ]]; then - pushd $package - zip -r app.zip . - murano --os-username $OS_USERNAME \ - --os-password $OS_PASSWORD \ - --os-tenant-name $OS_PROJECT_NAME \ - --os-auth-url $KEYSTONE_SERVICE_URI \ - --murano-url $MURANO_API_URL \ - --glare-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_GLARE_HOSTPORT \ - --murano-packages-service $MURANO_PACKAGES_SERVICE \ - package-import \ - --is-public \ - --exists-action u \ - app.zip - popd - fi - done - done -} - - -# configure_service_broker() - set service broker specific options to config -function configure_service_broker { - - iniset $MURANO_CFAPI_CONF_FILE DEFAULT debug $MURANO_DEBUG - iniset $MURANO_CFAPI_CONF_FILE DEFAULT use_syslog $SYSLOG - - #Add needed options to murano-cfapi.conf - iniset $MURANO_CFAPI_CONF_FILE cfapi tenant "$MURANO_CFAPI_DEFAULT_TENANT" - iniset $MURANO_CFAPI_CONF_FILE cfapi bind_host "$MURANO_SERVICE_HOST" - iniset $MURANO_CFAPI_CONF_FILE cfapi bind_port "$MURANO_CFAPI_SERVICE_PORT" - iniset $MURANO_CFAPI_CONF_FILE cfapi auth_url "$KEYSTONE_SERVICE_URI" - - # configure the database. - iniset $MURANO_CFAPI_CONF_FILE database connection `database_connection_url murano_cfapi` - - # Setup keystone_authtoken section - configure_auth_token_middleware $MURANO_CFAPI_CONF_FILE $MURANO_ADMIN_USER $MURANO_AUTH_CACHE_DIR - -} - -function prepare_core_apps() { - cd $MURANO_DIR/meta - for i in */ - do pushd ./"$i" - zip -r ../"${i%/}.zip" * - popd - done -} - -function remove_core_apps_zip() { - rm -f $MURANO_DIR/meta/*.zip -} - -# init_murano() - Initialize databases, etc. -function init_murano() { - configure_murano_networking - - # (re)create Murano database - recreate_database murano utf8 - - $MURANO_BIN_DIR/murano-db-manage --config-file $MURANO_CONF_FILE upgrade - - create_murano_cache_dir - -} - -# create_murano_cache_dir() - Part of the init_murano() process -function create_murano_cache_dir { - # Create cache dirs - sudo install -d -o $STACK_USER $MURANO_AUTH_CACHE_DIR -} - - -# init_murano_cfapi() - Initialize databases, etc. -function init_murano_cfapi() { - - # (re)create Murano database - recreate_database murano_cfapi utf8 - - $MURANO_BIN_DIR/murano-cfapi-db-manage --config-file $MURANO_CFAPI_CONF_FILE upgrade -} - -function setup_core_library() { - prepare_core_apps - - set_packages_service_backend - - murano --os-username admin \ - --os-password $ADMIN_PASSWORD \ - --os-tenant-name admin \ - --os-auth-url $KEYSTONE_SERVICE_URI \ - --os-region-name $REGION_NAME \ - --murano-url $MURANO_API_URL \ - --glare-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_GLARE_HOSTPORT \ - --murano-packages-service $MURANO_PACKAGES_SERVICE \ - package-import $MURANO_DIR/meta/*.zip \ - --is-public - - remove_core_apps_zip -} - -# install_murano() - Collect source and prepare -function install_murano() { - install_murano_pythonclient - - git_clone $MURANO_REPO $MURANO_DIR $MURANO_BRANCH - - setup_develop $MURANO_DIR - - if is_murano_backend_glare; then - install_murano_artifact_plugin - fi - -} - -function install_murano_pythonclient() { -# For using non-released client from git branch, need to add -# LIBS_FROM_GIT=python-muranoclient parameter to localrc. -# Otherwise, murano will install python-muranoclient from requirements. - if use_library_from_git "python-muranoclient"; then - git_clone_by_name "python-muranoclient" - setup_dev_lib "python-muranoclient" - # Installing bash_completion for murano - sudo install -D -m 0644 -o $STACK_USER {${GITDIR["python-muranoclient"]}/tools/,/etc/bash_completion.d/}murano.bash_completion - fi -} - - -# start_murano() - Start running processes, including screen -function start_murano() { - if [ "$MURANO_USE_UWSGI" == "True" ]; then - run_process murano-api "$(which uwsgi) --procname-prefix murano-api --ini $MURANO_UWSGI_CONF" - else - run_process murano-api "$MURANO_BIN_DIR/murano-api --config-file $MURANO_CONF_DIR/murano.conf" - fi - run_process murano-engine "$MURANO_BIN_DIR/murano-engine --config-file $MURANO_CONF_DIR/murano.conf" -} - - -# stop_murano() - Stop running processes -function stop_murano() { - # Kill the Murano screen windows - if [ "$MURANO_USE_UWSGI" == "True" ]; then - disable_apache_site murano-api - restart_apache_server - fi - stop_process murano-api - stop_process murano-engine -} - - -# start_service_broker() - start murano CF service broker -function start_service_broker() { - run_process murano-cfapi "$MURANO_BIN_DIR/murano-cfapi --config-file $MURANO_CONF_DIR/murano-cfapi.conf" -} - - -# stop_service_broker() - stop murano CF service broker -function stop_service_broker() { - # Kill the Murano screen windows - stop_process murano-cfapi -} - - -function cleanup_murano() { - - # Cleanup keystone signing dir - sudo rm -rf $MURANO_KEYSTONE_SIGNING_DIR - - if [[ "$MURANO_USE_UWSGI" == "True" ]]; then - remove_uwsgi_config "$MURANO_UWSGI_CONF" "$MURANO_UWSGI" - fi - -} - -function configure_murano_tempest_plugin() { - - # Check tempest for enabling - if is_service_enabled tempest; then - echo_summary "Configuring Murano Tempest plugin" - # Set murano service availability flag - iniset $TEMPEST_CONFIG service_available murano "True" - if is_service_enabled murano-cfapi; then - # Enable Service Broker tests if cfapi enabled and set murano-cfapi service availability flag - iniset $TEMPEST_CONFIG service_available murano_cfapi "True" - iniset $TEMPEST_CONFIG service_broker run_service_broker_tests "True" - fi - if is_service_enabled g-glare; then - # TODO(freerunner): This is bad way to configure tempest to - # TODO see glare as enabled. We need to move it out to tempest - # TODO of glance repo when glare become official OS API. - iniset $TEMPEST_CONFIG service_available glare "True" - fi - if is_murano_backend_glare; then - iniset $TEMPEST_CONFIG application_catalog glare_backend "True" - fi - if [[ "$TEMPEST_MURANO_SCENARIO_TESTS_ENABLED" == "True" ]]; then - if is_service_enabled cinder; then - iniset $TEMPEST_CONFIG application_catalog cinder_volume_tests "True" - fi - if [[ "$TEMPEST_MURANO_DEPLOYMENT_TESTS_ENABLED" == "True" ]]; then - iniset $TEMPEST_CONFIG application_catalog deployment_tests "True" - iniset $TEMPEST_CONFIG application_catalog linux_image "$CLOUD_IMAGE_NAME" - fi - fi - fi -} - -#### lib/murano-dashboard - -# Dependencies: -# -# - ``functions`` file -# - ``DEST``, ``DATA_DIR``, ``STACK_USER`` must be defined -# - ``SERVICE_HOST`` - -# ``stack.sh`` calls the entry points in this order: -# -# - install_murano_dashboard -# - configure_murano_dashboard -# - cleanup_murano_dashboard - -. $TOP_DIR/lib/horizon - -# Defaults -# -------- - -HORIZON_CONFIG=${HORIZON_CONFIG:-$HORIZON_DIR/openstack_dashboard/settings.py} -HORIZON_LOCAL_CONFIG=${HORIZON_LOCAL_CONFIG:-$HORIZON_DIR/openstack_dashboard/local/local_settings.py} - -# Set up default repos -MURANO_DASHBOARD_REPO=${MURANO_DASHBOARD_REPO:-${GIT_BASE}/openstack/murano-dashboard.git} -MURANO_DASHBOARD_BRANCH=${MURANO_DASHBOARD_BRANCH:-master} - -# Set up default directories -MURANO_DASHBOARD_DIR=$DEST/murano-dashboard -MURANO_PYTHONCLIENT_DIR=$DEST/python-muranoclient - -MURANO_DASHBOARD_CACHE_DIR=${MURANO_DASHBOARD_CACHE_DIR:-/tmp/murano} - -MURANO_REPOSITORY_URL=${MURANO_REPOSITORY_URL:-'http://apps.openstack.org/api/v1/murano_repo/liberty/'} - -# Entry points -# ------------ - -# configure_murano_dashboard() - Set config files, create data dirs, etc -function configure_murano_dashboard() { - configure_local_settings_py - - configure_dashboard_functional_config - - restart_apache_server -} - - -function configure_dashboard_functional_config() { - cp $DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf.sample $DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf - sed -e "s%\(^\s*horizon_url\s*=\).*$%\1 $HORIZON_URL%" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - sed -e "s%\(^\s*murano_url\s*=\).*$%\1 $MURANO_API_URL%" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - sed -e "s/\(^\s*user\s*=\).*$/\1 $OS_USERNAME/" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - sed -e "s/\(^\s*password\s*=\).*$/\1 $OS_PASSWORD/" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - sed -e "s/\(^\s*tenant\s*=\).*$/\1 $OS_PROJECT_NAME/" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - sed -e "s%\(^\s*keystone_url\s*=\).*$%\1 $KEYSTONE_SERVICE_URI/v3%" -i "$DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf" - - echo_summary "Show the DASHBOARD_FUNCTIONAL_CONFIG" - cat $DASHBOARD_FUNCTIONAL_CONFIG_DIR/config.conf -} - - -function configure_local_settings_py() { - local horizon_config_part=$(mktemp) - - mkdir_chown_stack "$MURANO_DASHBOARD_CACHE_DIR" - - if is_murano_backend_glare; then - # Make Murano use GlARe only if MURANO_USE_GLARE set to True and GlARe - # service is enabled - local murano_use_glare=True - else - local murano_use_glare=False - fi - - if [[ -f "$HORIZON_LOCAL_CONFIG" ]]; then - sed -e "s/\(^\s*OPENSTACK_HOST\s*=\).*$/\1 '$HOST_IP'/" -i "$HORIZON_LOCAL_CONFIG" - fi - - # Install Murano as plugin for Horizon - ln -sf $MURANO_DASHBOARD_DIR/muranodashboard/local/enabled/*.py $HORIZON_DIR/openstack_dashboard/local/enabled/ - - # Install setting to Horizon - ln -sf $MURANO_DASHBOARD_DIR/muranodashboard/local/local_settings.d/*.py $HORIZON_DIR/openstack_dashboard/local/local_settings.d/ - - # Install murano RBAC policy to Horizon - ln -sf $MURANO_DASHBOARD_DIR/muranodashboard/conf/murano_policy.json $HORIZON_DIR/openstack_dashboard/conf/ - - # Change Murano dashboard settings - sed -e "s/\(^\s*MURANO_USE_GLARE\s*=\).*$/\1 $murano_use_glare/" -i $HORIZON_DIR/openstack_dashboard/local/local_settings.d/_50_murano.py - sed -e "s%\(^\s*MURANO_REPO_URL\s*=\).*$%\1 '$MURANO_REPOSITORY_URL'%" -i $HORIZON_DIR/openstack_dashboard/local/local_settings.d/_50_murano.py - sed -e "s%\(^\s*'NAME':\).*$%\1 os.path.join('$MURANO_DASHBOARD_DIR', 'openstack-dashboard.sqlite')%" -i $HORIZON_DIR/openstack_dashboard/local/local_settings.d/_50_murano.py - echo -e $"\nMETADATA_CACHE_DIR = '$MURANO_DASHBOARD_CACHE_DIR'" | sudo tee -a $HORIZON_DIR/openstack_dashboard/local/local_settings.d/_50_murano.py - -} - -# init_murano_dashboard() - Initialize databases, etc. -function init_murano_dashboard() { - # clean up from previous (possibly aborted) runs - # create required data files - - local horizon_manage_py="$HORIZON_DIR/manage.py" - - $PYTHON "$horizon_manage_py" collectstatic --noinput - $PYTHON "$horizon_manage_py" compress --force - $PYTHON "$horizon_manage_py" migrate --noinput - - # Compile message for murano-dashboard - cd $MURANO_DASHBOARD_DIR/muranodashboard - $PYTHON "$horizon_manage_py" compilemessages - - restart_apache_server -} - - -# install_murano_dashboard() - Collect source and prepare -function install_murano_dashboard() { - echo_summary "Install Murano Dashboard" - - git_clone $MURANO_DASHBOARD_REPO $MURANO_DASHBOARD_DIR $MURANO_DASHBOARD_BRANCH - - setup_develop $MURANO_DASHBOARD_DIR -} - - -# cleanup_murano_dashboard() - Remove residual data files, anything left over from previous -# runs that a clean run would need to clean up -function cleanup_murano_dashboard() { - echo_summary "Cleanup Murano Dashboard" - - # remove all the pannels we've installed, also any pyc/pyo files - for i in $(find $MURANO_DASHBOARD_DIR/muranodashboard/local/enabled -iname '_[0-9]*.py' -printf '%f\n'); do - rm -rf $HORIZON_DIR/openstack_dashboard/local/enabled/${i%.*}.* - done - - rm $HORIZON_DIR/openstack_dashboard/local/local_settings.d/_50_murano.* - - rm $HORIZON_DIR/openstack_dashboard/conf/murano_policy.json -} - -# Main dispatcher - -if is_service_enabled murano; then - if [[ "$1" == "stack" && "$2" == "install" ]]; then - echo_summary "Installing Murano" - install_murano - if is_service_enabled horizon; then - install_murano_dashboard - fi - elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then - echo_summary "Configuring Murano" - configure_murano - create_murano_accounts - if is_service_enabled horizon; then - configure_murano_dashboard - fi - if is_service_enabled murano-cfapi; then - configure_murano_cfapi - fi - elif [[ "$1" == "stack" && "$2" == "extra" ]]; then - echo_summary "Initializing Murano" - init_murano - if is_service_enabled horizon; then - init_murano_dashboard - fi - start_murano - if is_murano_backend_glare; then - restart_glare_service - fi - if is_service_enabled murano-cfapi; then - init_murano_cfapi - start_service_broker - fi - - # Give Murano some time to Start - sleep 3 - - setup_core_library - - # Install Murano apps, if needed - install_murano_apps - elif [[ "$1" == "stack" && "$2" == "test-config" ]]; then - configure_murano_tempest_plugin - fi - - if [[ "$1" == "unstack" ]]; then - stop_murano - if is_service_enabled murano-cfapi; then - stop_service_broker - fi - cleanup_murano - if is_service_enabled horizon; then - cleanup_murano_dashboard - fi - fi -fi - -# Restore xtrace -$XTRACE diff --git a/devstack/settings b/devstack/settings deleted file mode 100644 index 565b21844..000000000 --- a/devstack/settings +++ /dev/null @@ -1,76 +0,0 @@ -# Settings needed for the Murano plugin -# ------------------------------------- - -# Set up default repos -MURANO_REPO=${MURANO_REPO:-${GIT_BASE}/openstack/murano.git} -MURANO_BRANCH=${MURANO_BRANCH:-master} - -# Variables, which used in this function -# https://github.com/openstack-dev/devstack/blob/master/functions-common#L500-L506 -GITREPO["python-muranoclient"]=${MURANO_PYTHONCLIENT_REPO:-${GIT_BASE}/openstack/python-muranoclient.git} -GITBRANCH["python-muranoclient"]=${MURANO_PYTHONCLIENT_BRANCH:-master} -GITDIR["python-muranoclient"]=$DEST/python-muranoclient - -# Set up default directories -MURANO_DIR=$DEST/murano -MURANO_FILES_DIR=$MURANO_DIR/devstack/files -MURANO_CONF_DIR=${MURANO_CONF_DIR:-/etc/murano} -MURANO_CONF_FILE=${MURANO_CONF_DIR}/murano.conf -MURANO_CFAPI_CONF_FILE=${MURANO_CONF_DIR}/murano-cfapi.conf -MURANO_DEBUG=$(trueorfalse True MURANO_DEBUG) -MURANO_ENABLE_MODEL_POLICY_ENFORCEMENT=$(trueorfalse False MURANO_ENABLE_MODEL_POLICY_ENFORCEMENT) - -# Set up murano service endpoint -MURANO_SERVICE_HOST=${MURANO_SERVICE_HOST:-$SERVICE_HOST} -MURANO_SERVICE_PORT=${MURANO_SERVICE_PORT:-8082} -MURANO_SERVICE_PROTOCOL=${MURANO_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL} - -# Set up settings for service broker API -MURANO_CFAPI_SERVICE_PORT=${MURANO_CFAPI_SERVICE_PORT:-8083} -MURANO_CFAPI_DEFAULT_TENANT=${MURANO_CFAPI_DEFAULT_TENANT:-admin} - -# Set up default service user for murano -MURANO_ADMIN_USER=${MURANO_ADMIN_USER:-murano} - -MURANO_KEYSTONE_SIGNING_DIR=${MURANO_KEYSTONE_SIGNING_DIR:-/tmp/keystone-signing-muranoapi} - -# Set up murano networking settings -MURANO_DEFAULT_ROUTER=${MURANO_DEFAULT_ROUTER:-''} -MURANO_EXTERNAL_NETWORK=${MURANO_EXTERNAL_NETWORK:-''} - -DEF_MURANO_DEFAULT_DNS='8.8.8.8' -if [[ "$SERVICE_IP_VERSION" == 6 ]]; then - DEF_MURANO_DEFAULT_DNS='2001:4860:4860::8888' -fi -MURANO_DEFAULT_DNS=${MURANO_DEFAULT_DNS:-${DEF_MURANO_DEFAULT_DNS}} - -# Choose applications for installation -MURANO_APPS=${MURANO_APPS:-''} -MURANO_APPS_DIR=$DEST/murano-apps -MURANO_APPS_REPO=${MURANO_APPS_REPO:-${GIT_BASE}/openstack/murano-apps.git} -MURANO_APPS_BRANCH=${MURANO_APPS_BRANCH:-master} - -# MURANO_RABBIT_VHOST allows to specify a separate virtual host for Murano services. -# This is not required if all OpenStack services are deployed by devstack scripts -# on a single node. In this case '/' virtual host (which is the default) is enough. -# The problem arise when Murano installed in 'devbox' mode, allowing two or more -# devboxes to use one common OpenStack host. In this case it's better devboxes -# use separated virtual hosts, to avoid conflicts between Murano services. -# This couldn't be done using existing variables, so that's why this variable was added. -MURANO_RABBIT_VHOST=${MURANO_RABBIT_VHOST:-''} - -# Settings needed for the Murano Tempest Plugin installation -TEMPEST_DIR=$DEST/tempest -TEMPEST_CONFIG_DIR=${TEMPEST_CONFIG_DIR:-$TEMPEST_DIR/etc} -TEMPEST_CONFIG=$TEMPEST_CONFIG_DIR/tempest.conf -TEMPEST_MURANO_SCENARIO_TESTS_ENABLED=$(trueorfalse True TEMPEST_MURANO_SCENARIO_TESTS_ENABLED) -TEMPEST_MURANO_DEPLOYMENT_TESTS_ENABLED=$(trueorfalse False TEMPEST_MURANO_DEPLOYMENT_TESTS_ENABLED) - -# GlARe variables -# Glance Artifact Repository endpoint type for Murano communications. -# Public by default. -GLARE_ENDPOINT_TYPE=${GLARE_ENDPOINT_TYPE:-publicURL} - -enable_service murano -enable_service murano-api -enable_service murano-engine diff --git a/devstack/upgrade/resources.sh b/devstack/upgrade/resources.sh deleted file mode 100755 index 56b217654..000000000 --- a/devstack/upgrade/resources.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -set -o errexit - -source $GRENADE_DIR/grenaderc -source $GRENADE_DIR/functions - -source $TOP_DIR/openrc admin demo - -set -o xtrace - -function create { - # add later - : -} - -function verify_noapi { - # currently no good way - : -} - -function verify { - # add later - : -} - -function destroy { - # add later - : -} - -# Dispatcher -case $1 in - "create") - create - ;; - "verify_noapi") - verify_noapi - ;; - "verify") - verify - ;; - "destroy") - destroy - ;; - "force_destroy") - set +o errexit - destroy - ;; -esac diff --git a/devstack/upgrade/settings b/devstack/upgrade/settings deleted file mode 100644 index 3a324ae49..000000000 --- a/devstack/upgrade/settings +++ /dev/null @@ -1,11 +0,0 @@ -register_project_for_upgrade murano -register_db_to_save murano - -devstack_localrc base enable_plugin murano https://opendev.org/openstack/murano -devstack_localrc target enable_plugin murano https://opendev.org/openstack/murano - -devstack_localrc base enable_service murano-api murano-engine -devstack_localrc target enable_service murano-api murano-engine - -BASE_RUN_SMOKE=False -TARGET_RUN_SMOKE=False diff --git a/devstack/upgrade/shutdown.sh b/devstack/upgrade/shutdown.sh deleted file mode 100755 index 19edf0e7b..000000000 --- a/devstack/upgrade/shutdown.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -o errexit - -source $GRENADE_DIR/grenaderc -source $GRENADE_DIR/functions - -# We need base DevStack functions for this -source $BASE_DEVSTACK_DIR/functions -source $BASE_DEVSTACK_DIR/stackrc # needed for status directory -source $BASE_DEVSTACK_DIR/lib/tls -source $BASE_DEVSTACK_DIR/lib/apache - -MURANO_DEVSTACK_DIR=$(dirname $(dirname $0)) -source $MURANO_DEVSTACK_DIR/settings -source $MURANO_DEVSTACK_DIR/plugin.sh - -set -o xtrace - -stop_murano - -# sanity check that service is actually down -ensure_services_stopped murano-api murano-engine diff --git a/devstack/upgrade/upgrade.sh b/devstack/upgrade/upgrade.sh deleted file mode 100755 index 94fc7ea01..000000000 --- a/devstack/upgrade/upgrade.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash - -# ``upgrade-murano`` - -echo "*********************************************************************" -echo "Begin $0" -echo "*********************************************************************" - -# Clean up any resources that may be in use -cleanup() { - set +o errexit - - echo "********************************************************************" - echo "ERROR: Abort $0" - echo "********************************************************************" - - # Kill ourselves to signal any calling process - trap 2; kill -2 $$ -} - -trap cleanup SIGHUP SIGINT SIGTERM - -# Keep track of the grenade directory -RUN_DIR=$(cd $(dirname "$0") && pwd) - -# Source params -source $GRENADE_DIR/grenaderc - -# Import common functions -source $GRENADE_DIR/functions - -# This script exits on an error so that errors don't compound and you see -# only the first error that occurred. -set -o errexit - -# Upgrade murano -# ============== - -# Get functions from current DevStack -source $TARGET_DEVSTACK_DIR/stackrc -source $TARGET_DEVSTACK_DIR/lib/apache -source $TARGET_DEVSTACK_DIR/lib/tls -source $(dirname $(dirname $BASH_SOURCE))/settings -source $(dirname $(dirname $BASH_SOURCE))/plugin.sh - -# Print the commands being run so that we can see the command that triggers -# an error. It is also useful for following allowing as the install occurs. -set -o xtrace - -# Save current config files for posterity -[[ -d $SAVE_DIR/etc.murano ]] || cp -pr $MURANO_CONF_DIR $SAVE_DIR/etc.murano - -# Install the target murano -install_murano - -# calls upgrade-murano for specific release -upgrade_project murano $RUN_DIR $BASE_DEVSTACK_BRANCH $TARGET_DEVSTACK_BRANCH - -# Migrate the database -murano-db-manage upgrade || die $LINO "DB migration error" - -start_murano - -# Don't succeed unless the services come up -ensure_services_started murano-api murano-engine - -set +o xtrace -echo "*********************************************************************" -echo "SUCCESS: End $0" -echo "*********************************************************************" diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 26d39490b..000000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -# doc build requirements -sphinx>=2.0.0,!=2.1.0 # BSD -sphinxcontrib-httpdomain>=1.3.0 # BSD -reno>=3.1.0 # Apache-2.0 -openstackdocstheme>=2.2.1 # Apache-2.0 -os-api-ref>=1.4.0 # Apache-2.0 -oslo.config>=6.8.0 # Apache-2.0 -oslo.policy>=3.6.0 # Apache-2.0 diff --git a/doc/source/_templates/sidebarlinks.html b/doc/source/_templates/sidebarlinks.html deleted file mode 100644 index c4b3e6650..000000000 --- a/doc/source/_templates/sidebarlinks.html +++ /dev/null @@ -1,11 +0,0 @@ -

Useful Links

- - -{% if READTHEDOCS %} - -{% endif %} diff --git a/doc/source/admin/admin_troubleshooting.rst b/doc/source/admin/admin_troubleshooting.rst deleted file mode 100644 index b54da757e..000000000 --- a/doc/source/admin/admin_troubleshooting.rst +++ /dev/null @@ -1,189 +0,0 @@ -.. _admin-troubleshooting: - -=============== -Troubleshooting -=============== - -Log location -~~~~~~~~~~~~ - -By default, logs are sent to stdout. Consider how to set up the log files. - -Murano API + Engine -------------------- - -To define a file where to store logs, use the ``log_file`` option in the -:file:`murano.conf` file. You can provide an absolute or a relative path. - -To enable a detailed log file configuration, set up :file:`logging.conf`. -The example is provided in :file:`etc/murano` directory. The log configuration -file location is set with the ``log_config_append`` option in the murano -configuration file. - -Murano applications -------------------- - -Murano applications have a separate logging handler and a separate file where -all logs from application definitions should be provided. Open the -:file:`logging.conf` file and check the ``args: ('applications.log',)`` -option in the ``handler_applications`` section. - -Verify that ``log_config_append`` is not empty and set to the -:file:`logging.conf` location. - -Issues during configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If any issues occur, first of all verify the following: - -* All murano components have consistent versions: murano-dashboard and - murano-engine should use the same or compatible python-muranoclient version. - Dependent component versions can be found in :file:`requirements.txt` file. - -* The database is synced with code by running: - - .. code-block:: console - - murano-db-manage --config-file murano.conf upgrade - -**Failed to execute `murano-db-manage`** - -* Make sure the ``--config-file`` option is provided. -* Check `connection` parameter in the provided configuration file. It should - be a `connection string `_. - -* Check that MySQL or PostgreSQL (depending of what you provided in the - connection string) Python modules are installed on the system. - -**Applications panel is not seen in horizon** - -* Make sure that the following files are copied to the - ``openstack_dashboard/local/enabled`` directory, and _50_murano.py is copied - to ``openstack_dashboard/local/local_settings.d`` directory. - - * _50_dashboard_catalog.py - * _51_muranodashboard.py - * _60_panel_group_browse.py - * _63_panel_murano_catalog.py - * _70_panel_group_manage.py - * _71_panel_murano_packages.py - * _72_panel_murano_images.py - * _73_panel_murano_categories.py - * _80_panel_group_applications.py - * _81_panel_applications_environments.py - -* Check that murano data is not inserted twice in the settings file and as a - plugin. - -**Applications panel can be browsed, but 'Unable to communicate to murano-api server.' appears** - -If you have murano registered in keystone, verify the endpoint URL is valid -and service has *application-catalog* name. If you do not want to register the -murano service in keystone, just add ``MURANO_API_URL`` option to the horizon -local setting. - -Issues during deployment -~~~~~~~~~~~~~~~~~~~~~~~~ - -Besides identifying errors from log files, there is another and more flexible -way to browse deployment errors -- directly from UI. When the *Deploy Failed* -status appears, navigate to :menuselection:`Environment Components` and click -the :guilabel:`Latest Deployment Log` tab. You can see steps of the deployment -and the one that failed would have red color. - -**while scanning a simple key in "", line 32, column 3: ...** - -There is an error in the YAML file format. Before uploading a package, -validate your file in an online YAML validator like -`YAMLint `_. -Later `validation tool `_ -to check package closely while uploading will be added. - -**NoPackageForClassFound: Package for class io.murano.Environment is not found** - -Verify that murano core package is uploaded. If not, the content of the -``meta/io.murano`` folder should be zipped and uploaded to Murano. - -**[keystoneclient.exceptions.AuthorizationFailure]:** -**Authorization failed: You are not authorized to perform the requested action. (HTTP 403)** - -The token expires during the deployment. Usually the default standard token -lifetime is one hour. The error occurs frequently as, in most cases, a -deployment takes longer than that or does not start right after a token is -generated. - -Workarounds: - -* Use trusts. Only possible in the v3 version. Read more in the - `official documentation `_ - or `here `_. - Do not forget to check the corresponding heat and murano settings. Trusts - are enabled by default in murano and heat since Kilo release. - - In murano, the corresponding configuration option is located in the - ``engine`` section: - - .. code-block:: ini - - [engine] - - ... - - # Create resources using trust token rather than user's token (boolean - # value) - use_trusts = true - - If your Keystone runs v2 version, see the solutions below. - -* Make logout/login to compose a new token and start the deployment again. - Would not help for long deployment or if the token lifetime is too small. - -* Increase the token lifetime in the keystone configuration file. - -**The murano-agent did not respond within 3600 seconds** - -* Check transport access to the virtual machine: verify that the router has a - gateway. -* Check the RabbitMQ settings: verify that the agent has valid RabbitMQ - parameters. - Go to the spawned virtual machine and open :file:`*/etc/murano/agent.conf` - on the Linux-based machine or :file:`C:\\Murano\\Agent\\agent.conf` on the - Windows-based machine. Additionally, you can examine agent logs that by - default are located at :file:`/var/log/murano-agent.log` The first part of - the log file contains reconnection attempts to the RabbitMQ since the valid - RabbitMQ address and queue have not been obtained yet. -* Verify that the ``driver`` option in ``[oslo_messaging_notifications]`` group - is set to ``messagingv2``. - -**murano.engine.system.agent.AgentException** - -The agent started the execution plan but something went wrong. Examine agent -logs (see the previous paragraph for the logs placement information). Also, -try to manually execute the application scripts. - -**[exceptions.EnvironmentError]: Unexpected stack state NOT_FOUND or UPDATE_FAILED** - -An issue with heat stack creation, examine the heat log file. Try to manually -spawn the instance. If the reason of the stack creation fail is ``no valid -host was found``, there might be not enough resources or something is wrong -with the nova-scheduler. - -**Router could not be created, no external network found** - -Find the ``external_network`` parameter in the ``networking`` section of the -murano configuration file and verify that the specified external network does -exist through Web UI or by executing the -:command:`openstack network list --external` command. - -**Deployment log in the UI contains incomplete reports** - -Sometimes logs contain only two messages after the application deployment. -There are no messages provided in applications themselves: - -.. code-block:: console - - 2015-09-21 11:14:58 — Action deploy is scheduled - 2015-09-21 11:16:43 — Deployment finished successfully - -To fix the issue, set the ``driver`` option in the :file:`murano.config` file -to ``messagingv2``. diff --git a/doc/source/admin/appdev-guide/app_debugging.rst b/doc/source/admin/appdev-guide/app_debugging.rst deleted file mode 100644 index bb290c73e..000000000 --- a/doc/source/admin/appdev-guide/app_debugging.rst +++ /dev/null @@ -1,62 +0,0 @@ -.. _app-debugging: - -================================ -Application developer's cookbook -================================ - -If you have not written murano packages before, -start from the existing :ref:`Step-by-Step ` guide. It contains -general information about murano packages development process. Additionally, -see the :ref:`MuranoPL reference `. - -Load applications from a local directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Normally, whenever you make changes to your application, you have to package it, -re-upload the package to the API, and delete the old package from the API. This -makes developing and testing murano applications troublesome and time-consuming. -Murano-engine provides a way to speed up the edit-upload-deploy loop. This can be -done with the ``load_packages_from`` option. Murano-engine examines any directories -mentioned in this option before accessing the API. Therefore, you do not even -need to package the application into a ZIP archive and any changes you make are -instantly available to the engine, if you do not plan to check or change the -application UI. To check your application's appearance in the OpenStack dashboard, -upload the application for the first run. Additionally, re-upload the package -using the OpenStack dashboard or CLI each time you update the application UI. - -To load an application from a local directory, modify -the ``load_packages_from`` parameter in murano config ``[engine]`` section. - -.. code-block:: console - - [engine] - ... - load_packages_from = /path/to/murano/applications - ... - -.. note:: - The murano-engine scans the directory structure and seeks application - manifests. Therefore, you can point the ``load_packages_from`` parameter - to a cloned version of the murano-apps repository. - -Deploy environment using CLI -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The standard way to deploy an application in murano is by using the murano -dashboard (OpenStack dashboard plug-in). However, if the OpenStack dashboard is -not available or some sort of automation is required, murano provides the -capability to deploy environments through CLI. It is a powerful tool -that allows users and application developers make arbitrary changes to apps -object-model. This can be useful in early stages of application development to -experiment with different object models of an application. You can read more about -it in :ref:`Deploying environments using CLI ` - -Application unit test framework -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -An application unit test framework was created to make development process -easier. With this framework you can check different scenarios of application -deployment without running real deployments. - -For more information about application unit tests, see -:ref:`Application unit tests `. diff --git a/doc/source/admin/appdev-guide/app_development_framework.rst b/doc/source/admin/appdev-guide/app_development_framework.rst deleted file mode 100644 index ae7c6b707..000000000 --- a/doc/source/admin/appdev-guide/app_development_framework.rst +++ /dev/null @@ -1,932 +0,0 @@ -.. _app-development-framework: - -================================= -Application development framework -================================= - -Application development framework is a library that helps application -developers to create applications that can be scalable, highly available, -(self)healable and do not contain boilerplate code for common application -workflow operations. This library is placed into the Murano repository under -the ``meta/io.murano.applications`` folder. - -To allow your applications to use the code of the library, zip it and upload -to the Murano application catalog. - -Framework objectives --------------------- - -The library allows application developers to focus on their -application-specific tasks without the real need to dive into resource -orchestration, server farm configuration, and so on. For example, on how to -install the software on the VMs, how to configure it to interact with other -applications. Application developers are able to focus more on the software -configuration tools (scripts, puppets, and others) and care less about the -MuranoPL if they do not need to define any custom workflow logic. - -The main capabilities the library provides and its main use-cases are as -follows: - -* Standard operations are implemented in the framework and can be left as is -* The capability to create multi-server applications and scale them -* The capability to create composite multi-component applications -* The capability to track application failures and recover from them -* The capability to define event handlers for various events - -Quickstart ----------- - -To use the framework in your application, include the following lines to the -``manifest.yaml`` file: - -.. code-block:: yaml - - Require: - io.murano.applications: - -Create a one-component single-server application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**To create a simple application deployed on a single server**: - -#. Include the following lines to the code of the application class: - - .. code-block:: yaml - - Namespaces: - =: my.new.ns - apps: io.murano.applications - - Name: AppName - Extends: apps:SingleServerApplication - - -#. Provide an input for the application ``server`` property in your - ``ui.yaml`` file: - - .. code-block:: yaml - - Application: - ?: - type: my.new.ns.AppName - server: - ?: - type: io.murano.resources.LinuxMuranoInstance - name: generateHostname($.instanceConfiguration.unitNamingPattern, 1) - flavor: $.instanceConfiguration.flavor - ... - - - Now you already have the app that creates a server ready for installing - software on it. - -#. To create a fully functional app, add an installation script to the body - of the ``onInstallServer`` method: - - .. code-block:: yaml - - Methods: - onInstallServer: - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(apps:ServerGroup).notNull() - Body: - - $file: sys:Resources.string('installScript.sh') - - conf:Linux.runCommand($server.agent, $file) - - -#. Optional. Add other methods that handle certain stages of the application - workflow, such as ``onBeforeInstall``, ``onCompleteInstallation``, - ``onConfigureServer``, ``onCompleteConfiguration``, and others. For details - about these methods, see the - :ref:`Software components ` section. - -Create a one-component multi-server application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**To create an application that is intended to be installed on several servers**: - -#. Make it inherit the ``MultiServerApplication`` class: - - .. code-block:: yaml - - Namespaces: - =: my.new.ns - apps: io.murano.applications - - Name: AppName - Extends: apps:MultiServerApplication - - -#. Instead of the ``server`` property in ``SingleServerApplication``, provide - an input for the ``servers`` property that accepts the instance of one of - the inheritors of the ``ServerGroup`` class. The ``ui.yaml`` file in this - case may look as follows: - - .. code-block:: yaml - - Application: - ?: - type: my.new.ns.AppName - servers: - ?: - type: io.murano.applications.ServerList - servers: - - ?: - type: io.murano.resources.LinuxMuranoInstance - name: "Server-1" - flavor: $.instanceConfiguration.flavor - ... - - - ?: - type: io.murano.resources.LinuxMuranoInstance - name: "Server-2" - flavor: $.instanceConfiguration.flavor - ... - - -#. Define the custom logic of the application in the handler methods, and it - will be applied to the whole app, exactly like with - ``SingleServerApplication``. - -Create a scalable multi-server application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**To provide the application with the ability to scale**: - -#. Make the app extend the ``MultiServerApplicationWithScaling`` class: - - .. code-block:: yaml - - Namespaces: - =: my.new.ns - apps: io.murano.applications - - Name: AppName - Extends: apps:MultiServerApplicationWithScaling - -#. Provide the ``ui.yaml`` file: - - .. code-block:: yaml - - Application: - ?: - type: my.new.ns.AppName - servers: - ?: - type: io.murano.applications.ServerReplicationGroup - numItems: $.appConfiguration.numNodes - provider: - ?: - type: io.murano.applications.TemplateServerProvider - template: - ?: - type: io.murano.resources.LinuxMuranoInstance - flavor: $.instanceConfiguration.flavor - ... - serverNamePattern: $.instanceConfiguration.unitNamingPattern - - - The ``servers`` property accepts instance of the ``ServerReplicationGroup`` - class, and in turn it requires input of the ``numItems`` and ``provider`` - properties. - -After the deployment, the ``scaleOut`` and ``scaleIn`` public methods -(actions) become available in the dashboard UI. - -For a working example of such application, see the -``com.example.apache.ApacheHttpServer`` package version 1.0.0. - - -Library overview ----------------- - -The framework includes several groups of classes: - - ``replication.yaml`` - Classes that provide the capability to replicate the resources. - - ``servers.yaml`` - Classes that provide instances grouping and replication. - - ``component.yaml`` - Classes that define common application workflows. - - ``events.yaml`` - Class for handling events. - - ``baseapps.yaml`` - Base classes for applications. - -As it is described in the :ref:`Quickstart` section, the application makes use -of the Application development framework by inheriting from one of the base -application classes, such as ``SingleServerApplication``, -``MultiServerApplication``, ``MultiServerApplicationWithScaling``. In turn, -these classes are inheritors of the standard ``Application`` class and the -``SoftwareComponent`` class. The latter class binds all of the framework -capabilities. - -The ``SoftwareComponent`` class inherits both ``Installable`` and -``Configurable`` classes which provide boilerplate code for the installation -and configuration workflow respectively. They also contain empty methods -for each stage of the workflow (e.g. ``onBeforeInstall``, ``onInstallServer``), -which are the places where application developers can add their own -customization code. - -The entry point to execute deployment of the software component is its -``deployAt`` method which requires instance of one of the inheritors of the -``serverGroup`` class. It is the object representing the group of servers -the application should be deployed to. The application holds such an object as -one of its properties. It can be a single server (``SingleServerGroup`` -subclass), a prepopulated list of servers (``ServerList`` subclass) or a list -of servers that are dynamically generated in runtime -(``ServerReplicationGroup`` subclass). - -``ServerReplicationGroup`` or, more precisely, one of its parent classes -``ReplicationGroup`` controls the number of items it holds by releasing items -over the required amount and requesting creation of the new items in runtime -from the ``ReplicaProvider`` class which acts like an object factory. In case -of servers, it is ``TemplateServerProvider`` which creates new servers from the -given template. Replication is done during the initial deployment and during -the scaling actions execution. - -Framework detailed description ------------------------------- - -This section provides technical description of all the classes present in the -application development library, their hierarchy and usage. - -Scaling primitives -~~~~~~~~~~~~~~~~~~ - -There is an ability to group similar resources together, produce new copies -of the same resources or release the existing ones on request. Now it is -implemented for instances only, other resources may be added later. - -The following is the hierarchy of classes that provide grouping and -replication of resources: - -:: - - +-------+ - | +-------+ - | | +--------+ +------------------+ +-----------------+ - | | | | | | | | - +-+ | Object <--------+ ReplicationGroup +--------> ReplicaProvider | - +-+ | | | | | - +--------+ +---+--------------+ +-+--------+------+ - ^ ^ ^ - | | | - | +------------------+-----+ | - | | | | - +-------+ | | CloneReplicaProvider | | - | +-------+ | | + other | | - | | +----------+ | +------------------------+ | - | | | | | | - +-+ | Instance | | | - +-+ | | | - +----+-----+ | | - | | | - +-----+-------+ | | - | | | | - | ServerGroup | | +---------------+--+ - | | | | Template | - +-----^-------+ +---+----------+ | Server +--+ - | | Server +-------> Provider | | - +------------+ Replication | +-----+------------+ +---+ - | Group | | | | - +--------------+ +---+---other---+ | - | | - +---------------+ - - -**ReplicationGroup** - - A base class which holds the collection of objects generated in runtime in - its ``items`` output property and contains a reference to a - ``ReplicaProvider`` object in its ``provider`` property which is used to - dynamically generate the objects in runtime. - - Input properties of this class include the ``minItems`` and ``maxItems`` - allowing to limit the number of objects it holds in its collection. - - An input-output property ``numItems`` allows to declaratively change the - set of objects in the collection by setting its size. - - The ``deploy()`` method is used to apply the replica settings: it drops - the objects from the collection if their number exceeds the number - specified by the ``numItems`` or generate some new if there are not enough - of them. - - The ``scale()`` method is used to increase or decrease the ``numItems`` by - some number specified in the ``delta`` argument of the method, but in - range between ``maxItems`` and ``minItems``. - -**ReplicaProvider** - - A class which does the object replication. The base one is abstract, its - inheritors should implement the abstract ``createReplica`` method to - create the actual object. The method accepts the ``index`` parameter to - properly parametrize the newly created copy and optional ``owner`` - parameter to use it as an owner for the newly created objects. - - The concrete implementations of this class should define all the input - properties needed to create new instances of object. Thus the provider - actually acts as a template of the object it generates. - -**CloneReplicaProvider** - - An implementation of ``ReplicaProvider`` capable to create replicas by - cloning some user-provided object, making use of the ``template()`` - contract. - -**PoolReplicaProvider** - - Replica provider that takes replicas from the prepopulated pool instead - of creating them. - -**RoundrobinReplicaProvider** - - Replica provider with a load balancing that returns replica from the - prepopulated list. Once the provider runs out of free items it goes to the - beginning of the list and returns the same replicas again. - -**CompositeReplicaProvider** - - Replica provider which is a composition of other replica providers. It - holds the collection of providers in its ``providers`` input property. - Its ``ReplicaProvider`` method returns a new replica created by the first - provider in that list. If that value is `null`, the replica created by the - second provider is returned, and so on. If no not-null replicas are - created by all providers, the method returns null. - - This provider can be used to have some default provider with the ability - to fall back to the next options if the preferable one is not successful. - - -Servers replication -~~~~~~~~~~~~~~~~~~~ - -**ServerGroup** - - A class that provides static methods for deployment and releasing - resources on the group of instances. - - The ``deployServers()`` static method accepts instance of ``ServerGroup`` - class and a list of servers as the parameters and deploys all servers from - the list in the environment which owns the server group, unless server is - already deployed. - - The ``releaseServers()`` static method accepts a list of servers as the - parameter and consequentially calls ``beginReleaseResources()`` and - ``endReleaseResources()`` methods on each server. - -**ServerList** - - A class that extends the ``ServerGroup`` class and holds a group of - prepopulated servers in its ``servers`` input property. - - The ``deploy()`` method calls the ``deployServers()`` method with the - servers defined in the ``servers`` property. - - The ``.destroy()`` method calls the ``releaseServers()`` method with the - servers defined in the ``servers`` property. - -**SingleServerGroup** - - Degenerate case of a ``ServerGroup`` which consists of a single server. - Has the ``server`` input property to hold a single server. - -**CompositeServerGroup** - - A server group that is composed of other server groups. - -**ServerReplicationGroup** - - A subclass of the ``ReplicationGroup`` class and the ``ServerGroup`` - class to replicate the ``Instance`` objects it holds. - - The ``deploy()`` method of this group not only generates new instances of - servers but also deploys them if needed. - -**TemplateServerProvider** - - A subclass of ``ReplicaProvider`` which is used to produce the objects - of one of the ``Instance`` class inheritors by creating them from the - provided template with parameterization of the hostnames. The resulting - hostname looks like 'Server {index}{groupName}'. - - May be passed as ``provider`` property to objects of the - ``ServerReplicationGroup`` class. - -**other replica providers** - - Other subclasses of ``ReplicaProvider`` may be created to produce different - objects of ``Instance`` class and its subclasses depending on particular - application needs. - - -Classes for grouping and replication of other kinds of resources are to be -implemented later. - -.. _software-components: - -Software Components -~~~~~~~~~~~~~~~~~~~ - -The class to handle the lifecycle of the application is the -``SoftwareComponent`` class which is a subclass of ``Installable`` and -``Configurable``: - -:: - - +-----------+-+ +-+------------+ - | | | | - | Installable | | Configurable | - | | | | - +-----------+-+ +-+------------+ - ^ ^ - | | - | | - +-+---------------+-+ - | | - | SoftwareComponent | - | | - +-------------------+ - - -The hierarchy of the ``SoftwareComponent`` classes is used to define the -workflows of different application lifecycles. The general logic of the -application behaviour is contained in the methods of the base classes and -the derived classes are able to implement the handlers for the custom logic. -The model is event-driven: the workflow consists of the multiple steps, and -most of the steps invoke appropriate `on%StepName%` methods intended to -provide application-specific logic. - -Now 'internal' steps logic and their 'public' handlers are split into the -separate methods. It should improve the developers' experience and simplify -the code of the derived classes. - -The standard workflows (such as Installation and Configuration) are defined -by the ``Installable`` and ``Configurable`` classes respectively. The -``SoftwareComponent`` class inherits both these classes and defines its -deployment workflow as a sequence of Installation and Configuration flows. -Other future implementations may add new workflow interfaces and mix them in -to change the deployment workflow or add new actions. - -**Installation** workflow consists of the following methods: - -:: - - +----------------------------------------------------------------------------------------------------------------------+ - | INSTALL | - | | - | +------------------------------+ +---------------+ | - | +------------------------------+ | +---------------+ | | - | +------------------------------+ | | +---------------+ +---------------+ | | +----------------------+ | - | | | | | | | | | | | | | | - | | checkServerIsInstalled | +-+ +----> beforeInstall +----> installServer | +-+ +----> completeInstallation | | - | | +-+ | | | +-+ | | | - | +------------------------------+ +------+--------+ +------+--------+ +-----------+----------+ | - | | | | | - +----------------------------------------------------------------------------------------------------------------------+ - | | | - | | | - | | | - v v v - onBeforeInstall onInstallServer onCompleteInstallation - - -.. list-table:: - :widths: 10 10 40 - :header-rows: 1 - - * - Method - - Arguments - - Description - - * - **install** - - ``serverGroup`` - - Entry point of the installation workflow. - Iterates through all the servers of the passed ServerGroup and calls the - ``checkServerIsInstalled`` method for each of them. If at least one - of the calls has returned `false`, calls a ``beforeInstall`` method. - Then, for each server which returned `false` as the result of the - ``checkServerIsInstalled`` calls the ``installServer`` method to do - the actual software installation. - After the installation is completed on all the servers and if at - least one of the previous calls of ``checkServerIsInstalled`` returned - `false`, the method runs the ``completeInstallation`` method. - If all the calls to ``checkServerIsInstalled`` return `true`, this - method concludes without calling any others. - - * - **checkServerIsInstalled** - - ``server`` - - Checks if the given server requires a (re)deployment of the software - component. By default checks for the value of the attribute `installed` - of the instance. - May be overridden by subclasses to provide some better logic (e.g. the - app developer may provide code to check if the given software is - pre-installed on the image which was provisioned on the VM). - - * - **beforeInstall** - - ``servers``, ``serverGroup`` - - Reports the beginning of installation process, sends notification about - this event to all objects which are subscribed for it (see - *Event notification pattern* section for details) and calls the public - event handler ``onBeforeInstall``. - - * - **onBeforeInstall** - - ``servers``, ``serverGroup`` - - Public handler of the `beforeInstall` event. Empty in the base class, - may be overridden in subclasses if some custom pre-install logic needs - to be executed. - - * - **installServer** - - ``server``, ``serverGroup`` - - Does the actual software deployment on a given server by calling an - ``onInstallServer`` public event handler (with notification on this - event). If the installation completes successfully sets the `installed` - attribute of the server to `true`, reports successful installation and - returns `null`. If an exception encountered during the invocation of - ``onInstallServer``, the method handles that exception, reports a - warning and returns the server. The return value of the method indicates - to the ``install`` method how many failures encountered in total during - the installation and with what servers. - - * - **onInstallServer** - - ``server``, ``serverGroup`` - - An event-handler method which is called by the ``installServer`` method - when the actual software deployment is needed.It is empty in the base - class. The implementations should override it with custom logic to - deploy the actual software bits. - - * - **completeInstallation** - - ``servers``, ``serverGroup``, ``failedServers`` - - It is executed after all the ``installServer`` methods were called. - Checks for the number of errors reported during the installation: if it - is greater than the value of ``allowedInstallFailures`` property, an - exception is raised to interrupt the deployment workflow. Otherwise the - method emits notification on this event, calls an - ``onCompleteInstallation`` event handler and then reports the successful - completion of the installation workflow. - - * - **onCompleteInstallation** - - ``servers``, ``serverGroup``, ``failedServers`` - - An event-handler method which is called by the ``completeInstallation`` - method when the component installation is about to be completed. - Default implementation is empty. Inheritors may implement this method to - add some final handling, reporting etc. - - -**Configuration** workflow consists of the following methods: - -:: - - +----------------------------------------------------------------------------------------------------------------------+ - | CONFIGURATION | - | +-----------------+ | - | | | | - | | +---------------+ +-----------------+ | - | | +---------------+ | +-----------------+ | | - | +------------v--+ +---------------+ | | +--------------+ +-----------------+ | | +-----------------------+ | - | | | | | | | | | | | | | | | | - | | checkCluster\ +---> checkServer\ | +-+---> preConfigure +---> configureServer | +-+---> completeConfiguration | | - | | IsConfigured | | IsConfigured +-+ | | | +-+ | | | - | +------------+--+ +---------------+ +------+-------+ +--------+--------+ +-----------+-----------+ | - | | | | | | - | | | | | | - | +----------v----------+ | | | | - | | | | | | | - | | getConfigurationKey | | | | | - | | | | | | | - | +---------------------+ | | | | - | | | | | - +----------------------------------------------------------------------------------------------------------------------+ - | | | - | | | - v v v - configureSecurity, onConfigureServer onCompleteConfiguration - onPreConfigure - - -.. list-table:: - :widths: 10 10 40 - :header-rows: 1 - - * - Method - - Arguments - - Description - - * - **configure** - - ``serverGroup`` - - Entry point of the configuration workflow. - Calls a ``checkClusterIsConfigured`` method. If the call returns `true`, - workflow exits without any further action. Otherwise for each server in - the ``serverGroup`` it calls ``checkServerIsConfigured`` method and gets - the list of servers that need reconfiguration. The ``preConfigure`` - method is called with that list. At the end calls the - ``completeConfiguration`` method. - - * - **checkClusterIsConfigured** - - ``serverGroup`` - - Has to return `true` if the configuration (i.e. the values of input - properties) of the component has not been changed since it was last - deployed on the given server group. Default implementation calls the - ``getConfigurationKey`` method and compares the returned result with a - value of `configuration` attribute of ``serverGroup``. If the results - match returns `true` otherwise `false`. - - * - **getConfigurationKey** - - None - - Should return some values describing the configuration state of the - component. This state is used to track the changes of the configuration - by the ``checkClusterIsConfigured`` and ``checkServerIsConfigured`` - methods. - Default implementation returns a synthetic value which gets updated on - every environment redeployment. Thus the subsequent calls of the - ``configure`` method on the same server group during the same deployment - will not cause the reconfiguration, while the calls on the next - deployment will reapply the configuration again. - The inheritors may redefine this to include the actual values of the - configuration properties, so the configuration is reapplied only if the - appropriate input properties are changed. - - * - **checkServerIsConfigured** - - ``server``, ``serverGroup`` - - It is called to check if the particular server of the server group has - to be reconfigured thus providing more precise control compared to - cluster-wide ``checkClusterIsConfigured``. - Default implementation calls the ``getConfigurationKey`` method and - compares the returned result with a value of `configuration` attribute - of the server. If the results match returns `true` otherwise `false`. - This method gets called only if the ``checkClusterIsConfigured`` method - returned `false` for the whole server group. - - * - **preConfigure** - - ``servers``, ``serverGroup`` - - Reports the beginning of configuration process, calls the - ``configureSecurity`` method, emits the notification and calls the - public event handler ``onPreConfigure``. This method is called once per - the server group and only if the changes in configuration are detected. - - * - **configureSecurity** - - ``servers``, ``serverGroup`` - - Intended for configuring the security rules. It is empty in the base - class. Fully implemented in the ``OpenStackSecurityConfigurable`` class - which is the inheritor of ``Configurable``. - - * - **onPreConfigure** - - ``servers``, ``serverGroup`` - - Public event-handler which is called by the ``preConfigure`` method - when the (re)configuration of the component is required. - Default implementation is empty. Inheritors may implement this method to - set various kinds of cluster-wide states or output properties which may - be of use at later stages of the workflow. - - * - **configureServer** - - ``server``, ``serverGroup`` - - Does the actual software configuration on a given server by calling the - ``onConfigureServer`` public event handler. Before that reports the - beginning of the configuration and emits the notification. If the - configuration completes successfully calls the ``getConfigurationKey`` - method and sets the `configuration` attribute of the server to resulting - value thus saving the configuration applied to a given server. Returns - `null` to indicate successful configuration. - If an exception encountered during the invocation of - ``onConfigureServer``, the method will handle that exception, report a - warning and return the current server to signal its failure to the - ``configure`` method. - - * - **onConfigureServer** - - ``server``, ``serverGroup`` - - An event-handler method which is called by the ``configureServer`` - method when the actual software configuration is needed. It is empty in - the base class. The implementations should override it with custom logic - to apply the actual software configuration on a given server. - - * - **completeConfiguration** - - ``servers``, ``serverGroup``, ``failedServers`` - - It is executed after all the ``configureServer`` methods were called. - Checks for the number of errors reported during the configuration: if it - is greater than set by the ``allowedConfigurationFailures`` property, an - exception is raised to interrupt the deployment workflow. Otherwise the - method emits notification, calls an ``onCompleteConfiguration`` event - handler, calls the ``getConfigurationKey`` method and sets the - `configuration` attribute of the server group to resulting value and - then reports successful completion of the configuration workflow. - - * - **onCompleteConfiguration** - - ``servers``, ``serverGroup``, ``failedServers`` - - The event-handler method which is called by the ``completeConfiguration`` - method when the component configuration is finished at all the servers. - Default implementation is empty. Inheritors may implement this method to - add some final handling, reporting etc. - - -The ``OpenStackSecurityConfigurable`` class extends ``Configurable`` by -implementing the ``configureSecurity`` method of the base class and adding the -empty ``getSecurityRules`` method. - -.. list-table:: - :widths: 10 10 40 - :header-rows: 1 - - * - Method - - Arguments - - Description - - * - **getSecurityRules** - - None - - Returns an empty dictionary in default implementation. Inheritors which - want to add security rules during the app configuration should - implement this method and make it return a list of dictionaries - describing the security rules with the following keys: - - * FromPort (port number, e.g. 80). - - * ToPort (port number, e.g. 80). - - * IpProtocol: (string, e.g. 'tcp'). - - * External: (boolean: `true` means that the inbound traffic to the given - port (or port range) may originate from outside of the environment; - `false` means that only the VMs spawned by this or other apps of the - current environment may connect to this port). - - * Ethertype: (optional, can be 'IPv4' or 'IPv6'). - - * - **configureSecurity** - - ``servers``, ``serverGroup`` - - Gets the list of security rules provided by the ``getSecurityRules`` - method and adds security group with these rules to the Heat stacks of - all regions which the component's ``servers`` are deployed to - -Consider the following example of this class usage: - -.. code-block:: yaml - - Namespaces: - =: com.example.apache - apps: io.murano.applications - - Name: ApacheHttpServer - - Extends: - - apps:MultiServerApplicationWithScaling - - apps:OpenStackSecurityConfigurable - - Methods: - getSecurityRules: - Body: - - Return: - - ToPort: 80 - FromPort: 80 - IpProtocol: tcp - External: true - - ToPort: 443 - FromPort: 443 - IpProtocol: tcp - External: true - - -In the example above, the ``ApacheHttpServer`` class is configured to create -a security group with two security rules allowing network traffic over HTTP -and HTTPS protocols on its deployment. - - -The ``SoftwareComponent`` class inherits both ``Installable`` and -``Configurable`` and adds several additional methods. - -.. list-table:: - :widths: 10 10 40 - :header-rows: 1 - - * - Method - - Arguments - - Description - - * - **deployAt** - - ``serverGroup`` - - Binds all workflows into one process. Consequentially calls ``deploy`` - method of the ``serverGroup``, ``install`` and ``configure`` methods - inherited from the parent classes. - - * - **report** - - ``message`` - - Reports a ``message`` using environment's reporter. - - * - **detectSuccess** - - ``allowedFailures``, ``serverGroup``, ``failedServers`` - - Static method that returns `true` in case the actual number of failures - (number of ``failedServers``) is less than or equal to the - ``allowedFailures``. The latter can be on of the following options: - `none`, `one`, `two`, `three`, `any`, 'quorum'. `any` allows any number - of failures during the installation or configuration. `quorum` allows - failure of less than a half of all servers. - - -Event notification pattern -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``Event`` class may be used to issue various notifications to other -MuranoPL classes in an event-driven manner. - -Any object which is going to emit the notifications should declare the -instances of the ``Event`` class as its public Runtime properties. You can see -the examples of such properties in the ``Installable`` and ``Configurable`` -classes: - -.. code-block:: yaml - - Name: Installable - - Properties: - beforeInstallEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: beforeInstall - - -The object which is going to subscribe for the notifications should pass -itself into the ``subscribe`` method of the event along with the name of its -method which will be used to handle the notification: - -.. code-block:: yaml - - $event.subscribe($subscriber, handleFoo) - - -The specified handler method must be present in the subscriber class -(if the method name is missing it will default to ``handle%Eventname%``) -and have at least one standard (i.e. not ``VarArgs`` or ``KwArgs``) argument -which will be treated as ``sender`` while invoking. - -The ``unsubscribe`` method does the opposite and removes object from the -subscribers of the event. - -The class which is going to emit the notification should call the ``notify`` -method of the event and pass itself as the first argument (``sender``). All -the optional parameters of the event may be passed as varargs/kwargs of the -``notify`` call. They will be passed all the way to the handler methods. - -This is how it looks in the ``Installable`` class: - -.. code-block:: yaml - - beforeInstall: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - ... - - $this.beforeInstallEvent.notify($this, $servers, $serverGroup) - - ... - - -The ``notifyInParallel`` method does the same, but invokes all handlers of -subscribers in parallel. - - -Base application classes -~~~~~~~~~~~~~~~~~~~~~~~~ - -There are several base classes that extend standard ``io.murano.Application`` -class and ``SoftwareComponent`` class from the application development -library. - -**SingleServerApplication** - A base class for applications running a single software component on a - single server only. Its ``deploy`` method simply creates the - ``SingleServerGroup`` with the ``server`` provided as an application input. - -**MultiServerApplication** - A base class for applications running a single software component on - multiple servers. Unlike ``SingleServerApplication``, it has the - ``servers`` input property instead of ``server``. It accepts instance of - on of the inheritors of the ``ServerGroup`` class. - -**MultiServerApplicationWithScaling** - Extends ``MultiServerApplication`` with the ability to scale the - application by increasing (scaling out) or decreasing (scaling in) the - number of nodes with the application after it is installed. The - differences from ``MultiServerApplication`` are: - - * the ``servers`` property accepts only instances of - ``ServerReplicationGroup`` rather than any ``ServerGroup`` - - * the additional optional ``scaleFactor`` property accepts the number by - which the app is scaled at once; it defaults to 1 - - * the ``scaleOut`` and ``scaleIn`` public methods are added - - -Application developers may as well define their own classes using the -same approach and combining base classes behaviour with the custom code to -satisfy the needs of their applications. diff --git a/doc/source/admin/appdev-guide/app_migrating.rst b/doc/source/admin/appdev-guide/app_migrating.rst deleted file mode 100644 index 0402e1d59..000000000 --- a/doc/source/admin/appdev-guide/app_migrating.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _app_migrating: - -======================================= -Migrating applications between releases -======================================= - -This document describes how a developer of murano application can update -existing packages to make them synchronized with all implemented features -and requirements. - -.. toctree:: - :maxdepth: 1 - - app_migrating/app_migrate_to_juno - app_migrating/app_migrate_to_kilo - app_migrating/app_migrate_to_liberty - app_migrating/app_migrate_to_newton diff --git a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_juno.rst b/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_juno.rst deleted file mode 100644 index 205ade47e..000000000 --- a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_juno.rst +++ /dev/null @@ -1,88 +0,0 @@ -.. _app_migrate_to_juno: - - -Migrate applications from Murano v0.5 to Stable/Juno -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Applications created for murano v0.5, unfortunately, are not supported in Murano -stable/juno. This document provides the application code changes required for -compatibility with the stable/juno murano version. - -Rename *'Workflow'* to *'Methods'* ----------------------------------- - -In stable/juno the name of section containing class methods is renamed to -*Methods*, as the latter is more OOP and doesn't cause confusion with Mistral. So, -you need to change it in *app.name/Classes* in all classes describing workflow -of your app. - -For example: - -.. code-block:: yaml - - Workflow: - deploy: - Body: - - $._environment.reporter.report($this, 'Creating VM') - -Should be changed to: - -.. code-block:: yaml - - Methods: - deploy: - Body: - - $._environment.reporter.report($this, 'Creating VM') - -Change the Instance type in the UI definition 'Application' section -------------------------------------------------------------------- - -The Instance class was too generic and contained some dirty workarounds to -differently handle Windows and Linux images, to bootstrap an instance in a -number of ways, etc. To solve these problems more classes were added to the -*Instance* inheritance hierarchy. - -Now, base *Instance* class is abstract and agnostic of the desired OS and agent -type. It is inherited by two classes: *LinuxInstance* and *WindowsInstance*. - -- *LinuxInstance* adds a default security rule for Linux, opening a standard - SSH port; - -- *WindowsInstance* adds a default security rule for Windows, opening an RDP - port. At the same time WindowsInstance prepares a user-data allowing to use - Murano v1 agent. - -*LinuxInstance* is inherited by two other classes, having different software -config method: - -- *LinuxMuranoInstance* adds a user-data preparation to configure Murano - v2 agent; - -- *LinuxUDInstance* adds a custom user-data field allowing the services to - supply their own user data. - -You need to specify the instance type which is required by your app. It -specifies a field in UI, where user can select an image matched to the instance -type. This change must be added to UI form definition in *app.name/UI/ui.yaml*. - -For example, if you are going to install your application on Ubuntu, you need to -change: - -.. code-block:: yaml - - Application: - ?: - instance: - ?: - type: io.murano.resources.Instance - -to: - -.. code-block:: yaml - - Application: - ?: - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - diff --git a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_kilo.rst b/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_kilo.rst deleted file mode 100644 index cf64d244a..000000000 --- a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_kilo.rst +++ /dev/null @@ -1,118 +0,0 @@ -.. _app_migrate_to_kilo: - -Migrate applications to Stable/Kilo -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In Kilo, there are no breaking changes that affect backward compatibility. -But there are two new features which you can use since Kilo. - -1. Pluggable Pythonic classes for murano ----------------------------------------- - -Now you can create plug-ins for MuranoPL. A plug-in (extension) is an -independent Python package implementing functionality which you want -to add to the workflow of your application. - -For a demo application demonstrating the usage of plug-ins, see the -``murano/contrib/plugins/murano_exampleplugin`` folder. - -The application consist of the following components: - - * An `ImageValidatorMixin` class that inherits the generic instance class - (``io.murano.resources.Instance``) and adds a method capable of validating - the instance image for having an appropriate murano metadata type. This - class may be used as a mixin when added to inheritance hierarchy of - concrete instance classes. - - * A concrete class called `DemoInstance` that inherits from - `io.murano.resources.LinuxMuranoInstance` and `ImageValidatorMixin` - to add the image validation logic to a standard, murano-enabled and - Linux-based instance. - - * An application that deploys a single VM using the `DemoInstance` - class if the tag on the user-supplied image matches the user-supplied - constant. - -The **ImageValidatorMixin** demonstrates the instantiation of plug-in provided -class and its usage, as well as handling of exception which may be thrown if -the plug-in is not installed in the environment. - -2. Murano mistral integration ------------------------------ - -The core library has a new system class for mistral client that allows to call -Mistral APIs from the murano application model. - -The system class allows you to: - - * Upload a mistral workflow to mistral. - - * Trigger the mistral workflow that is already deployed, wait for completion - and return the execution output. - -To use this feature, add some mistral workflow to ``Resources`` folder -of your package. For example, create file `TestEcho_MistralWorkflow.yaml`: - - .. code-block:: yaml - - version: '2.0' - - test_echo: - type: direct - input: - - input_1 - output: - out_1: <% $.task1_output_1 %> - out_2: <% $.task2_output_2 %> - out_3: <% $.input_1 %> - tasks: - my_echo_test: - action: std.echo output='just a string' - publish: - task1_output_1: 'task1_output_1_value' - task1_output_2: 'task1_output_2_value' - on-success: - - my_echo_test_2 - - my_echo_test_2: - action: std.echo output='just a string' - publish: - task2_output_1: 'task2_output_1_value' - task2_output_2: 'task2_output_2_value' - .. - -And provide workflow to use the mistral client: - - .. code-block:: yaml - - Namespaces: - =: io.murano.apps.test - std: io.murano - sys: io.murano.system - - - Name: MistralShowcaseApp - - Extends: std:Application - - Properties: - name: - Contract: $.string().notNull() - - mistralClient: - Contract: $.class(sys:MistralClient) - Usage: Runtime - - - Methods: - initialize: - Body: - - $this.mistralClient: new(sys:MistralClient) - - deploy: - Body: - - $resources: new('io.murano.system.Resources') - - $workflow: $resources.string('TestEcho_MistralWorkflow.yaml') - - $.mistralClient.upload(definition => $workflow) - - $output: $.mistralClient.run(name => 'test_echo', inputs => dict(input_1 => input_1_value)) - - $this.find(std:Environment).reporter.report($this, $output.get('out_3')) diff --git a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_liberty.rst b/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_liberty.rst deleted file mode 100644 index 53c209f70..000000000 --- a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_liberty.rst +++ /dev/null @@ -1,259 +0,0 @@ -.. _app_migrate_to_liberty: - -Migrate applications to Stable/Liberty -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In Liberty a number of useful features that can be used by developers creating -their murano applications were implemented. This document describes these -features and steps required to include them to new apps. - -1. Versioning -------------- - -Package version -``````````````` - -Now murano packages have a new optional attribute in their manifest called -`Version` - a standard SemVer format version string. All MuranoPL classes have -the version of the package they contained in. -To specify the version of your package, add a new section to the manifest file: - - .. code-block:: yaml - - Version: 0.1.0 - - .. - -If no version specified, the package version will be equal to *0.0.0*. - -Package requirements -```````````````````` - -There are cases when packages may require other packages for their work. -Now you need to list such packages in the `Require` section of the manifest -file: - - .. code-block:: yaml - - Require: - package1_FQN: version_spec_1 - ... - packageN_FQN: version_spec_N - - .. - -`version_spec` here denotes the allowed version range. It can be either in -semantic_version specification pip-like format or as partial version string. -If you do not want to specify the package version, leave this value empty: - - .. code-block:: yaml - - Require: - package1_FQN: '>=0.0.3' - package2_FQN: - - .. - -In this case, the last dependency *0.x.y* is used. - - -.. note:: - All packages depend on the `io.murano` package (core library). If you do not - specify this requirement in the list (or the list is empty or even there is - no `Require` key in package manifest), then dependency *io.murano: 0* will - be automatically added. - - -Object version -`````````````` -Now you can specify the version of objects in UI definition when your -application requires specific version of some class. To do this, add new key -`classVersion` to section `?` describing object: - - .. code-block:: yaml - - ?: - type: io.test.apps.TestApp - classVersion: 0.0.1 - - .. - -`classVersion` of all classes included to package equals `Version` of this -package. - -2. YAQL -------- - -In Liberty, murano was updated to use `yaql 1.0.0`. -The new version of YAQL allows you to use a number of new functions and -features that help to increase the speed of developing new applications. - -.. note:: - Usage of these features makes your applications incompatible with - older versions of murano. - -Also, in Liberty you can change `Format` in the manifest of package from -*1.0* to *1.1* or *1.2*. - - * **1.0** - supported by all versions of murano. - * **1.1** - supported by Liberty+. Specify it, if you want to use features - from *yaql 0.2* and *yaql 1.0.0* at the same time in your application. - * **1.2** - supported by Liberty+. A number of features from *yaql 0.2* do not - work with this format (see the list below). We recommend you to use it for - new applications where compatibility with Kilo is not required. - -Some examples of *yaql 0.2* features that are not compatible with the *1.2* format -``````````````````````````````````````````````````````````````````````````````````` - - * Several functions now cannot be called as MuranoObject methods: - ``id(), cast(), super(), psuper(), type()``. - - * Now you do not have the ability to compare non-comparable types. - For example "string != false" - - * Dicts are not iterable now, so you cannot do this: - ``If: $key in $dict``. Use ``$key in $dict.keys()`` - or ``$v in $dict.values()`` - - * Tuples are not available. ``=>`` always means keyword argument. - -3. Simple software configuration --------------------------------- - -Previously, you always had to create execution plans even when some short -scripts had to be executed on a VM. This process included creating a template -file, creating a script, and describing the sending of the execution plan to -the murano agent. - -Now you can use a new class **io.murano.configuration.Linux** from murano -`core-library`. This allows sending short commands to the VM and putting files -from the ``Resources`` folder of packages to some path on the VM without the -need of creating execution plans. - -To use this feature you need to: - -* Declare a namespace (for convenience) - - .. code-block:: yaml - - Namespaces: - conf: io.murano.configuration - ... - .. - -* Create object of ``io.murano.configuration.Linux`` class in workflow of - your application: - - .. code-block:: yaml - - $linux: new(conf:Linux) - .. - -* Run one of the two feature methods: ``runCommand`` or ``putFile``: - - .. code-block:: yaml - - # first argument is agent of instance, second - your command - $linux.runCommand($.instance.agent, 'service apache2 restart') - .. - - or: - - .. code-block:: yaml - - # getting content of file from 'Resources' folder - - $resources: new(sys:Resources) - - $fileContent: $resources.string('your_file.name') - # put this content to some directory on VM - - $linux.putFile($.instance.agent, $fileContent, '/tmp/your_file.name') - .. - - -.. note:: - At the moment, you can use this feature only if your app requires an - instance of ``LinuxMuranoInstance`` type. - -4. UI network selection element -------------------------------- - -Since Liberty, you can provide users with the ability to choose where to join -their VM: to a new network created during the deployment, or to an already -existing network. -Dynamic UI now has a new type of field - ``NetworkChoiseField``. This field -provides a selection of networks and their subnetworks as a dropdown populated -with those which are available to the current project (tenant). - -To use this feature, you should make the following updates in the Dynamic UI of -an application: - -* Add ``network`` field: - - .. code-block:: yaml - - fields: - - name: network - type: network - label: Network - description: Select a network to join. 'Auto' corresponds to a default environment's network. - required: false - murano_networks: translate - .. - - To see the full list of the ``network`` field arguments, refer to the UI - forms :ref:`specification `. - -* Add template: - - .. code-block:: yaml - - Templates: - customJoinNet: - - ?: - type: io.murano.resources.ExistingNeutronNetwork - internalNetworkName: $.instanceConfiguration.network[0] - internalSubnetworkName: $.instanceConfiguration.network[1] - .. - -* Add declaration of `networks` instance property: - - .. code-block:: yaml - - Application: - ?: - type: com.example.exampleApp - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - networks: - useEnvironmentNetwork: $.instanceConfiguration.network[0]=null - useFlatNetwork: false - customNetworks: switch($.instanceConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet) - - .. - -For more details about this feature, see :ref:`use-cases ` - -.. note:: - To use this feature, the version of UI definition must be **2.1+** - -5. Remove name field from fields and object model in dynamic UI ---------------------------------------------------------------- - -Previously, each class of an application had a ``name`` property. It had no -built-in predefined meaning for MuranoPL classes and mostly used for dynamic UI -purposes. - -Now you can create your applications without this property in classes and -without a corresponding field in UI definitions. The field for app name will be -automatically generated on the last management form before start of deployment. -Bonus of deleting this - to remove unused property from muranopl class that is -needed for dashboard only. - -So, to update existing application developer should make 3 steps: - -#. remove ``name`` field and property declaration from UI definition; - -#. remove ``name`` property from class of application and make sure that it is - not used anywhere in workflow - -#. set version of UI definition to **2.2 or higher** diff --git a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_newton.rst b/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_newton.rst deleted file mode 100644 index 3b360cce6..000000000 --- a/doc/source/admin/appdev-guide/app_migrating/app_migrate_to_newton.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. _app_migrate_to_newton: - -Migrate applications to Stable/Newton -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In Newton a number of useful features that can be used by developers creating -their murano applications were implemented. Also some changes are not backward -compatible. This document describes these features, how they may be included -into the new apps and what benefits the apps may gain. - - -1. New syntax for the action declaration ----------------------------------------- - -Previously, for declaring action in MuranoPL application, following syntax was -used: - - .. code-block:: yaml - - methodName: - Usage: Action - -This syntax is deprecated now for packages with FormatVersion starting from -1.4, and you should use the `Scope` attribute: - - .. code-block:: yaml - - methodName: - Scope: Public - -For more information about actions in MuranoPL, see :ref:`actions`. - - -2. Usage of static methods as Action ------------------------------------- - -Now you can declare static method as action with `Scope` and `Usage` -attributes - - .. code-block:: yaml - - methodName: - Scope: Public - Usage: Static - -For more information about static methods in MuranoPL, see :ref:`static_methods_and_properties`. - -3. Template contract support ----------------------------- - -New contract function ``template`` was introduced. ``template`` works -similar to the ``class`` in regards to the data validation but does not -instantiate objects. The template is just a dictionary with object model -representation of the object. - -It is useful when you do not necessarily need to pass the actual object as a -property or as a method argument and use it right away, but rather to create -new objects of this type in runtime from the given template. It is especially -beneficial for resources replication or situations when object creation -depends on some conditions. - -Objects that are assigned to the property or argument with ``template`` -contract will be automatically converted to their object model -representation. - -4. Multi-region support ------------------------ - -Starting from Newton release cloud resource classes (instances, networks, -volumes) can be explicitly put into OpenStack regions other than environment -default. Thus it becomes possible to have applications that make use of more -than one region including stretching/bursting to other regions. - -Each resource class has got new ``regionName`` property which controls its -placement. If no value is provided, default region for environment is used. -Applications wanting to take advantage of multi-region support should access -security manager and Heat stacks from regions of their resources rather than -from the environment. - -Regions need to be configured before they can be used. Please refer to -documentation on how to do this: :ref:`multi_region`. - -Changes in the core library -``````````````````````````` - -`io.murano.Environment` class contains `regions` property with list of -`io.murano.CloudRegion` objects. Heat stack, networks and agent listener are -now owned by `io.murano.CloudRegion` instances rather than by `Environment`. - -You can not get `io.murano.resources.Network` objects from -`Enviromnent::defaultNetworks` now. This property only contains templates for -`io.murano.CloudRegion` default networks. - -The proper way to retrieve `io.murano.resources.Network` object is now the -following: - - .. code-block:: yaml - - $region: $instance.getRegion() - $networks: $region.defaultNetworks - -5. Changes to property validation ---------------------------------- - -`string()` contract no longer converts to string anything but scalar values. - -6. Garbage collection ---------------------- - -New approach to resource deallocation was introduced. - -Previously murano used to load ``Objects`` and ``ObjectsCopy`` sections of the -JSON object model independently which cause for objects that were not deleted -between deployments to instantiate twice. If deleted objects were to cause any -changes to such alive objects they were made to the objects loaded from -``ObjectsCopy`` and immediately discarded before the deployment. -Now this behaviour is changed and there are no more duplicates of the same object. - -Applications can also make use of the new features. Now it is possible to -perform on-demand destruction of the unreferenced MuranoPL objects during the -deployment from the application code. -The ``io.murano.system.GC.GarbageCollector.collect()`` static method may be -used for that. - -Also objects obtained ability to set up destruction dependencies to the -other objects. Destruction dependencies allow to define the preferable order -of objects destruction and let objects be aware of other objects destruction, -react to this event, including the ability to prevent other objects from -being destroyed. - -Please refer to the documentation on how to use the -:ref:`Garbage Collector `. diff --git a/doc/source/admin/appdev-guide/app_unit_tests.rst b/doc/source/admin/appdev-guide/app_unit_tests.rst deleted file mode 100644 index 03b07f8af..000000000 --- a/doc/source/admin/appdev-guide/app_unit_tests.rst +++ /dev/null @@ -1,220 +0,0 @@ -.. _app-unit-tests: - -====================== -Application unit tests -====================== - -Murano applications are written in :ref:`MuranoPL `. -To make the development of applications easier and enable application -testing, a special framework was created. So it is possible to add -unit tests to an application package and check if the application is in -actual state. Also, application deployment can be simulated with unit tests, -so you do not need to run the murano engine. - -A separate service that is called *murano-test-runner* is used to run -MuranoPL unit tests. - -All application test cases should be: - -* Specified in the MuranoPL class, inherited from - `io.murano.test.testFixture `_ - - This class supports loading object model with the corresponding `load(json)` - function. Also it contains a minimal set of assertions such as - ``assertEqual`` and etc. - - Note, that test class has the following reserved methods are: - - * *initialize* is executed once, like in any other murano application - * *setUp* is executed before each test case - * *tearDown* is executed after each test case - -* Named with *test* prefix - -.. code-block:: console - - usage: murano-test-runner [-h] [--config-file CONFIG_FILE] - [--os-auth-url OS_AUTH_URL] - [--os-username OS_USERNAME] - [--os-password OS_PASSWORD] - [--os-project-name OS_PROJECT_NAME] - [-l [ [ ...]]] [-v] - [--version] - - [ [ ...]] - - positional arguments: - - Full name of application package that is going to be - tested - - List of method names to be tested - - optional arguments: - -h, --help show this help message and exit - --config-file CONFIG_FILE - Path to the murano config - --os-auth-url OS_AUTH_URL - Defaults to env[OS_AUTH_URL] - --os-username OS_USERNAME - Defaults to env[OS_USERNAME] - --os-password OS_PASSWORD - Defaults to env[OS_PASSWORD] - --os-project-name OS_PROJECT_NAME - Defaults to env[OS_PROJECT_NAME] - -l [ [ ...]], --load_packages_from [ [ ...]] - Directory to search packages from. Will be used instead of - directories, provided in the same option in murano configuration file. - -v, --verbose increase output verbosity - --version show program's version number and exit - - -The fully qualified name of a package is required to specify the test location. -It can be an application package that contains one or several classes with all -the test cases, or a separate package. You can specify a class name to -execute all the tests located in it, or specify a particular test case name. - -Authorization parameters can be provided in the murano configuration file, or -with higher priority ``-os-`` parameters. - -Consider the following example of test execution for the Tomcat application. -Tests are located in the same package with application, but in a separate class -called ``io.murano.test.TomcatTest``. It contains ``testDeploy1`` and -``testDeploy2`` test cases. -The application package is located in the */package/location/directory* -(murano-apps repository e.g). As the result of the following command, both -test cases from the specified package and class will be executed. - -.. code-block:: console - - murano-test-runner io.murano.apps.apache.Tomcat io.murano.test.TomcatTest -l /package/location/directory /io.murano/location -v - -The following command runs a single *testDeploy1* test case from the -application package. - -.. code-block:: console - - murano-test-runner io.murano.apps.apache.Tomcat io.murano.test.TomcatTest.testDeploy1 - -The main purpose of MuranoPL unit test framework is to enable mocking. -Special :ref:`yaql` functions are registered for that: - -`def inject(target, target_method, mock_object, mock_name)` - ``inject`` to set up mock for *class* or *object*, where mock definition is a *name of the test class method* - -`def inject(target, target_method, yaql_expr)` - ``inject`` to set up mock for *a class* or *object*, where mock definition is a *YAQL expression* - -Parameters description: - -**target** - MuranoPL class name (namespaces can be used or full class name - in quotes) or MuranoPL object - -**target_method** - Method name to mock in target - -**mock_object** - Object, where mock definition is contained - -**mock_name** - Name of method, where mock definition is contained - -**yaql_expr** - YAQL expression, parameters are allowed - -So the user is allowed to specify mock functions in the following ways: - -* Specify a particular method name -* Provide a YAQL expression - -Consider how the following functions may be used in the MuranoPL class with -unit tests: - -.. code-block:: yaml - - Namespaces: - =: io.murano.test - sys: io.murano.system - - Extends: TestFixture - - Name: TomcatTest - - Methods: - initialize: - Body: - # Object model can be loaded from JSON file, or provided - # directly in MuranoPL code as a YAML insertion. - - $.appJson: new(sys:Resources).json('tomcat-for-mock.json') - - $.heatOutput: new(sys:Resources).json('output.json') - - $.log: logger('test') - - $.agentCallCount: 0 - - # Mock method to replace the original one - agentMock: - Arguments: - - template: - Contract: $ - - resources: - Contract: $ - - timeout: - Contract: $ - Default: null - Body: - - $.log.info('Mocking murano agent') - - $.assertEqual('Deploy Tomcat', $template.Name) - - $.agentCallCount: $.agentCallCount + 1 - - # Mock method, that returns predefined heat stack output - getStackOut: - Body: - - $.log.info('Mocking heat stack') - - Return: $.heatOutput - - testDeploy1: - Body: - # Loading object model - - $.env: $this.load($.appJson) - - # Set up mock for the push method of *io.murano.system.HeatStack* class - - inject(sys:HeatStack, push, $.heatOutput) - - # Set up mock with YAQL function - - inject($.env.stack, output, $.heatOutput) - - # Set up mock for the concrete object with mock method name - - inject('io.murano.system.Agent', call, $this, agentMock) - - # Mocks will be called instead of original function during the deployment - - $.env.deploy() - - # Check, that mock worked correctly - - $.assertEqual(1, $.agentCallCount) - - - testDeploy2: - Body: - - inject(sys:HeatStack, push, $this, getStackOut) - - inject(sys:HeatStack, output, $this, getStackOut) - - # Mock is defined with YAQL function and it will print the original variable (agent template) - - inject(sys:Agent, call, withOriginal(t => $template) -> $.log.info('{0}', $t)) - - - $.env: $this.load($.appJson) - - $.env.deploy() - - - $isDeployed: $.env.applications[0].getAttr(deployed, false, 'com.example.apache.Tomcat') - - $.assertEqual(true, $isDeployed) - -Provided methods are test cases for the Tomcat application. Object model and -heat stack output are predefined and located in the package ``Resources`` -directory. By changing some object model or heat stack parameters, different -cases may be tested without a real deployment. Note, that some asserts are used -in those example. The first one is checked, that agent call function was called -only once as needed. And assert from the second test case checks for a variable -value at the end of the application deployment. - -Test cases examples can be found in :file:`TomcatTest.yaml` class of the -Apache Tomcat application located at `murano-apps repository `_. -You can run test cases with the commands provided above. diff --git a/doc/source/admin/appdev-guide/cinder_volume_supporting.rst b/doc/source/admin/appdev-guide/cinder_volume_supporting.rst deleted file mode 100644 index 6d30756f0..000000000 --- a/doc/source/admin/appdev-guide/cinder_volume_supporting.rst +++ /dev/null @@ -1,152 +0,0 @@ -.. _cinder_volume_supporting: - -Cinder volume support -~~~~~~~~~~~~~~~~~~~~~ - -Cinder volume is a block storage service for OpenStack, which represents a -detachable device, similar to a USB hard drive. You can attach a volume to -only one instance. In murano, it is possible to work with Cinder volumes -in several ways: - -* Attaching Cinder volumes to murano instance -* Booting from Cinder volume - -Below both ways are considered with ApacheHttpServer application as an -example. - -For more information about Cinder volumes, see -`Manage Cinder volumes -`_. - -Attaching Cinder volumes ------------------------- - -Several volumes can be attached to the murano instance. Consider an example -that shows how to attach a created volume to the instance (next, in the -*Booting from Cinder volume* section, we are going to boot from a volume -created by us). - -**Example** - -#. In the OpenStack dashboard, go to :guilabel:`Volumes` to create a volume. - -#. Modify the ``ui.yaml`` file: - -.. code-block:: yaml - - .... - - Application: - .... - instance: - .... - volumes: - $.volumeConfiguration.volumePath: - ?: - type: io.murano.resources.ExistingCinderVolume - openstackId: $.volumeConfiguration.volumeID - - .... - -An existing Cinder volume can be initialized with its ``openstackId`` and can -be attached with its ``volumePath``. These parameters come here from -modified ``Forms`` section of the ``ui.yaml`` file: - -.. code-block:: yaml - - .... - - Forms: - - appConfiguration: - .... - - instanceConfiguration: - .... - - volumeConfiguration: - fields: - - name: volumeID - type: string - label: Existing volume ID - description: Put in existing volume openstackID - required: true - - name: volumePath - type: string - label: Path - description: Put in volume path to be mounted - required: true - -Therefore, create a ZIP archive of the built package and upload it to murano. -Attach created application to the environment. Enter its openstackId (which -can be found in OpenStack dashboard) and path for mounting. For example, you -can fill the latter with ``/dev/vdb`` value. - -After the application is deployed, verify that the volume is attached to the -instance in the OpenStack dashboard :guilabel:`Volumes` tab. Alternatively, -see the topology of the ``Heat Stack``. - - -Booting from Cinder volume --------------------------- - -You can create a volume from an existing image. The example below shows how to -create a volume from an image and use the volume to boot an instance. - -**Example** - -It is possible to create a volume through the Heat template, instead of -the OpenStack dashboard. For this, modify the ``ui.yaml`` file: - -.. code-block:: yaml - - .... - - Templates: - customJoinNet: - .... - bootVolumes: - - volume: - ?: - type: io.murano.resources.CinderVolume - size: $.instanceConfiguration.volSize - sourceImage: $.instanceConfiguration.osImage - bootIndex: 0 - deviceName: vda - deviceType: disk - .... - - Application: - .... - instance: - .... - blockDevices: $bootVolumes - - .... - -The example above shows that the ``Templates`` section now has a -``bootVolumes`` field, which is stored in the changed ``Application`` -section. -Pay attention that ``image`` property should be deleted from -``Application`` to avoid defining both image and volume to boot. -The ``size`` and ``sourceImage`` properties come in ``Templates`` from the -changed ``Forms`` section of the ``ui.yaml`` file: - -.. code-block:: yaml - - .... - - Forms: - - appConfiguration: - .... - - - instanceConfiguration: - fields: - .... - - name: volSize - type: integer - label: Size of volume - required: true - description: >- - Specify volume size which is going to be created from image - .... - -After sending this package to murano you can boot your instance from the -volume by chosen image. diff --git a/doc/source/admin/appdev-guide/developer_index.rst b/doc/source/admin/appdev-guide/developer_index.rst deleted file mode 100644 index 23a9d1462..000000000 --- a/doc/source/admin/appdev-guide/developer_index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. _developer-guide: - -Application Developer Guide -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 1 - - step-by-step/step_by_step - exec_plan - hot_packages - murano_pl - murano_packages - murano_bundles - app_migrating - app_unit_tests - cinder_volume_supporting - multi_region - examples - use_cases - app_development_framework - app_debugging - garbage_collection - encrypting_properties diff --git a/doc/source/admin/appdev-guide/encrypting_properties.rst b/doc/source/admin/appdev-guide/encrypting_properties.rst deleted file mode 100644 index 179afe2e9..000000000 --- a/doc/source/admin/appdev-guide/encrypting_properties.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _encrypting-properties: - -================================= -Managing Sensitive Data in Murano -================================= - -Overview --------- -If you are developing a Murano application that manages sensitive data such as -passwords, user data, etc, you may want to ensure this is stored in a secure -manner in the Murano backend. - -Murano offers two `yaql` functions to do this, `encryptData` and -`decryptData`. - -.. note:: Barbican or a similar compatible secret storage backend must be - configured to use this feature. - -Configuring ------------ -Murano makes use of Castellan_ to manage encryption using a supported secret -storage backend. As of OpenStack Pike, Barbican_ is the only supported -backend, and hence is the one tested by the Murano community. - -To configure Murano to use Barbican, place the following configuration into -`murano-engine.conf`:: - - [key_manager] - auth_type = keystone_password - auth_url = - username = - password = - user_domain_name = - -Similarly, place the following configuration into `_50_murano.py` to configure -the murano-dashboard end:: - - KEY_MANAGER = { - 'auth_url': '/v3', - 'username': '', - 'user_domain_name': '', - 'password': '', - 'project_name': '', - 'project_domain_name': '' - } - -.. note:: Horizon config must be valid Python, so the quotes above are important. - -Example -------- -`encryptData(foo)`: Call to encrypt string `foo` in storage. Will return a -`uuid` which is used to retrieve the encrypted value. - -`decryptData(foo_key)`: Call to decrypt and retrieve the value represented by -`foo_key` from storage. - -There is an example application available in the murano repository_. - -.. _Castellan: https://opendev.org/openstack/castellan -.. _Barbican: https://opendev.org/openstack/barbican -.. _repository: https://opendev.org/openstack/murano/src/branch/master/contrib/packages/EncryptionDemo diff --git a/doc/source/admin/appdev-guide/examples.rst b/doc/source/admin/appdev-guide/examples.rst deleted file mode 100644 index 51a65c58a..000000000 --- a/doc/source/admin/appdev-guide/examples.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. _examples: - -======== -Examples -======== - -.. list-table:: - :header-rows: 1 - :widths: 30 70 - :stub-columns: 0 - :class: borderless - - * - Application name - - Description - - * - | `Zabbix Agent`_ - - Zabbix Agent is a simple application. It doesn't deploy a VM by itself, - but is installed on a specific VM that may contain any other - applications. This VM is tracked by Zabbix and by its configuration. - - So Murano performs the Zabbix agent configuration based on the user - input. The user chooses the way of instance tracking - HTTP or ICMP that may - perform some modifications in the application package. - - It is worth noting that application scripts are written in Python, not - in Bash as usual. This application does not work without Zabbix server - application since it's a required property, determined in the - application definition. - - * - | `Zabbix Server`_ - - Zabbix Server application interacts with Zabbix Agent by calling its - setUpAgent method and providing information about itself: IP and hostname - of VM on which the server is installed. - - Server installs MySQL database and requests database name, password and - some other parameters from the user. - - * - | `Docker Crate`_ - - This is a good example on how difficult logic may be simplified with - the inheritance that is supported by MuranoPL. Definition of this app is - simple, but the opportunity it provides is fantastic. - - Crate is a distributed database, in the Murano Application catalog it - looks like a regular application. It may be deployed on Google Kubernetes - or regular Docker server. The user picks the desired option while filling in - the form since these options are set in the UI definition. The form field - has a list of possible options:: - - ... - type: - - com.mirantis.docker.kubernetes.KubernetesPod - - com.mirantis.docker.DockerStandaloneHost - - Information about the application itself (docker image and port that is - needed to be opened) is contained in the getContainer method. All other - actions for the application configuration are located at the - DockerStandaloneHost definition and its dependencies. Note that this - application doesn't have a filename:Resources folder at all since the - installation is made by Docker itself. - - - -.. Links: -.. _`Zabbix Agent`: https://github.com/openstack/murano-apps/tree/master/ZabbixAgent/package -.. _`Zabbix Server`: https://github.com/openstack/murano-apps/tree/master/ZabbixServer/package -.. _`Docker Crate`: https://github.com/openstack/murano-apps/tree/master/Docker/Applications/Crate/package diff --git a/doc/source/admin/appdev-guide/exec_plan.rst b/doc/source/admin/appdev-guide/exec_plan.rst deleted file mode 100644 index 40e85085e..000000000 --- a/doc/source/admin/appdev-guide/exec_plan.rst +++ /dev/null @@ -1,174 +0,0 @@ -.. _exec_plan: - -======================= -Execution plan template -======================= - -An execution plan template is a set of metadata that describes -the installation process of an application on a virtual -machine. It is a minimal executable unit that can be -triggered in Murano workflows and is understandable to -the Murano agent, which is responsible for receiving, -correctness verification and execution of the statements -included in the template. - -The execution plan template is able to trigger any type of script -that executes commands and installs application components -as the result. Each script included in the execution -plan template may consist of a single file or a set of interrelated -files. A single script can be reused across several execution -plans. - -This section is devoted to the structure and syntax of an execution -plan template. For different configurations of templates, please -refer to the :ref:`Examples ` section. - -Template sections -~~~~~~~~~~~~~~~~~ - -The table below contains the list of the sections that can be -included in the execution plan template with the description of -their meaning and the default attributes which are used by the -agent if any of the listed parameters is not specified. - -================== =================================================== - Section name Meaning and default value -================== =================================================== - FormatVersion a version of the execution plan template syntax - format. Default is ``1.0.0``. **Optional** - - Name a human-readable name for the execution plan to - be used for logging. **Optional** - - Version a version of the execution plan itself, is used - for logging and tracing. Each time the content - of the template content changes (main script, - attached scripts, properties, etc.), the version - value should be incremented. - This is in contrast with ``FormatVersion``, - which is used to distinguish the execution plan - format. - The default value is ``0.0.0``. **Optional** - - Body string that represents the Python statement and is - executed by the murano-agent. Scripts defined in - the Scripts section are invoked from here. - **Required** - - Parameters a dictionary of the ``String->JsonObject`` type - that maps parameter names to their values. - **Optional**. - - Scripts a dictionary that maps script names to their - script definitions. **Required** -================== =================================================== - - -.. _format_version: - -FormatVersion property -~~~~~~~~~~~~~~~~~~~~~~ - -``FormatVersion`` is a property that all other depend on. -That is why it is very important to specify it correctly. - -FormatVersion 1.0.0 (default) is still used by Windows murano-agent. -Almost all the applications in murano-apps repository work with FormatVersion -2.0.0. New features that are introduced in Kilo, such as Chef or Puppet, -and downloadable files require version 2.1.0 or greater. Since FormatVersion -2.2.0 it is possible to enable Berkshelf. It requires Mitaka version of agent. -If you omit the ``FormatVersion`` property or put something like ``<2.0.0``, -it will lead to the incorrect behaviour. The same happens if, for example, -``FormatVersion=2.1.0``, and a VM has the pre-Kilo agent. - - -Scripts section -~~~~~~~~~~~~~~~ - -Scripts are the building blocks of execution plan templates. As -the name implies those are the scripts for different deployment -platforms. - -Each script may consists of one or more files. Those files are -script's program modules, resource files, configs, certificates etc. - -Scripts may be executed as a whole (like a single piece of code), -expose some functions that can be independently called in an execution -plan script or both. This depends on deployment platform and executor -capabilities. - -Scripts are specified using ``Scripts`` attribute of execution plan. -This attribute maps script name to a structure (document) that describes -the script. It has the following properties: - -**Type** - the name of a deployment platform the script is targeted to. - The available alternative options for version>=2.1.0 are - ``Application``, ``Chef``, ``Puppet``, and for version<2.1.0 is - ``Application`` only. String, required. - -**Version** - the minimum version of the deployment platform/executor required - by the script. String, optional. - -**EntryPoint** - the name of the script file that is an entry point for this - execution plan template. String, required. - -**Files** - the filenames of the additional files required for the script. Thus, - if the script specified in the ``EntryPoint`` section imports other - scripts, they should be provided in this section. - - The filenames may include slashes that the agent preserve on VM. - If a filename is enclosed in the angle brackets (<...>) it will be - base64-encoded. Otherwise, it will be treated as a plain-text that - may affect line endings. - - In Kilo, entries for this property may be not just strings but also - dictionaries (for example, ``filename: URL``) to specify downloadable files - or git repositories. - - The default value is ``[]`` that means that no extra files are used. - Array, optional. - -**Options** - an optional dictionary of type ``String->JsonObject`` that contains - additional options for the script executor. If not provided, an - empty dictionary is assumed. - - Available alternatives are: ``captureStdout``, ``captureStderr``, - ``verifyExitcode`` (raise an exception if result is not positive). - As Options are executor-dependent, these three alternatives - are available for the Application executor, but may have no sense for - other types. ``captureStdout``, ``captureStderr`` and ``verifyExitcode`` - require boolean values, and have True as their default values. - - Dictionary, optional. - -Please make sure the files specified in EntryPoint and Files sections exist. - -.. needs checking, commenting it for now - - Files section - ~~~~~~~~~~~~~ - - Files is an execution plan's entry that describes files that are passed - as the part of the execution plan template. This is a dictionary that - maps file ID to a document describing the file. - - It has the following attributes: - - **Name** - the filename; may include slashes to specify files located in nested - folders. The root directory is the ``Resources/scripts`` directory. - - **BodyType** - is one of the following: - * ``Text``: Body attribute contains string content of the file - * ``Base64``: Body attribute contains base64 encoded string content of - the (binary) file - - **Body** - contains file data or valid file reference - diff --git a/doc/source/admin/appdev-guide/faq.rst b/doc/source/admin/appdev-guide/faq.rst deleted file mode 100644 index 907863672..000000000 --- a/doc/source/admin/appdev-guide/faq.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. _faq: - -=== -FAQ -=== - -**There are too many files in Murano package, why not to use a single -Heat Template?** - - To install a simple Apache service to a new VM, Heat Template is - definitely simpler. But the Apache service is useless without its - applications running under it. Thus, a new Heat Template is necessary - for every application that you want to run with Apache. In Murano, - you can compose a result software to install it on a VM on-the-fly: - it is possible to select an application that can run under Apache - dynamically. Or you can set a VM where Apache is installed as a - parameter. This way, the files in the application package allow - to compose compound applications with multiple configuration options. - For any single combination you need a separate Heat Template. - -**The Application section is defined in the UI form. Can I remove it?** - - No. The ``Application`` section is a template for Murano object model - which is the instruction that helps you to understand the - environment structure that you deploy. While filling the forms that - are auto-generated from the UI.yaml file, object model is - updated with the values entered by the user. Eventually, the Murano - engine receives the resulted object model (.json file) after the - environment is sent to the deploy. - -**The Templates section is defined in the UI form. What's the purpose?** - - Sometimes, the user needs to create several instances with the same - configuration. A template defined by a variable in the - ``Templates`` section is multiplied by the value of the number of - instances that are set by the user. A YAQL ``repeat`` function is - used for this operation. - -**Some properties have Usage, others do not. What does this affect?** - - ``Usage`` indicates how a particular property is used. The default - value is ``In``, so sometimes it is omitted. The ``Out`` property - indicates that it is not set from outside, but is calculated in - the class methods and is available for the ``read`` operation from - other classes. If you don't want to initialize in the class - constructor, and the property has no default value, you specify - ``Out`` in the ``Usage``. - -**Can I use multiple inheritance in my classes?** - - Yes. You can specify a list of parent classes instead of a single - string in the regular YAML notation. The list with one element is - also acceptable. - -**There are FullName and Name properties in the manifest file. What's -the difference between them?** - - ``Name`` is displayed in the web UI catalog, and ``FullName`` is a - system name used by the engine to get the class definition and - resolve the class interconnections. - -**How does Murano know which class is the main one?** - - There is no ``main`` class term in the MuranoPL. Everything depends - on a particular object model and an instance class representing the - instance. Usually, an entry-point class has exactly the same name - as the package FullName, and it uses other classes. - -**What is the difference between $variable and $.variable in the class -definitions?** - - By default, ``$`` represents a current object (similar to ``self`` - in Python or ``this`` in C++/Java/C#), so ``$.variable`` accesses - the object field/property. In contrast, ``$variable`` (without a dot) - means a local method variable. Note that ``$`` can change its value - during execution of some YAQL functions like select, where it means - a current value. A more safe form is to use a reserved variable - ``$this`` instead of ``$``. ``$this.variable`` always refers to an - object-level value in any context. diff --git a/doc/source/admin/appdev-guide/figures/chef_server.png b/doc/source/admin/appdev-guide/figures/chef_server.png deleted file mode 100644 index 98b60aa3d..000000000 Binary files a/doc/source/admin/appdev-guide/figures/chef_server.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/chef_server_form.png b/doc/source/admin/appdev-guide/figures/chef_server_form.png deleted file mode 100644 index 80dd09617..000000000 Binary files a/doc/source/admin/appdev-guide/figures/chef_server_form.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/logo.png b/doc/source/admin/appdev-guide/figures/logo.png deleted file mode 100644 index 29990d097..000000000 Binary files a/doc/source/admin/appdev-guide/figures/logo.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/step_1.png b/doc/source/admin/appdev-guide/figures/step_1.png deleted file mode 100644 index d09b5315d..000000000 Binary files a/doc/source/admin/appdev-guide/figures/step_1.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/step_2.png b/doc/source/admin/appdev-guide/figures/step_2.png deleted file mode 100644 index 3bd995426..000000000 Binary files a/doc/source/admin/appdev-guide/figures/step_2.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/structure.png b/doc/source/admin/appdev-guide/figures/structure.png deleted file mode 100644 index 2b48019ea..000000000 Binary files a/doc/source/admin/appdev-guide/figures/structure.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/figures/structure.vdx b/doc/source/admin/appdev-guide/figures/structure.vdx deleted file mode 100644 index d2743ddd3..000000000 --- a/doc/source/admin/appdev-guide/figures/structure.vdx +++ /dev/null @@ -1 +0,0 @@ -1.36590.3152.66291.09280.68290.1575-0.01.36590.3150.68290.15750.68290.1575-0.0#000000#0000001.00.010.0125#0000001040.00312510#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.15751.36590.15750.682950.00.682950.3151.36590.3150.682950.15750.682950.15750.0#000000#0000001.00.010.00625#00cccc1000.0031251111110001.09260.31471.36570.15720.01.00.01.0NURBS(1.0,3,0,0,0.9106,0.999,0.0,1.0,0.9999,0.7768,0.0,1.0)1.0926-3.0E-40.01.00.01.0NURBS(1.0,3,0,0,0.9999,0.2235,0.0,1.0,0.9106,-0.001,0.0,1.0)0.2732-3.0E-41.0E-40.15720.01.00.01.0NURBS(1.0,3,0,0,0.0898,-0.001,0.0,1.0,1.0E-4,0.2235,0.0,1.0)0.27320.31470.01.00.01.0NURBS(1.0,3,0,0,1.0E-4,0.7768,0.0,1.0,0.0898,0.999,0.0,1.0)1.09260.31471images.lst1.00121.00122.43726.8740.50060.5006-0.0#000000#0000001.00.010.0125#0000001040.0031251100.00.50061.00120.50060.50060.00.50061.00121.00121.00120.50060.50060.50060.50060.0111111iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAgAElEQVR4nO2daZBc13men3P32/vsA4AgNi7gIhIkTUmh6BAuWatlexw5FS+RBakC25FdMfIjyU8QVU7KrrhiqhwvMeIyaEmJaLssSKDlTbaGkmVRpBiBErhhGc5gMFvPTE/vt++eH909093TPZilB6SlflEX3X373HNPn+897/ed7557B3rooYceeuihhx566KGHHnrooYceeuihhx566KGHHnrooYceeuihhx566KGHHnr4PoF4qxvQirNnhg5E48lTB+68f8wwIgc9z80WcpmLb77x3XFg/OTpxefe6jZ+P+FtRYCzZ4YOHH345y8++iO/nNJ1DYIFCDMQpAGYu36NuekJioXsxRsTr18slwoXgYs9UmwfylvdgBac+qHjv5JSRBbf8UGYCOluhPoIhAX2HL6fPQdXIEgfA/dYIZdhbnqC8fuukc8uXVyYmboI7Dopzp4ZOg08Wft4ERivbRdPnl6c2q3z7gbeVgrwpT/60Hc+/LO/fozAAsKWb0MQGkKKg2QihAQUIViEcAUR5rArFnPT18ik55ibvsbc9MQqIegSKc6eGfpjTY+fuP+Rd3Pn/Y8QT/Yzd/0ay+lZ5qYnWFman8xnl8dr5xw/eXrx5Z2eczfxtiLA+Bd+OXzP+34GwkbjNxNBNH2Wq2SQEyAZCEoQVkkhwiWAJuMsp2cp5le2TYr6yB/75Gfp6zNBiiBJElCAsLoJKk1EnJ95M7uyOD9ulYt1Qryt3NXbhgBnzww98cgTnxp/x8OPs2p0QfV9qxjQmSCICEKOgxRFCBtBBYJlCJcQeNTdxnJ6lrnr18gszkEDIehAirNnhp4Axh96/Bd58JFjhN587Xyids4kQh5ASCpC0iFcAYqIMAvAcnp2dcukZ8dr6jROlRS5bXfcDvF2IsDHP/Qz/+PcyJ6B2p5Gw69jAJ1IEDb8LwCEDlIEISUQklwbpVkIi1XFYC24nJuuqoVjV6CFFMC5/uE7D/7kx/47QeXF2tlE51elHyElQIojSRoIB0ERwhwCu+G8k+SyK5O55YXxpYWpcaoEvGVu4+1EgN/+2Km/OCWRqdmv0Yg0uYVwC4Ro/By61wkDG0kZRigjSNptCOECVi2OWAFYVYe66yjmV9D0GD/58d/DVK9A6NJkbAGiqSvbEEIoICUQSl+NjAKBBVQAgRAahXy+phDXWVmaHn/ztb8/D5zbTYV42xDgz373XV8d+9ivHw+D+m8NG15ajSva7GPDz4G3DEGW+k8WyhBCilXfiyjISYQUQUgB4NakO4vAx65Y2M4IpiEIgyyEPuBB6NTe18/XbHgh1u9r6nIpXg1q5RhCjiGkGELIIOkIEcW2Hf728790ceHGd47vFgmapoFnzwwlgTEgyy32TZHY0LHQzwJedcfqiA9X+yxcR4bmz6KDMoRBGfxlViuSEwih10YyhOEKYbCydojQEVISIe9HSDFkrYIpzUJQrI10CYQGaCAgxAf8KhlCDwiq511tTge1CHKEQb7KpdW2pRBSEkm/E90Y5LEPnDr2hT/6+Ang05voxi1jlQBP/8ae3z581z2nHvnhn8DzfKauvMJz75i/ODN5ebxUyJ3f5Xl1cnjvXSmCMgBhoy6FnWKBViVo5xhCCD1Cb4GqUUTNuDHC0GG9ktQPcwiDQtWWTadpJBsN7+ttlEHIDa0JqipB0FB3naYtilAnhp8h9FfwvUVE/DEGhgcAjrPbBBjdkzw1NOgz/cZfEU3sZe9tI9x534PH9A/+62Nz16+deumxCVaWF8bffOO75+n+/PZY/9A+QtxqR4aNoVy7kb15+Q/cBcCtfVIQcoIwdNr4vrDN23aKUt3dVo1aiVvfGfotBURDcFMv30KMsEDozyGEB9UgdFewSoDMUjF7+4G+VBg4FFbKlHLTZBaiqFoKMzbIXfc/iBFJHn/8/R89Pjd9jW+983p2eWFmfGbqSp0QO8mAHY9GFcLAadh186BPtHxufhcS+nkIq0EWQoAcp6a3hGG7WKJul7C5ynZlRUOR+n9hO0UJEI0KsHpgm5hAtOwL8szdeLX1h3cVqwQoFu2nclnryWTKhNAj8D0Cv4Rnp7GtWQorcWRtAM1IkeobYGTvwZRumGOlYm5sbnqCbzw8M5memRxfWpipE2Ir8cPBvoEBQm+ZzqO70QjV92vEaPiu9jYMbAjyrElsHBGGNUluqFM0HFc/Y9iGcE22bc1NhB3KVb8LV9u3VqhtgBg275MkB3DgVigA8NT0VOZUMrUv1Voo8IoEXhG3Modd1Cnnkqj6CIo2iBFNsXf/UQ7ddeygqiknltOzJ+auX+O5d8xfvPy9F+tk2DB+2H/kncdCv1Dzy/WR3Xmqt/p/fcSJhu9DIPQJ/dxqSSEMBBKENuss1GZ0t1WcJrcgGtrZWq51RsD6kY1oHyACQjQcFzrMTU9CNSjfFawS4OTpxdzZM0Pni0X7RCymdzwgDGw8O41nV6/QlbIJNGMvijGKHtmLpg9yx32jqJp07F9W4wdeemyChZnJ8zNTV8ZpEz/EEgPHQj9Ds4Gb3zWNStGyr8Vnh0Gh6ndF7ScKpUauljrbTik32t9aqs3IhvUGbxnZq6+CdfvW+GOC0OkfPgywblB2C00kPntm6MDwSHzyrqMj26tMqKjGHlRzL3r0DjQjhaYLVDVAVd3VHHl69np2durK+aWFmXGAH3rPh84dve8QhOF647cau+V70fI5DO2azIvqJpmNlbRp9fp9awrUSIJ2iR7aju715ToQo+1xNWIIEz1xFDD56rN/MHntleeeBM53e2q+TsXOnhn64x9614EThqHuuHJJSaCZ+zHid6NHj6DpGqpqI8sVZMlZzcsPDQ9gaH5DtLzNqD/0qwRAVLtcqA2d3lq8hWCr6GDojkbsYMzWKL9DQmijOuXIO5C1IYQUpZDLMnXlBS6//Oz5TPryqW5ddm5HgB2pwEZQ9VGMxH0Ysbsw4neiyCVkuYwsCgjhEvgBgVfBdzIEbmtASNPnMGww4Goo7jaUlQGp6ZimH7sZA7UL1FrKiKayHeppPG6zdQoBqAhlAEndg6SOImkHQJh89fx/nrx26fyxbqiB3Lrjwng5995HleN7b0serF7q7B4Cv4hTfpNy9tsUFr9GpbyA68ig3AfyIZAHkNQhVHMfavRBJO02EAaEbjWoCxuzbfXsW/29y9oMgdqrDwS1aV1APUPXXIZanqEeTDZ/t1a+4TjRqUxrnZsr21ymuf1hUCBw5wncGYRkImmHSPQfSr3+0mfmL4yXv7Vxj98cnVYEPTl7Izd++8H+ndbfEWFgYeVexsq9zMrMMyjaIGbifqL97yaSfAhZ1ZB0F8m4HyEUAt/CznyewL5ar6H22ngZRjS8NIyypiCs9r6hzGo8HzbWtIGcr5vDrz/vhnWuCxJv7kbCoIBb/DqS8QADwwcAjtEFrFMAgAvj5akffad6fHRPousq0AmBX8YpT1Jc/kdWZv+cUubbeK6Nat6NrA4iJA018jCBM0HoplkdLaLNiGod2bC5kdikAl2qc8tq0f681Smui6wdwHFKfPebT5+/MF5+jh2io3Vd139q9sZbtk4Bu3SN5etPM/nSJ6iUJgED8NH7fw5JHWVV0sP65q+9EgA+IUFt8wnDgLCxTP09a+Wr9fnNdXWsM2hTZ7DWrnqdrfXU20trnfU2VusMW867VqdDZr57eaGOBDh5evGL6XRhsmtn2iYCv8j1i79aJYGIISQNY/jfISnDa8Zqa8Q2xqx39DojtpKoxYht61w7b7jOiH4bYjS2sQMxmgxdb5/fRApJ0bEri1BdTbRjbKjvFct9cmE+TxiGbbeb5Em6hjUSTINIIoSGMfxJhDrSeSSGjZ3ZzohBkyGbjEjjKOxsoPaje61Me2IEtKrKerVo3NbKCMmAIE9m/vWu9W3bGKCOC+Pll9/7qHpi795k+0xU21zoziGagqoqwtChlHme5J4xJCmCEBZK5F4CN03oLnX0n2tJnXZ+vvV1ozLt6qyX3cBvbxgLrJVpqrPDrEBSEij6IJOXn2fsF1/4BF3ATSO8SsV9amGhRQXq/zoow+Y2qsm9Nlun+p3KHFMXf4VKeQ6kUYQwMAd/GnPo51DM+6qJn5ZRu07y2a5atHMjdbVo40Z2QS2qaxrzZNJdyQEBm7sx5NxiuvDk0HBsTQU2Lf1ig0/h+qo2ytjWUClcZuo7n2Lkjv9IavSDEOaQzSiyvgd4H74zj29fx7eu4TuzdLrsGjbtE2uDft3UbhNTNtEy9WsoJ9bVt/68ze3rUGcoqvdChGUgHG/XN9vBhi4A4MJ42X7/v9CNRMI4ruuNfOms/1vyDNtwI0HgUFj6Gpkbf4pdToPUh2I8gCQPIilxZG0QNXIANXYfsjqAkPXqsrDa1cb2Mswm3cgmJX9DV7O9KaqiDyApUb72V386eWG8/PRm+upm2OytYU/dmM6euicxuqoC7dffVbEpgdhwtG+GXCG+WyA79yzZuWcBiPQ9TGLwCSJ9D2PG7kcEyyjyKIq5gJ54gMDL41Vu4DtL+M4y1TWBDQ3omlqslWub9m2ts+G8TUmtFrUQsoZdsaBLMwDYJAHql4pLRftENKoBW/ACrVg1/EZG3h65SpmXKGVeAgGSEicx9ATR1MMkho4jyxaSfB1NGYEwD0LFd1bwKrP4znzLtYdGUjQ2eu11rfmtxNhE7n+bbkRIOpm5mQ16YOvYys2hT87P5U8cPjJw85KrEE3EBxr6tsOCCrpDLt8tsDL7LCuzVXUwY3cSHz5OYugJzNg+CJaQ5TSyfmd1RAYhXmUS357Bs96gupSstUU3UYtVYqz38etjga2rhSQb2PZboAAAJ08vTp09w7m9+5IndL0xdNjAiYuwoQPWY/tphPXTxOYK15PLKlzGKlwmfe0PkZQYieHjxPrq6uAjwiKqshc15gMqvruEb0/iFV8gcG9s0PqWX7FLQaeQDQAy6bk2bdk+thSCnT0z9ODAYOTiocMbq0DrJfjtYSNibbrkpmDE7yJZV4f4HbVI26V6j4KKZ12isnSW+rL17WG9G1lrePN37dyIpPZj9j3CN//hSzz2kS90LQOz5Yr++L+OfPX+B0aPK3KbFMKWa9vaAd0h1sbnldUYyeHj9O39cWJ9D1JdlCnjOzewZv9LtxrQ0o5mtWj3nWrsQ0/ez19+/g/4yCe/1bWe2PKlPs8LnlyYLzQnhDZMDLHB1i693GHbcuJpi+etTb18t0Bm5gLXXvxF5q/9EWACCrI6jJb8YLf6vYb6eRsTRu2vEwh5dZ3meDdbsGUCnDy9+NxiujTuecFNOrlzZ29k7HXZxt0mF53JNX/1f7E49X+pGiaPGjsGovOC2e6hlRj+agwwNz3R1TNt62K/5wXnlpZKWxqRGxq7neFvFbluoiBzV/4Az6sAHgIHLXZvVw2wWUhypOs5ANgmAU6eXnx6abE0uRUJv+kIprZtwrjbPt82yOW5BRYnPwciVVWByH4Qt/7RSpKSIJOe7X692z3Qtv0nM0vlTXT8Bp28qZG4DRVZd56dkSv95ufwfAnQECJAix7umgE2CyGpXc8BwA4IcPL04tPz88XJzY6k9iNx8yN6vbE3SawukMtz8qTf/CwoVflXI4duqQpISgLofg4AdkAAAMfxz2Wz1qZHVKNBdmzkjc6xBSNv9rwLE5/DCyLVy9CSektVQEjVezRs26Lbt+nvdMXnU8tLVvZmBl8V4bYdvr0g71aTy3PzLEx8DpTqYtxbqQJrCvA2igGgepGoWHSeKhad5lHWYvCuGfmtIFcDsRaufeYtUYG6AtBl/w87VwCAp9ILpTU70EUjN2yrld1qcjVU6Ll5Fq599parQF0Bup0DgC4Q4OTpxVyp5J6zHW/TAdfmfTTNxOJWkqs9qebeAhUQkrorOQDojgIAPLmYLm/T2LRT8dpXzRJ8a8nVvh7PyTN/9TO3VAV2KwcAXSLAydOLU9msfc5x/A6dz7ptw47eodFbDbyOXOyMXHNX/wTPv3UqsFs5AOieAgA8tZi2Ohh7a75384Zefy5uMoJ3TqwQzykw/drv3hIVaMkBdP1JIV0jwMnTiy8Xi864ZujIiry1jt7Q8M3ueVvE6hK5GlVr7sqfUKn4qyogGwe71ZVNqM8ACrkMu/EI2a7e+en74ZOZTIW+wX0cOPpvGD7wPrTI8MYGIWzoWNp0fD3a3+a2i+SafvV/rqqAFj3M9FSBxYUi5ZLTtn+2g7oClIq5XXlO0E2XhW8FF8bLU+99p3J8ZEQ/6FYmICyQHLyX5NAjyHIUzy3hezbNkdfOV/Q0YV1lovNXO0Qp9xrDhz6GIpeRhEXg+6wszmCVHfLZCq5bjYkURW5++NMWoOjDyNoAL39r/Pk/+3K6K0vBG9H1e789NziXXigihECIERan/o7c3F+iKln2HHqc/Ud/msTgOxCSvjqSgiAgCLYzwtcH9Z0UZEcq0k61alVff2VNBQb33YuiatSpZlkuKxmLuZkcmeUSpaKN7wVteq0zZK26/C67vPD2VwCo3k/4vndrJ/buS6aEsIn33w0ijlVYIpd+gcCdIRofon/Po0QSRxCyhlPJEQbumu9twk3uEej2sG533g5rUAFK2dcYObymAmHoYxUWagOA6qsk8P0Ax/Gxyi6OU30eshBws+cvqOZ+SsUKr7z0j5/vxvMAWtF1AgB8+HEjq+vKWCQicCoFfG8JI9ZHcuA+wkCnkHmDwvIlQm+OSGKIvpFHiCQOQwi2tbzml2vYQNV3gE3cfLLJ83hugcHbfgr8qxjRfrLpy4QEqySQpOpDq4QAIQnCMMR1A2zbx7F9giBEkgSStP6ERvIYmfQsV1556Xw3HgnTil0hwIXx8ss/+k7lxMhoPKWZw0hSCit/g3LhOkKyiaYOEY0fwPdCSitXKWVfRxJZYsl99I8+gpk4hGPn8JxSVbrrFe/CotNukKu48hqjh38BRWmjAlKDEjSpAki1uCDwA1zXx3V8wqAaFNWVQYvdxdz0BFNXX/mNC+Plqa23bmPsCgEAPvzDZsqMqMdVpULg5zHj+zGjt1WNnruObc2jaDKx1GGMyB4cq0Ix8wpW8RqKXCHZfwd9o4+iR0bwnCKuW1ynDFVs3mIbltwhuTw3z+D+mgpE+sktXqF6R2/z6G90C/X3Up0YVB8U6fsBnhsgawPosduZuvIKc9MTT10YLy9stZU3w64R4Cd+JHrRc/1fHhiMGEb0Dkq5CWxrHlmRiCQOYpiD2FaF4so1fC9bJUP/PahqCquYJ7/4Ep49g6I4pIbuITV0DFk2cd0SgWfXiLDeat0Vic3VJqipwJGPN6hAgFVMbzj6V/dJLd/XyygRjPgBLl/6Nh/91Hf//VZ/2mawawS4MF62P/CYbsTj+nFZLmPG9mOYQ/iuRzF7GdctoGg6sdQRZDlOpVykmHmdkAqaYZLoP4oQEcr5ZXJLFwm8eQxTo3/0QZKD7wCkavAY+lsz+jY1fzOlPCfP0O3/Cvyr6JE+8ktXIQw2MfrbKIQQmPHbUc0hXv3OP2X/z/kbv7mVn7lZ7Pa1zKdmZ/OnYjEtVS5MAgJFidA3+ii+W8Eq3iBb+C6yaqAZ/cSSD+FUHKzCFIWVGYxIAiPWR6J/H3bFIrf4JuHiZYxogljiCP2jH8Su2BSz0xQyVwl8u0MzGu7CWedC1u3Y9m1scxN/zsEHfg1THUVW5kkNH2Vl/nvNN3vUmlJzDPUPzZGnqB1SO65cLOza08J3TQFgVQXmbdsfS6Wq69qDwMWxlnDdbM3ohxBCoVKap1xcIAwtzPhtmNERfE9QLsxRLi0gyT5mfD+R2G34tkspO0EpewURLhGND9I/+hCR+EFCwLYytJvGtcPOJhTr54dNKmD2UVi6CgQto782O9hg9AsBkb57kJUI//SV8xcvjJef2VFTO2BXCQC1vMC7tMl8vjJm6DKqVjtlCL5n4VQyhIGDGbsdMzZKGPiU89epWCtIsiCSOIBhDuHZDsXcJE5lBVlViCQOoJvDOOUCpdwEVnEKVSkRS+5hYM/DmPEDuHYe16n+abitG3oTR7QpUlh5lT13nkCtxQKEPpVSuir3UqOBq36+re+vvddjt1Mu7V4OAG4BAaBKgg++xzi3vGyllpdKB4MgMFRVRlaqU50wDPCcHE5lBSSZaPIwuhbF8xyK2Qk8t4SkaERTR9C0WDVeWLmG5xVR9Rix1BFUNUq5WCC/fAm3MoeqOiQHjzCw52F0cxjPLVWnlU3Yxvhvc0jrLs/JM3zgo+BfRYv0UcxcY3VGsMnRLyRBbPBhMulZLl/69hcvjJef33pjt/Vzdh9nzwx9HBiLx9WxVF+E/n5zNQlSTbFWU6+KmsCIDOE7ZUr5aVy3jKJFULUURmQIzy5Qyk/jBza6GUfTE5jRUVy7gFVawrWXMGP9GNFB9OghQqLklqfIZ67h2oUttXmrqfz3fPTrmOoriDBNduESufSlLY1+IaD/wBiXL32b8b985kd+6cxSXQE2E45sGrdEAVpxYbz88oXx8jMfeMw4l8/Zk4vp4tGK5aUUVULTlNXbowPfxrYyOE4ePTpKNL4fSQisYppycRY/sDGje4nE94EfYJXSlArzhLjoZpJE6hBhIFPO36CYnSD0F4hEdAb2PEBq8F5CBI6dJwi81bZtbZLQ+UvXzjK4/8eRwik0M1VTgXDTo18zBldzADNT1z797HOrOYB1YeNO8JYoQDucPTP0IHBKVaWxwaFIKpUy0DSFIAyq2bHaxR8kGcMcQZYFVilLpbQAkoyqRTEiI8iSwK1kKJcWkRUV3Yyhm8PIksCxstjWIsgKZjSJEbsdzdiDVa5QyE6TW56o5hhg6/ngNnhs7K+JmhNILJNLXyK/+OqmR78W2UNs6F089+VnOP7Tf10fqBuN/m0pw9uGAI1YcxHaWDJlkEpV78hdvSoXVK/HybKJERmC0KNcmMOxc8hqBFWLY0ZHEKGNVVzEtpfRjASaHsOIDCHCAKu4gG1nUPUoZjSJFtmLoo1ilS1yyxPkM2/u6DcIYPTwj3PPu34VTbxI4DvMXfkyhO5NR78QAjN5N2byKF9+5g+zP/aJb47SfI2TNp8bsWkyvC0JUMfZM0MHgDFJ4lQioR/s7zcwTYWg5ZIwYYhm9GMYSVynQjk/gR+EKJqJZqSIRIfwnSJWcRrP99GMGJoRxzAHIahQzM3hOivo0X7MaB+auRdZ7ccqWSzOvoxVWurYxo07UPDOj/w5ifgKirRAfvEVCsuv3nT0CyGI9N2PHj/CM//7t77+M//hlQ/TfgEcN9nf+LqN9r+N0Ogikkk9lUxqqKq0bn0AhJixfaiKgmMtYZUWCYWCqpmY0WFUNYJnZ6hYaYJQoBkxdD2BYfbhu0WK+Tk8r4wZ68OIDqKZexBSimJ+mczC61TWkWHjJMPIgQ9y96O/RlT/fwS+w8LEXzWrQJvRLwmIDb8HRR/k9//bf/ryp3596d/ScQUkbPBdp+9X8ZYEgdvBhfHywoXx8hfP/33pN3/0XdrkyopNqeQdhRBVlWpRepUErp2nYmUJkIjED2CYKQK3QrkwjV3JEwJG7DZMs5/AtSgXF7DKGYIwwIgMEk/uIwwE5dwNCrlpQm8RTQsYGLmL1PC9yIqB61j4XoelXw2cKGavMrT//aiagqI4EAY41mLn0V9zC3rsCKVimRe+8fXzf/MN61tUF+902uSbfN9YptpRNfyzIUAj6rOIDz1unisWvcls1j7qOEFKlgWKItVWBoUEvotTyVKp5JAUg1jyEKpm4laWKRfTOHYJZJ1o4nYMPYJbKVHKT+M4VpUMsT1EowP4ToVyfpZSYRaCDGbEYGjvffQN300YCpxKnmD1D16th+cUSA49gaEvo+pJrPybiNX1AmuGlxpUwex7kEx6lpdeePHv/v556yI3N/JmSFD/+zqr+GfjAm6GuotQFDEWjyupWExBkUVtuVh99We1g83YCLoeIXDzlIvpWrxgoBlJItEBAreEVZjB811UI46imZhmPyKoULGy2DVCRRP9aOYImjFKuVQmv3KD7NJUwzWJte596L2/T/+gj6EXKGZepZR5vXn0N8wMJFkjsedDXL70bX7vdz77s5/+TO6bbE7uO5XxgQJNf8W6in+WCtAOdRfxxX8o/eb73m1M5vMeFcs/CqAoNUOEIUEY4jolKtYKrudhxvZiRuJIhFjFNFZpGdd3MKKjxBJ7EKFHpZSmXM7iBwGyahJL3o6hq1SKKxRWJqiUF1DkErHkAAOjdxNJ3AZApby2jM918sQH3k3ELKJoSSqFNxGELenh6ntF70eL7GfqyiuMf+31z7z8hpNh86O8tZwD5OkQDH7fKEA7NM4iTFM+GI1I6PraX+ds7HhFjRCNDSJLPhUri1VaQlYjKFqESGQQVZWwy0tY5QxIKroZR9WimGaCwC1Sys/gej5GrK865TSHkZUU5WKZ9OwlrGKGB574LYZGFCIRm9LKa1jZ19vmBVRzD5H+R+s5gPvYfvCXAYob9dH3NQEaUXcRssxYNCKnIqZAlsXqZddGA2h6nGi8n9B3sIrTOE6ArBloWgwzOohMCaucw7ZWkLU4qm5imH1oqoprFyjnr4NsoplJVD2KHtkDIo6sHyA59CiDg8sEvkN25m8h9FoWiwj0+F3o8bu58Pmzsz/xyX/qNAXciAQuMEftL09vhFv/tKO3CLW7aj4BfOLsmaGP5wuMaZoYi5gCw5CQJVaJULHy2JUCQkA0sZ9IQiXwChTz81ilZRQtgqbHSQ7vAy9HqZBmpbCEZiaRFY1o31FkKcSxMpQykxSzC5jRJIo+Qd/wMRw3iqaCkbiDSv71dcnd+t1AmaWVOUBlaz5/BVigjb9vh++bGGArqM8ifuxfRs5V7HCyWAqOen6YIgyRZRruIQDHLlCxcriuRzR5gGg0CUGFcnEeq1zAcX2MyAjxxAgSPnZpifhcYakAAAPeSURBVFIxi+M5IGmY8b1ETQPHymEVlwEbxTiCGfGQtSROcbI6I2i4UqjH7kSSI/zNF//i4leet77B5vw+wCywyBYygT8wCtAOtb+/+2ng02fPDD1oWeEpWQ7GDF2kolEJpcFFBIFDLjO16iKSA/ciYVMqzJHPTFBSosiqTiR+EFWRcO2lan6htIKmR1G0GLHIXnJL38FMPoRhJIlEVfT4EezCGw2tEiAUltOzFMvBAms22kgFSsAEYG21D36gCdCIVhdRKvtjisJYNNLBRVh5kATR6CCpRITAK2AV06wsZVHUCKpqEk3diSI5ONYypewSlhJF1aMsz3wFWfkAkWgcI34Ez7pBGFi1OABkNYlTWWIlHyyy3gXQ8nmBqvE7JyI2wA+kC7gZ6i7iI09Ez9k2k6VSeNTz1lxEWL9XIQTXKWGVcjiOhx7dQzzRhyQ8KuXlaqDoOCBHSaRuQ9c0HGsZ21pB0YbRzQS6aSAkFa8yX00DKyZa9DBTV17h2b/+3pdem3AXaT/N84GrwHV2sEagpwAbYJ2LqISn5II/pusiFY0IFKXBRYQOhewsBSFQVYNo8hCa4mOVsliFGaxyFllWMSOjSGFANv0iupkinjyIFtmPU7wMgYUkRwBw7AoTN9xl2geBeeB7VJM7O8IPzDSwm6hfrlZVxiKGQDcEslRf8ds8tdSNGPF4CkKHcnEB23GQ5AiSJNO353EO3n2M1EA/rjWNnXsZxbwNI3mMv/vC07z/58c/ynrjXwcuUU3r7hg9BdgGTp5efBp4+uyZoQM5NxwTxfCUrnPQNAS6Vl/WXV32XSnnqVgFhBBEYwOkYgahX6KQWyC/dIkbqkks8RCqWVWBugIUS3ar/3eAi1QJ0DX0CLADtLqISiU8JUvhmGGQMk2BIlcFtj69K+SXKBYEsqwQjR+iVJinlJ9j4UaKfYfuQYvdtVr39NT0Emv2WQaeZxceEdMjQJewfhYRjqlqOGYaYOj1O39DRH1KuTKDEILCylXmrhsM7zuIau4nDFwAlpdLdf9/BXiBanav6+j6AyJ6qLqIk6cXf8p1OZgvcGpxOZzM5gJsO6g9DCMg8Kubba1QLqSZnazmAoSkMnnlEktZ/03gq8A32CXjQy8IvGVYvRYhUXURBsg1F6FoMeKpAzz+4V9g7vo1vvKlz1x88eWVD/zO5/Lp3W5XjwBvARpnEaYBugZmdJj40F288tI/ngdOnDy9mLsVbekR4C1E/XJ1bQM4V5th9NBDDz300EMPPfTQQw899NBDDz300EMPPfTQQw899NBDDz300EMPPfTQQw899NBDDz1sCf8f8xls1NFsOQgAAAAASUVORK5CYII=0.4380.4384.85266.36090.2190.219-0.0#000000#0000001.00.010.0125#0000001040.0031251100.00.2190.4380.2190.2190.00.2190.4380.4380.4380.2190.2190.2190.2190.0111111iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAPgUlEQVR4XuWaW4xmx1W2n1X7+74+zYzHM/bYZowJzsjBcWY8B8+h52A7vooEImgGIRIHwQVIKBKWcscFuUAgcRtAwAURigQkQtANEpGChGR8nrEdx+P2iYzj+Ld/Oz7EnrHn1P31t3e99Hxd2ktV+obu6bFAFkvaXV2nXd96ax2rtknif5Ie+MuneOcnH4XT712obt6yodepwvg3//DeD3Di+VfeDD973WRsIh8bbd68iVF0WQAe/OFZ7xBgfCwkAYimEZ1OOAGclPh3M2aREIYQJhBOqQ8QAhBeej+GaHETSHDg5ksAXMso6nB54uYtExkG+ILeXrT5j/E+KPshRlE3zYHFQTywsBB/V+KYYBbAZAjhvAuVi5e7Y2ACjej33V81AE4XB/JlBNjlBQKBLOvzseUkg1BVdGXUVUPd2AxwDDSrTFpAmO89ZGAIA3mPDHD0EU5mloEKEFaQ1Xz3rewuNiRnvpyC5KWZEQyqYPR6FZEGYAZxtB1rPlEkhkVbCgMKaZPa8REDcXkaDYCjrpIJuSSY+f9SoRJZPT3mg828w4JRdSrGexWM9wBmBEcz8A3KH0MLhPdF1K4lOeJrAgCp0Pm8lEa0uRp4vZggM+R6TUyAVCFQxYYwOQYwAxxtxVggEoNSKx1CkAFULrxWAEpmCh2WlQp+uUXlY7AWGUWQJcOFQZIoRdEJUCUQJI5K1i5hyvbHgRRIahtVjFs7AFagkQoTOPJyUJR22OUeB893TWleFESJGEXTRIiRYEY3QGdyWR0MHVUC8h8e+jAkN1nohy+Q1ihN5hq8QIGmcFEwjJdefpU6RqpQYQ6AE0M3x53bb6OuI6dOvcYo2rbtUwwGNa+/8RYbN25gYmqcBqPbMVg/xuDcwgyyYxKzv3bPxvivz30UfmnHhjhqlyXfGYmrA0Cjpprv6u23f5pRVOqGBFUV0nhlniAqEhsRI9x0443UdUO3AmugFnSDYevH6Z9dmBE6Bjb7i0vM/+Of/ks49sAXI4B7FRUiL7gaL4DKavlyUPF4zUa/SpbNTiAgoImiHjRDprsVdEPaoWCMbxhPNkFHEfzqA78S/+kbzwVfDiSPMoUQoLV7gdK1CROYCoZV+npDMu8vvIWsHYXS+5BldiAYCYRlW2BAFYypjROZd7iw4YxJ0D6ojFPcWqxJBVpUBRiyvHPu+VOUtGPHbQDMzZ1iLTQ53qOyzTRA1wAMQ9QRMGPdtROcPzM/VIff/K17Z7/9nR/2vvSlzywiMpWTJeYLwzQ5+cssLj5PXb+2cjL0by+c0c/dtI4LA2EGEu7NcGmwwhtg5lvu81Ab0EJEGBCbyODS069ZmK/p9xf57LbrqBtRCwDqCHXUsGzShpw9fRGJY8DsX/zdi92v3n/HwCNGXBUMTPDFPdcBcOut8MEHX+Ps2W+sGApnVjRz5cJjfgNH3V2efKwDhuUWIgosYGbDhwCNIi7+lmxAUokAlYGZcc3mqVYdvnr/Zwd/9r2zQZ4Fgo1O3GIEaXClNsBAZMgCHo8XE+R9hTHOG2QBEjCk2CHIChtgyQawXK+gk5jbeN1UGzb/3hfWR2dYnjqXtpwG0JVlg2WwZ6k8efKlodWuOhVXQgZEibqu2XvXdiTxyo9e4+KFBSbHx6hsS2EDoI6CBIwhLMIgLoNw+qfnZzAdQzb7R9/+2/D1L38lCjKXu+ZASBJZZiahJPe7dn62bTdSX1uBvLPcCw+qLMBtt/088/M1cXGwzGRUigPAMMwSCCQQDEjGcdP165ZAODcjcezr939l9vf//pvhT778O1ESwNrjgAiAkUuui1UUnqJCYl6gYmwLpgGW+esoMAKSEXAx74ZRNsCSDRgC07rJwCUQ1iebwK9fYv6VN09WaXVkWhMATuamUFKWfhrmDBqZ3kl5fpCGlW7WsaqMyfEuwdpAaIQNsGQDcBAqCAabtqxD4jtIR7dtvbNxm2VrVIGYmJJBKv2UwHj66TlWS7t330FVVSvO2bntRirbQMOQsbSeFTaAZANEJ6kICBtKwhRLh60ziGMyzborWgMAEXyHJAzLDknu2rujzISx4kzMbYaKObkaNHXDYr9mvq55aO5tzpw5x6AWdROJTcTMqKpACIFOFRgb6zAxOcbkxBhTUz3GlkqrwAhs3DzJmfcvDhMo0OzaJcAJ4UyYDMwDDloGLU+hi5RVJlcHASaIaa6BBWPQiMXGCN0xLNYERWQRNQwBgUifyIX5Gj6cB4MmRszg+s3r2XLDRiamJqg3jnH2w/4M2DHE7NIJN/d9ZsOVASAJZdtriWGBDCE8RDSszMJw5pADZKIkLAU4VgWqXoduFCJQ1c2QwVgJtVKo7CyiipEmNrz77ke89dZpxid63HzL9UytG+PC+XpGxp4Xn3n9ufs+s72p5wNmqwagCGdTRQmVRx95Cic4ML2Lbre31P4k/x0duXs/TdPwxOPf5+OiTRu3UIUO1WDAwmLNK6feYnyiy9at11PX1R//wp4bfuO7z7w9P37DTRd/+nJY3cXIPz/zvm695RrmB8K9oEAOofC55tuycpkotgISaZrIoI4sLgzo92sW+gNirUtgDR8BJvPQPABqjWDKKGuaWkOpuTi/wPqpcTZvvu7Vqh7bS/9M/8//euPFp2a+xrnBt5C0khcAZUwakos8AGqDktQvMAdDngUgLGM/1RBCMoxAIBKqik5HjAliNxKbaghOjEKSv9XMQTfBEICqjb8mpybodrvI7NP9D9/Wj+P6ePvWE+Gdbe/GF1+2FVXAgxxnErcFwnBjSMGaipMaYfg7DZm/iyjvMyMEI1SBECOmCrOYwm2BGPanGcsgeKZG0zTJnoAwqqpDf6HmhQ9+Up/vTqpz7QY6X9gTqhu2xVXZAIEzL0Oe9nLi+DMMBgM63Q4oN2h1EzHg0JG9zF9c4OmnTrJWuuNzd9BE8Z8vvXz5+GHnDiLQS+xYSMmV2dL6Nc1HoXl7bFGbOpP2M5+6U2femVhZAjyrk0eEcuu+f3o3BRW6DgLGJyY4cs90EVmag6qkBhFijNR1gyKEyojNcr1rgZ27dgIiBMMl0VeqLIAp5Q5GVByWQpw5N9Cm86ZaFTf1r2Fq8+ZVJkMIr4ILn/nfQp/xPiRKYBxZwFAWFxi0ZwMCsECvZwBEmc8Lltb02MIM1EQwaBQxkvWJEJCZumZqsIU+22+bWkUyJCCL/sozgfwAxKFSOy/LGDIwAHes3j5kxH11UnoIhg2fQKgMc1yX+0yJmwAEglWEEJIqaNghA7OGoIo3+u+tLhKMgHyn8bjGOPG4xwH7Du6hssDxx5/m46Cde3YSDJ79wXOshnbdtRMzIRkhgJKnwkCkOnXoqFIc6zUW65UBUPStlYsxshT4HN6bxf2SOHBon6fE5vf8Zpn7c3fp19vFNdey29u1dxeVAQYxetit6O9UzI/rJTCM2KiVAMOqdrQqG+/3AxBXdINq+bb8bFAgT4YdJOT8y4hplKfG8uNRgUyQwJCcqZAMmjzmoKrcjVIFVzVz20MUUYBElKgiy8kU1sNYrImRhUaDix82qwuFETEmps0w1LYDI2J7w329A5k4bBOp1O4SgAhmRBnBRJTa7wdEMnBpvgVzGwUpfFJCDoIAgRq1TqshNrEhRkU6FVh3lbmAhItwcT54/NGnkMRqyfX1TiYmxnn4wcfo9XoJMIYxxZH7DjFYHPD9J5/lSmjfob0eFUrILOFhRLEMUSRYqBpVC3SDbCUAsqMvDzn9bn768D5ktCKO5elv6fOtkJK77zucgLU0TAjo9npMH9mPmRFjDruZyjglBZMGFrEIBE/Tlaa35iyKvoxw7c1XEAhlip/7XqDVc5Snv7nrSEC2wJTXZiIntwkQnNPyggJDkjvUNNRIeUNl6cygamR1E+u++gOjd+MtWsW9gFBbmh9qeL/re5Yn5NfTKlIfEFImDWme5V+j+OOeorylNWFJCpXWMFIsYYEQAmBYx5rAYtPp9WNTfxSn3j+3sgTQio1BJUT+A088+iRXQzt2b2dyapLjj5wYJjGhCiseYS+NG2Z4u/fvptfr8uiDjw2ZPHTvwTY+wdSqrAUwoH9hfmC9ismuNeu3Vrz62vxqjKBaEXfj4q7vwJEDgPDuQkoh9xRWpMsCDd8zjZEmI9c0PN+X2xI3LYIjnz/s80jrx7SACQhI0B+sbyZ77xDP9nj7R6f1wlwT4a6VVCD/mClB7GKp9BhIhqu8qwGpzG5X23l+aoCBxx1FqGuJOQnyq3b/TQLJ8DmpTgALVHFSZ6uu/v//u0YvfPe9+N7J11dzL5BeHMl1U77NwkC50XRvJP8WCOX95UcWcosthORcRgA5ww5ummeeosU0SDL8BCtSnT7Fc2yPz765N54/dz3j61itF5CjLdqdlMGJh59Y1t0QKCnG5UOM6SMHmb94kbkfzA39vI/1cYc/fwQQj//HYwB+W7xKEuLgPYeKr0lzaf3W39wfAfgDgN++slNhSS7K8ujtwN3TAKM0Htw7MD45wb7D+8moiAgBDi3ps78iH2uWxgnX+SzXVNqYMpLlqj6R8U9N5boWszNAc1Vx7CnzZGEI11NZPtYrVjLR5gRidMwgv4xBI14prfl2OF9OHgy6qpuKI3PlSRPlR5VCuEGUCiESTuabLS+hvKjxCBWzgnEZYiRdWSQonGJi7vgjj6GoK9LX0sUevPcwGDzh+t/2fW7XDmLT8NLci23f5W3AYUhznUQ0rf1uUMW9nsgj0um7DwPKJSOVyrJoc1eXZY9en14CwhhtAqYv2QYlACQYfcqW1sg9lgkkWyMAvlPIV2yZEEqMe1xurevDAXMXmDFZkkb/j6NaqJaywQ562Y+u4kPJURLkCLvfxsuck7Ka/DpXaK1l5bcHsAKIIZiPXbME+KKU5uTEw49nOrfn4D663d5S+2OMJhf14w/5mKsjf6+UJ4pOa7AB5cT+Qs34WMdbDPbfcwjAdR+Q/xiw4grAs2QOLI1BgLna4IkM7vJx1Rt91YhB3gc0jYjRA6E1q4BwGtQRLF/IVFyDleLvkpTmlp5OXje51/GxWS5ijv9odTSo6+iSuQoEwmqMIDiy3mdElBuizLe7WDiaXsVAbeltpf2R1x3s8mDFlp/YyF2p83EVKmBeixIhQgMoqgQHjQ6MR8c47teL9svXbYW1sNFGVWuPA1xeYxREoWCYDEUN2z4JZFyNFxBUnUBTxwyITwJVnYC4ikAIicmeLT0dPnm0RjdoZjcCPWDrX33vdebm3uETTc7XNPAWsCgpY8okjZpwC3ArHz8FIP4vzP2xpDdwygH4v0z/BSjKoeHVWVh6AAAAAElFTkSuQmCC3.62940.34634.77844.06911.81470.1732-0.03.62940.34631.81470.17321.81470.1732-0.0#000000#0000001.00.010.0125#0000001040.00312510#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.173153.62940.173151.81470.01.81470.34633.62940.34631.81470.173151.81470.173150.0#000000#0000001.00.010.00625#ffffff1000.0031251111110001.0E-40.31530.03140.34650.01.00.01.0NURBS(1.0,3,0,0,0.0,0.9593,0.0,1.0,0.0038,1.0006,0.0,1.0)3.59820.34653.62950.31530.01.00.01.0NURBS(1.0,3,0,0,0.9962,1.0006,0.0,1.0,1.0,0.9593,0.0,1.0)3.62950.03153.59823.0E-40.01.00.01.0NURBS(1.0,3,0,0,1.0,0.0404,0.0,1.0,0.9962,9.0E-4,0.0,1.0)0.03143.0E-41.0E-40.03150.01.00.01.0NURBS(1.0,3,0,0,0.0038,9.0E-4,0.0,1.0,0.0,0.0404,0.0,1.0)1.0E-40.31530an application entry point. The file name is fixed. 0.96610.96611.10819.51690.48310.4831-0.0#000000#0000001.00.010.0125#0000001040.0031251100.00.483050.96610.483050.483050.00.483050.96610.96610.96610.483050.483050.483050.483050.0111111iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAQbZJREFUeNrtvWm0JVd1JvjtExH33jdlvpyUqZSERjRZAwUWCIMtgTGuFghbLlAhBgnKuLDLpso2NDaYqja97HK3Vxl3F7VsY+MuELSFxSAshjJgMRuQEBIIAZLQkMpUZr4c33yniDi7f5xpnxPxXqZm1uq6uV7ee+OeOHHi7G/v/e19hiBmxv98/f/3pZ7uBvzP19P7yp/uBhz3a+F3ZjFZ7YDKTqiJzyTmExSyrZpUFxpboYhArrD7oMRn9/14Xtq+s/3M5qP7TQMAM6APK1IjrevDTHQwI3oAuj6Ifj6H2T9feLq77Hhe9JPpAt6tRssHnpnnuIgU/ZxS6mIA21jRdlJqEyiDEawTKK3znn6m5FoKXsiAfU8/p33kgJGU1zW4rucJOADoQ1rje6z5q5nCXehu/zHwv2n8hL1+ggBw9QSGmy7VpF5GjJ+ForOoU2y2zTRvThlZHIMCyH6mRMOj720AYPFbKmgtvvqLmncS3915zCAwoCgxPAyMq6N1zfeT0l9TJT6Dhflv4eQbB093jwM/AQAYr/7aRXmRvYJq/lek6GJ0CwIYqAGuGZGmK/dfKiy2IHBAsP/RehYB8fnROyEAwAkYiFwBs63KtpFJlBfvrEEZWWAwMKiYwd9jwserenRzZ+qDdz2d/f+0AOCjH706u+qK6ZfkSv07VtmLqaumoQGUDGaycnLaWwNcA1Sbz9AIJttqnBScFDrZ3+hYPCABE7MRKIAGB4iAQOIcAljBuCf7574z2fsAiBgoyPw8rFZI8xcr5r+46bMr//SqV91YP9WyeGoB8J03F/XZ43+pFL0Fmfp56mQKJYO17UxyHV7Zv9K+c/idpN8nY46lqZeWAAognRwD2q0AEJt6cawVAAlXYG5aDCYLiALgHKAcBhwAuDbN6ijwuNbQfIvW1Xuz+374j3jO7eVTJZKnDADVyrUvzpR6B0i9hDo5MNZW2525LQGMzDvV8BpMwncTmsIkSjScggvwb+L89V6pmfdmXPSR5ALp79ExHbsLwFqCAqAuQIWxENDGKnQz8KgCWP9Tzfwn+fT1X3wq5PLkA2D+umegw/8JWfY6FKqLkQazsryrAngEYAygTvy4Ei5bAgChjBeqEsJNAICkjugnKVj3g9Rk91tqFVLhOq23YHbHo/N8+Gj/MgAdC4bMuAcFoABQ8wil/jAq+t+x6YO7n0zxPLkAWHrt6znL/pi62SkYW1NPZAU/AHgMf+epdkvt9/IWv62n4Q2Tj6Ssa4f9yv5DLPy0b7jtuBacVJp/aQ1sOZ2eY8uqDqAmAOQmmlCwrqHarcvqXdnsDR96skT05ABg4apZTb3/rDqdN4OguGQY8lUCegBwCSYGRQKWJh/xO6Ua32bmEyvQZvIbmCAhINEPxEKo4sVCwABiS2C/u3LOGvjfdfM8byUcAAsDBCoMEAoCWGtdVu9TPHwnZm9aeKJF9cQD4PCrn81d9V7q5j+DsWX1rI3G6yGCxrcIM7IC4nubCY+0nLC2C1jD98vgofHiNYQv3iVh9MfTcDGxBg4kyW9G/AxoDQaBqGeBoAw/MNbgGzTSb8HWj9zxRIrrCQVAPf/qV6tMvZe62VYeuQ4bAXXf3Hhk1i2Dd2EZJZbAHYt8v9DyBhlMzg0HY3PvhLduJJB89wKG+C1JDklz3wBD4gqk+Zf1yz/KgGzCcAQA1CXwqD6sy+ot2daPfuSJktkTNhikj/7rt2UFfZAy2soDDXANrlaActnE8WknN5Iva/2WxujuY5KNS49HPpmTz22XlufJwxxnfdN2Ha/+NCIMaYLilDODwFwB1TK4WgZQgwc1KKOtWSf/oD78qrc9UXJ7AizA1Zk+Qn9IXfUu0gzWCuAKXK8AXIEkmyeh7cc0+baTIl4ABIsBNDN9sr40AZSUi46lZE9+aAkLU8D5sYC0TItLkMe5paxmW0PgE4QMyKcA5CBik1+q9B9hM/0h8PiSR4/PAnz06kwf1X+iuvQuqtkouh6AqyUQl0mHU7u2yI6JOpeS766IDp+jfHwitNbrtTK7uL7UWrTVkSaFGveRaHZajqnZdgYYLGyA7zWwLsHjJaAegDVAmoGOepc+rP8PfPTq7PGI8HEAgEi/WL9bdbL/FZUGaw2uV8FjZ/KF8FvDojXi7RQQqR9NBbmmL9WBeTdMt0gne5+r4/rXvJ5oqwQjkoE+r+VJnek9+7osJRI9R67fyBBpLlfB1SpYM1AyVJfepi+v3t1i6o779ZgBoA//yu9Srv7ApHIZqPqgqh/119pEew3Nku/c0un+cxsjdwXSq1phe4GkAzfOfIsRwYY2J/yhrT28ngzaeEyLJWIbDXg7IF2LbV/VB6o+WGugYlCh/kAf/KW3PlY5PiYOUB/4pVerIr+eSBWsGVz3gXpk4noyt0DenyfhmGrJ20ehHsL3tlCvjQ/47wl/aL1jmQA6lrDcoZQ4JhaoUSaNANYjryyKcQAAAxxlExnEbL9qQHVB+SRIEZi51FV9bbb9Hx51dPCoAVDOXfncvFN8loi2cA0j/GpoRuUEySMpKHvcpP7TDF9K8lJBr5P7bx0DANYnf+uFgBIAMjMogdAWDooDUuASLLqlDs3R7yyAZcy/9sCIrI9zP1kPlE2CcoA1H6kG1RXFyZ+67dHI89G5gLkrtmVK/Q3ltIUrDa5WgXIYtMreHFlIs+xI1+3MBtnS/0Yja4gUbF0ZHSsU9GXbyCLWOQbEPtxyilbht1mDRPhsJZqOIFJcn0xTOEJo4J9mLK3LKYfgsg8uNShXW7KC/gZzV2x7kgDwbqWR/6mayC/ikQZXA6Ac+BsncScsGsrOnAkBkWT6guw3yVwqZIRObJA0AQL/J7WRY8FJTY8AmQjY8YfoUi1tSClDC+FlwfMDNuJ2xQTQNakNwLZA3QdXQ/CohppQF2mt/hR493HL9bgL1vu/81oq6DoMa6AeB+Gn/ZK8KD3WxowbISAHwUS+tAUEkRVJLtAQeAtAUtPaWieStqClPRrJzbVYBKfNVjmi4tpXT96SinuPbk8oEACUfaAaAyOG6qjr6r23vfZ45Xp8HGD/LzyD1cTXqVCn8LgEj1fhU7sgOysHIpEj07skeCDB5LodaVbRb3GKWHxH+NrAbBsfiBJE6/l86e/lMYrf1xot9EJOfb4UfkICAYTwNNTHTuAik2lmnQkeAHGOq1c7F0NAZwqqU4DHes8Iwxf2TvzCMYeSj8sCaO78IfXUKVzW4HEf4BohlBL94UKhRmO9cwjT6Fi6AllHYoYbZlbO4JUdLLW7xbKsSSyo5bdm+9uTRG1uow0EMifSvGcP0cQYNaxEYyKqFFINjPvgcQ3qqVM6uvjD45HtMS1Atfd/+fmsyD9LoI4eD4BqgChF65i7CszeaLjBFgnWzra8JzUuQojIfFuaGGiNFFrH/NMBISFoyTfS09sEGHU6kmNthA9CMLGrYHseRQBwrkPKVSOEgK4eCUhxXIu2WHJN+QRUtweGHtcVX5Gf9D9uWU++61uA+6/oZip7FxXU4bI0jN9rOVreBQm0xNB8ZN8BrZMsWEpEkjNxgVZC10KOUs1sHFtDi7HW8ba2JlwiOk9WFdpHsMK3302fBHCwEH6ErNZJKCnoYC0qbGRQggrVyYD/CFzRfcwAqCfqKynny3hUg8v+GmY1EaIIBTm5aa/PDeFx+F+i3guxJf/fJuiGoFIGnwCojRA2ND4BSXpuIwUszklkGX437q+R8vXNDNeIbFljlDPpe5iUsS4H4JEG5fRz9e76yscGgF0v6inQ70IR8XgI1GKiats4uDyetDHVHPbERipf6JCG5nNyrTQKQFx/3AZXTqMNeK3coOHfk7p9PTpuowRMS10ctVkUt4rCHq/CUqT9Kq1PFEKLeqsSPB4BSpFS9LvY9aLeowZAnRUvpZwu5VEFVKM1blS2NdaM0PC4M8L9mQ4kW4xS/9YI0dx5cgCmrQNSk464IK/3p5NzRRu0uE6kgaJeLYXLYG7LIQAuH8BSkTxFkfWKUcMGIU3bk9xpOQKPSlCOS+useOmjBMDVmSJ6MzIiLodA7YacOXrzDVvLLUTviNBM/qPU/KTTYYXSuLZYrNGmbWtGBSmaWgQtz4suzYhi/ai6VAgcm28PjrTzjLl3SUKOMoMtfRl1fqIg4lrEAOoaXI6AjEhp/DrQPmzcCoDy4fmLoOhyjGqgHEc3zI2LUuPeG+nZyISaz+w6qCEwWf9aYWWSPmwxq0HwMoUrhoflXyOHI4HUluCRoGqbFcyRH3cnUUOoOuJITX+egnuN9YprfS9HwKgCMlyGhw9cdNwAyIivoY6a1OMRuK4j5MrRqmZGTt5I2qGCAGpLEhOlCP3qQJJagvR60twjblOrwBwI2s5vuY+UuXmwSAsUC4BZgNsLLlwjNJdB2vam09rokpL4xs1oRDjJPZrrM7iuoUdjUIcmK52/pk3WzTzA/Vds4KK8lTrqXL0sJneoJO5PZ/XKuD2N7+1vZIeKAftZnK9AQE5hdS1k3UkugCGyjeIakjP7aQBN/0iFMgs1S9gdEsiuS5S9HH+Eu6Rb+dso26K9NQO1blpNlmBIrBsAYm0Jo7QCOuYha2YGBRg1AypDNjMNrvQ9VK4+D2d9a0neU3ODiM7guciKs3k0BuvKCEu5xjlBuAaooNYsfzPhSDT2b27Z9iJHfaUKBV0yDuwbYd9cif4wlncqbIp+lBIi/7Z9e45tW7vYMJub5lUWTwXhnh+s4LP/NI8HHxxi27YCL3jeBrzkRbMRLzH3Y+GaE1Br7H1kiL37SozHHLXF+3EYzdNM6BbAtq0K27Z0MT2tzIqfksG1yfmTFFqk5Y4orzFI1UI3Il5CiLY7oLoGj0qgU5xd6d7zcuAL6wKg0tmVeRdKr45MRQQwk5mMCG2zeElq0ws+EQzbFpEoJ3wB2XGBL35pCX/5gSO498ER+n2NMp1OGLByXLOfCMDUlMLUZIaXXD6F33zTDuw8uQco4PZvL+O6X38Qcwcq5Dmh1oy//u9H8I637sBvvflEsxbV9yyDMsJdd63gz947hzu/38dqXwdOvNaLgSwDJiYIU5MZLjqvixf97DRe/MJp7DixAJUMraXplv3VsPeJtBN+5UCT8hAHR67B4xGyyUKpUr0cCQBiF3D/FV1djL6pcvoXemnJmC5SYuJuSwqYhAuIZvS0ze6BN/1EBCoI//iFJbz57XMYjBhFTi1rPWOBH+/kN80Aa8ZwxLjsBZP48N+ciW5H4Zo3PoAvf2MFE71gnaoamJki3HzDmTj7nEmYlUwAZYR9+0d45bUP4P4Hx+h0jOs6nhl4jgswmyBKM+PUkwr8m2tmce3VmzA1paDLFlKXgoGtWSfhAoTVYEsMo7kWkcUwWpzNzoArupNO6z4f+OzIXTW20dnKOYrYmn9tVl4LdLHzqSz8ZUT4pK9LkGqBxp6oMLhi/N1NSxiOgYmeQp4T8lwhy8SfouhPHedfnhGKQmF6KsO37xji9u+sYv/cCHf9cIiJXmYFaf6KnDC/qPG1b6z61dsAAwXw95+Yx/0PlZiczGzbju/6WaaQ5wpFodDrKUxOZNh/sMa733MYb/wPe/HQQ0OoHLJjIaQK789dX8pgxuMkhNAsIw+vKO4EDR6WAPjs8UMr50iRRwCoa3UxCjWlx2MBskBYKDIvbaxfhGfSgrXm5gFdMXY9UqNTKChl/qRgHsufq8f/ZQpECoeOMHbvLjEat5RRCooU9uwfA5URBgHAuMaPHxwiy7JG+cfStqJQ6HUzfO22Id7w23N4aNcYKpcuE2haADT7DnGfxhPjDH8JyTXrncdjUAdTWa0uXhMAIHoBdG1sYirAxFLF8XoaViWNDnbR3yQByHLCeeduRFXj0Ql1nb/Wju/kOOXkaQxGyrq0lmtkCit9ZVap2/sZD4Gj82Rd0xPTJqUIEz2F+3dVeOefHEZ/tbIbmUiFSgeFEPUtcaBcnCiY/B6JsKoBrQHiF6wBAFIgfQFqbcaWG5mmOLlBDL+KRVqKOJmjW5CsQ10EvPHaM7F16wSGwxr1MYDw6K0BgTVw0s4pXHDBRtS1XrssAURFBNaqYiwtE7LsibFK8livp/C1b49ww00rZhORNMZ3/eUziMGoM1yYKJJpkdtNBs8YRvhVDQAXQmyp4qOA1R9efsLEBJ8Ea/4bCymZTSzvLbxZ3k1s5rJ4FDsy6D67xkdj8aZOPapw6bNqfOSGF+OD19+PH/7wCHbtWjWrXxLL2PbKFGE8rg2w1yRmGq977VmY3DQGa0KWZWYkLinvhAxLHolMlJLnGZTKkGXtF0hJKzNQljWqilEUWRoJR6+iAD7yqT5e9bJJzEwpE7dHYairk0XqIQkJRb7ahZbsyF8UQDAwrkB5Z+fqDy8/Yep8zEUAoHJ0IneKHRjXEXJIKRESO2G7kE/6oCTca2zAAAEO23Ai6OVD+KmdNf70j8/Bar+D5dUMYeuYZNq3CEGVyvHd7+7DW9/6VZQlQ6mkw4gwGtV49nN24DWv3AwMHoZLQMncUhCkALFwY2Ylu5jmbsuyvYd3vOM5eO5zdxrrAmA0LvHwwyv4+j/P4ctf2oPFxTHynCIv6l6dgvDQIxrfumOMl76oZzZLSVPDoguda3IkOhRhMYnUnS+msDkwjCuwKnYQ9IlAAoCs09madbin+878k0/4EGBm87Cdo+YSQMzmuBOLOE1AL7EEzqIEweqVo8DqPKaKDqaKPIApnfnjZJARluYZ73/ffozGQJGnVIagNWPzlgn8p3eej+liv5/CuL4FaG5BR+R8edMCkMpx5olLOO/0Ghg7kGd49k9N4qorz8Ud3zsNb33bbdi3r+8tSJqZHI2BHz1Y46WXyUs3SV8s/FAuxmzgCDEfMFXVlUZWoNep+AR3fQ8A1Z06GbwMrrXoaQJDw1EFv+sa/BRGn9M3KVISlsHkEPyNRNlDwS2EVdDl2Mxu9RdzIKDoY83AH/3ZAu64a4BeL0OregF4y29dgIvPWwEv90HdMEWt3QIo709JcJ/gw6khPKIM9WgFWO1Dj4WrtL3z7PN34N//1jPxjj/4vrUg7C2JqyvPCfc+YJZ5NcJoAQJXnrzgXZ4hsRjCOjSiB5McAdPUSQ0ADIfZicWENmTBze/ziGIxB5TEbBZr2sjGqcTiXHjeEEBAoVEuQeRuVroHEu+yHphU7t/f2MfNnx9jciJv9f3DkcZVv3w6Xv+qKfDKgwArfwmlVKsFUIo9B5B56sDm4xPYukfDp5ruiplBC/vxwou34IQTJnDkyKjVihQM7N2vUVccXKsHASJAhSjAhXchPG+OPopKnPXVRr5lSTsc3fUAKEtsR1csooyWY7mLByZKlveDKMziZpily26gh6w2eUtgC65hAQRpiADobpq6CnfcMcZ7PzBAp9NOsKqKcc45m/C2f38K1OAhaB0sFTmBtswbzTI2LsCGTybKAZSwAG0AaMxikmycCdPZEk7a2cGRI2Vre1WWAJ3T+swBNzgorRNDlHGAcOeulTvQjHFN2ydTADDUBmgdX9w4f7CElTdhohXKlaEgLFHW34Q7FMW4CS9wg0lSG0CgHDh6uMIf/ddVrKwaAhX4prmO1oypqRzv+v3zsG1mH3R/HCIee2trWQBH9kIa3d7RMQAQBCf6SPRfWdYY9GufQQzns22PgSazTtLccWbPCToasXQmPz4tygtEDWYGtEbNaoM7HKIATd3GrBVNYWte/zIcgSQ4mI2ZJY4tudsWjgKbhdsKNuowN17gwh95QTMaWVXAf3nfCPc9BHQ7gZNIdl7XGr/2a2fjec9aBi8uJZbHlD+2C9BB3SxgjmkBtBBIVCmwb67E/rkaRdGckMPM0AxMTxXwK4bYxPkhYRaGjZ39jQd+ELmDJhgTMJh6/EzhMBqYUR5WmYiTNYxf9+QjJIQjBffCg6/DyNUeV0mjZdjoz43GWH0Zygkfv2mMz35ZY3Ki2ZFEwHCocfnlO3Hd1RPA4oMmUkk7CcfLAZwR4OMDQOJ7CQBlDF0DN9xcY1SabGLbq64ZZ501gSyroGvr7wXTd+1gl29x1+I42ZMy/sgNyHcDVi93MRxcC7RYsqYoBoEHuWABLjy0RNEs/0r9FEDOmkCATBI+AH782Yd/DJUD37mzxl/eoO2YQbMTq4pxxpkb8Pu/sxPF8CHjyXxPSVCqdcJAhvIWILgkkwQyiSAZBTgAKAVAse0etu0B9u3X+H8/qfGPX2P0upk4L41YGOee2QWyEihjjYaVs+ly02Eux88u4ogmhIj+lX5fvjRHYA0A0EqgSrD3KE/dNM/mjYNt0NbyWj9O0p+7OlwOwdciyoiyKiccPMD407/RWB3QGn4f6E0Q3vbbp+PE2X3QK2XSTtmpa1uATFkSCJm+dqN77XkAgPCRT1X4yrcY2q7vJ2bs3Q/ctwtYXGEUufLBT/rSmrFxNsdFZwOodEieOubveZbLuAoX7S1BfI8xAZRRQFK2AQC/i7UQvidpAkk+McNxGWEVSItyZLeIVZYnOKh4JWchr5AvJgWMx8B/vZ7x4B5Ct+PmE4juJ0JVa7zhdc/AC7zfd/eR9LZt5vouAPHDQ7B2FOBet32XLXe2GssEpcyEENfmtN3uVVWMCy+YxLmnM/RAC04V+sa4A7eKSJC+aPAoAUR63Jd3LiBYJAGAPBT2AltD4xnBnCMkOLzlFHwhRZ9P77hT29SDCJQBH/sUcMs3Cb1uTPbcazxmPP/SrXjD1R3w4iPWLMpElmg2u8u5gZ+4jFLmphh1uGe2qWOlWgUIAJ1CXiTB3BoJKiePTi/Da35lGrmeMyskmCNdawhTnhyZeSkmNgKMFu+mBDGIXYSBnXAhy/BjRhgLPEQIgg/Yjgj5HhcxiASLjAOjJJG7UUDlwO3fU/jAP2QoOmH1ucAHqgo46eQO3vqWbegOHzRTrKR1iqxX6Kwsy0HQLRbAhGQxi2bBAdYX6LFe6bnDEeOaV2/FCy4YQs+XPtXulcQ3Q3srgNTvR2AQJ7WAg0Q51p0mADQmYhR5mi/YvfvNS7glm+fpghs7sOSQQ0LGCTwe9bODPDlw8BDw3g/nGI4IRdHiPxnodBR+89d34pSNe6FXxiHkCyMnoYN884xPb0sFK2W5iASAllHA8Qn2WC+tGeOS8fIrt+LXX1uA5x8J7B6pYNl/jAW8js9vA0cUOgIaLQCoMQ1wZnwEkXEBcIJbw6H66CAhXN7mku9IKOv7rWly+RkZMSgFjMeEv/z7Dh7al6PT5agagyvCeMz416/chhc/ZwW8sIQoWZGOhgVJmTAwU2ZCRQqADCAX7orT6BgAOF5QMBuXNTOT4XWv3IrX/7JC9+iD0JVojJftWoLEmsJnR8B9RU522qzD8FYkQ80TLQDgaZhHmoQQyPSAcAMpHYiyTSEiiPrd/kSO4IhMEXkzzbazgZs+38VXbi/Q67oTg78mMp148bNmcN0rFXhxzkcbcEknCQQ5oGQvrUiBlCF38pVp+FSwDKUCCWwXdl0jzPCNOscc1xrodgmzsxme89Mz+KVXzOD8U1bAc7tQlxUIyiqdmCrfiOeFUL2VQqThURoY8IrLOgwVm/7OUWOqBQDYCHAHka2IhC3Ts4JVRR1PotVxZ4RUMYsSNnRkQHUYt97VwfWf6SLPqbXDtQa2nVDgP/zGDKbKXdC1DjyiVfFFO6yQlE3eNEcDRVSiYxC52UXy5TT1vPMnsHlzDjcL2L2UAqanc8xuynHuOQXOO7vA1qkBsLgb9Z5Fcfem8Z6ktQk9mZnVSO4giMN9Zjf7ynFCf37XyLoJgCkA02A953PcXqRypo/0/0pcsYUFt/1m5KM9f2CYkOnAgQx/8dEpjEuFIg+dKdk6A/g3123GmVvmoBdHLuEQCUXkYxDnpC0HWMsFNMYCYN2SQpY11rhAaxP5vP7qaTz3kq6YzQO4gQdyD73qLwLLi9BHhp40+zBPyDDWdCdkYZFlckdaqkTwfusdhhg+ttrKk6jaSOBYTwD5LMzmhBb5Ih0bLINYpeMzbu7q5BL04Vg6rhMhwWTQRiPCX3xiBvsO5+gWJqkkNY4IGI0YL3/5Rrz0kkXw/ALQtqyRWtbluY51mFXrAcCuWhJJGKUUMtUEABl/AizsB/avgqvAgN25WrN9CISDnxNFbO6piR00nkvUyvQFcNZi/bIvWINpBnXdRgI5R0UbQVBhFMpPfXIIsg1zmifYvRNAm6TlaKKYyW6wooCPf2ka3/p+D91Ch4kZvqMJdc141rMm8BuvrUH9OfPgLf8AigRU7oM1fdBkLTpDwXIAanEBfpKTHQyy96YIDQ7gJ2e4ftDaPxIh0hUATMrSqGCyZTrdfVqT9HlQhMgksqoREFisDQxuKixDU6gxjZpbE0HAkDdhgroAjwFNZr26CogOMJDCT82/dMbiGNvIgkPSSGXAPbu6uOkrMygKIxwpePfKMsL27Rk+/akF1MMphLECRClr6f6ne4wtmzSesaPE9s0huUOKQNxc3ZMRzIQMuRqcjZCJuBGJ+MEgkT2MI1A3eBNG9qQaeIF5oQLRAE9ARnKydAOiDiF8N17gw3AbcxN1MKyno01tIgAMeAN6atLuCCIaR0qE/Bysvmf0Hu7CCXP4TlYDdHAnJplDuPGWjVgdZugUzdBJguCLXxziC3oCoEms9SKfCDK5/SIHJnsaP33+CL961aLX5jYXQHKgSgyIOQLYbgGcb7acxloeR+ycL3Yanvr78LskeUJhWomhfYmxB1mOZH1WXm4AiLMpDOoJyH2NwqRQMMZ1D5XagoLnE24hWJGjAe6iPp2LEDJ6gMiowd0FWeLH2LW/ix893EOnCBpJa+Rcu93m8bSs0UyJTsJgpPCFWwsUivCcCysT/ikKIHb379yCDoMB7AEQA0Yp6yadBfBKGk/Fjvy8q1X48Thsky4glIlAgKgSPxVMjqNFeQFvycz1S2zEuMohB9TDpFBoVFxgjE0oso7dFEoIjhksJkCwnEYtyaGMGEKrAkH0oGHsOVBguZ+h22mPItYCw1qvdPq2e030gO/c28PJOwd2WFe1cwCqg+8kRkZAr2eAlJJAZqAoCNNTFG3/0prLp2A1GiGdLCeBQGsInwM4A+EUIJT1yPOowIhnUGmKUuuN5eEr9Sym1RS4nrcnO9WEZZEEglgQ4uqP9reRCRkbLiZTvMBW2UiBoqd1PHrhH6usAlBrBdUxOYaqasKNAWzZCKiMUZemrXnG2DgTtreNAUPo5ISpnvYAiIQv5NsUZKrtskysuQ3hR+Y+gIrcAy/kOVq4snwaq+Wk3YAjoFmwLqPd/XoKldpixvH9RUWjNMzTKmwDomnJWngfGbZoR64ETEvGaacqzM4qMMdLqdJFGMf6O1ZZrQkbZhQuPk+j17UATspkWYZn7BjDbINrMmiZqnHyCbUlgsk1mbBxA7B1Q2kebu6ijlRQ6aNr2oZufTmO+8iBxe0+JpI7blc1fxrk0nCO2kJMqNUm9Mc53IhMEwAwPmWsOxhis3lwoeaWhlsfo+VcdQ68woZFrTfqH9XDqGvGaSeOcMUvGg0rSzNOIP+A5rH1/uys59AH2swp6PYUrvqlDk7dtohLnpNhPDZ1O2FWFeHEnQoXn1uBx9rMbAaDyxovvKTCzIyym0KQv8VxCfzs8wkT+UjILBZUWMwh+k6vI/g0vo/2AwjnMlhgheM/iL52BFVNYqCnMa6VX5/gXmFSKIyL1iAsVxsxnW+yj35TiEmcKx0uxirObPmpZME/CNMUwja91Merf34BZ5y1Fd/4psZDD2j0B83JGuYsaqMJUYmoZQxs3Khwzrk5fu5nM5wzux98eAXXvHyEwwtT+MHdY5QlmxBzR4Y3XaewubuEuk+e2LEGTtu0gGtftwl/dwOwvKyhtRmhvOxFXVzxgiPQS6UFK8Q9uq/SLbpIIeEA0bu0nokVSChEPAIYlwnhpf3e2YSlQdcnFWVHRhzAJbdWqkmMe5uRq0NAPRK9KkkhhUkhlhS6xaOsOSBNgsA1TmYRH9mPS3cOcOkbN2GketCUiaST0DoHAjH7qA0AsnyGGh29AiweQX3A7I20qdyL3/vN03HfI5OYOwhsmGGcfTpj02gP9NGRSH5ZpVtexUsuOoALfupEPLALGI4IO3cAZ+9Ygtp/IMw/TJM3Ul8iAshr/CbPCfX5HcTc3dnfWdbXxjUsgCjrocQGrI5UWBUlmhiTQDJzYiousFRvxJZsC7jaG5M3OeKgk++2jCT7JqvM7kOcPbSupN53BJibD2v8otEaF59xOJeaAAhzS0jMaGPUWkcArPsjFPt+jAs3zeDCEztmyfTRFdTDUVSfj9MB1HMHsGNyGTvOnDThwmAIvXvZjgJK6+aEID60bZqxruBjAJA4BB9lJBFC6qYlOIotWBp2UNV2yDvBaMMCuI2blsoZzHY3QamjYD2wGm3NmBIJn2QvfB80JAYgjC3YAyqAwHUil3UQfKzQMRBa3AGLcp6Kpuu23ceqRn14PqDUL3YF5JQyKa96pQ8srwogUyxoyO/y5DXMvJezEKoEnuBODohhxDHRek+wheBZA2oKtdqIxb7plzbXKvIArpMBxYxR3cFyvQGzna3gwR74oE+OvcdDb4iGe9mGZxTMliF1QFgyRqFDJXKly/E2S3R045pJr0Y9DGE5EJ9HSTl5zbaxDglCqanp9dbV/Ph6fudEzZ4isPiNo3MSwYMTohgAwExQxTYsDTKMKkJWOMMa91cggSoA2/XB/HgGMxOboPJFcLkIVo5orSUA24hoto+YEwjnDsSiUl+fuLD3JrYjJfdsrCuU2iebQ83OapRpwUwjOdPit9c8Jr6nw9Sy2ULbwzprUS1HFUWXa5RrCN9alXwWNc1gftUOujk5gKBE8NfiAoKFHtUdLJZT2NzZZh4Rpyu4hZ+JBw5vDMsN2Gsdkxi5s8iWoIjvikyyKe3Y6GsLUBovKW3BGQhh2puvyw3CSLeUAq3ZlFhQLVzANy/wAZ++FeXZ9QnLsQT7vpavh/wcyrEVK3VOwEJfY1RlyPJAq4liUhkDQCKFAEWMo+MpTGfT6HS2Qg/3+97wTwWMLHKb2ZMdKR40BY761lcmOguN39YQsq8oKSNN+Voa6lxaq4QhcdlO4hrfOQBLClCkzV3IHJoQNDfUlZh72e40BHTn2mtR7wSMdQdHV8yUM9c1JoO7hguoq1pTZl1BDTC5EbscR0dT2DGx2T63dhHCxofJo5EgRAekx5nNMLOfPBLug2xHEZuh6EixG2hpwwLH15ezaWi989IDabxOLQJHEGrqamTFa0QKTutjytDi51sndsQADKuINZBtBOWbcGRhjKpWyN0GVwgcoK61h3wAgObSic2tpQeZlbmL4x6m8x6mu9uh6wHAYy9AtnFwFJl5ly410t2lNb92kIitgEiAwVllRjAYbhjadVQYkk5HacIH61Ds1zbSug6auLXSpplnea1YwIFnsuCHdotcUR97oIg6U96SmnvfFPtdM0BdqN6JWB7WWOyXINWFm9Mo09+1hn/8S1gYovWqY4iKyO/9o2x28NBgEr2pCll3J3i4G2HygvPzkhyKREDEOoUplEvCyZm/sAtZRNLDBJ0AFodWOXomtT5JeQrO2RRsNPMJ/lhkvtNKGEHo3suwT1MQyKTL/e9iPx9p03ULoNqGfyOSJ4+5AwrU24lK5zg4bzK4foN3DzqbC6nrVVe9V5/VwXBBielY8cYnjFGd4dCgC2TToM6OuHEO8m7cgzk+3jBrCMf9+IDpDBPratN5bhBJ4seh3b275L+LhZ1Gsg6PoQHbmTku7y4/A2k+nmUZuHaI33XahiAcsp85rRNC8G6AR2q9GCdp9hHHwmc0Bp6oux3IZ3BwYQWjsoYCeSCG5XCGfA8Gw4WGBajK6hBRF2EzZyN8BYCVmVe3OCrQVTk2T2wGdAkeHzQlhKtNdEtYBeH3vfaKkpF7CGrF1lrEgxgh2eMpqY0cXMhFwie5VIP5ogFxJhA2rYxm0ApXknKR4FaEm+HkfhraKz7LYo0y7ntsaZqRQEADdbaBOltxdHEFSytDZKrwM5m8Ejt5kkI1rg83ADAYjh/W6ELZ+VJ+ur8jEAQwEQ71C3RUjenuCQDX4PGR4IflDCBnLlMXEHnLNveAaJsAV698xp4HjLD40g8HjyBn3woASfA5vOlkzqNocVuUKV2Gh6U3721CTQUeNT728ZHgk7o8sbVWqrMFqrsDy/0BDi2sINhuc89m87bAAUAKo6ryj5T1ABiV+sCgX9aKVMaoo2ScEvKqWWFuJccpMyW6nR3G1JXzEEQgUjkf2niMUEuPik5IHqkunGqAsfMFAmzpadHaRCkkQpg5KzNv4mz2grVxebQhZowCFiOdfnhWgizV9FSovgHc+HnN+B9G+FRsBnV3YjgqMXd4EVozMmVSvsqafafACma39X6/rAflaL+7Bc8Bdh+YO1BWejG389wUxblj5w4yBZQ1Yd+SeVfdnaBic+zrXWO1OFbDP8Il+NDEJ0dITzrK+Uf3XiP4Uc8dQmeyXd8gx+iJ3WXY9mEyiYLDHDvv3xH/xiw5AcymhZIjOIFFPAHNvmnpI3YK0ygvweOEvwXUOwllWWPv4XmUlfbL3kPYFxJqRIxMZRjXvLB739KBBgD+9kM3z9Uac1me+VDQPyKHhBmBWcwxrBT2LmqUGqDuiUCxNXRkSv6ibJXVHFFUksCIkCVTtEOnJZ3oySQHMpUKQFtiBgcMnQggEbQWv8v2aTTKt4M+aZ9vp47ew+QOjold1CdJncU2UHcnylLjkUNHMRqX9lFL5BWVrNBJBUDkuYLWfOBvb/zcnJO7dwG33nr/8vLq8N4tk/n5ZKFBmuy+OYYTgAwonH8elIS98zVO2qhQdHeAUYDHc0aIJKbSxk4SgSNYnylWElHkVd3J0kUcK6PD7YdlVVGRtfK8UuuSY85VsajD3VfDjjsFEDWsSQA5eZcnMMAK6G6H6mxFWdXYe/gIBqMxMsr8OI6yAnJcOgCCkGU5lhZH99566/3Lruqwuo+ZH5k7eie5TZTsqhiPJBFGmBU25mKDMeORhTHGZQ3V3QqaOBWgLsB2f3rNMaobGiQ1B3aNnTDNci4hUm1INVJ0bKv1aDueanFb+eSagBnviHyzs15osQLia2SdOP4cuQeEurQG0AUmngHV3YZRWeKRg0cw6I+Q2dA9PGxNys3yAcdoVIY9B5fuZLF+PRoL2Lt/5fbVc2bLPKOi0lL4Ruh+2p+7GDGgGMOxxp6FMXZuBCY709ATp4NH+03a2A/3CjKXbjgRaabbZRSCUVs9E8YgaDSjMfQaETCZUfKmaG1thzwv0cRUsxvnuDawOCTdoCgblU8+syjEDOQbQb2dUKqD1cEQ+w4fxXhcI1NB+MRkzL113c4hODllmcLqoC73H1y5Xd5OlEf9yGdvvXt5pZzL8ywaFnYZJe9jKJysLGEcjzX2HBliYXUMUgWod4pNGGWIEi8S7dKXt02AlNrq+aLwmd6/JxMrU78pSVubX47qEmX0GmVdY8TvHP05n95yv+mz/6I2QdystiZ/B9TEKSBVYGF5GY/MHcJ4bBa4EIDMp3itvHwGN+znTAQUmcLSajn3yc/dcbeUeWQBvvrVb+8/uvjS23du6pxSktV4t44Pdlsgsht+KLMRogEEuUEG7JsfYjDW2Lahi6x7ApBPg4cHgGpZjAY6RRT5gnT0z/tPGTa6JJLVLR8exhpGaT0uhEyXH/gBGNkeXpsWCMX31knOhpaxM7ec1/DtyTHvIwDkG0Dd7aB8ArqucXD+CI4urIAIyEhZ5QuCVyA7LscWHAEYRObBF0eXRt/5/Ffv3i9aEFsAZq7u/OGeL5VV0G4nYIChbMDtWKZb7+ESRZmdWXV0eYQ9h1cNO80nQJPPAE2cAlBPaIUWHKFNy5IOivyjsBK1sBY6KGfqd1knWgoO1sT3PTczxbIO2TxpAaJwDzHvSdsb3YuOL6BrQPVAE6eYPssnMBqOsHv/ARyZXwSR3bGExLxHAhTbBBYRFIdt8MiCQYFQ1sD37jvwJWau1gQAAHz6i/d89ch8/2CeC/9C7HeBdxeTRNAAgj1YMgX0hxUePrSCI0sDoxzdWdDUGUBvJ0AdG0sLtwCddGbyLjsuXYARkSjz3ecafGjIDZcSx+tJXTr5S91J3VJnRAglwRMWygqb5f1pbYjzxEmgqTNAnc1gZhyZX8Cuffux2h8gU8oL0ymcfJKt++7Jn7MQbMz//OLo4D9+ffdXUnk3APC5L91275655W90LA/wIIDMCcQhhlKOhbIvnyuGrjXmjq5iz8ElrA5GAGUmUpg6A6p3EpDZnclqHRIqMlZue9BzW/ztTHEj/kaz83WbcIFm4ma9P2Gh2qKSaBUUPEfwFk8zSGu7ydYk0DvZ9El3GwgZVldXsXvvfswdPAxdaeRKQVkL7CIyZad6KJBVQo4GX8PWCWa30t0HV7/x6S/deV8q78baQGYevvPf/cubLzpz5mUZUVHbG1WKvM+HNT2y9xUQT2qEISIAY3U4Rn9ujI1TXWzeMIFetwC6W4BiFlyvAuWi4Qhcwee609Rv9EqcdDIzuXXPomhLO1ENS/4hz3FfE78d1Rn1XAtnCKgJmzQQWOWgfBooZkH5FIhyABrDwQBH5uexuLwCBhuWbxULclTPkXHFIeMHxwccMKyCKoX+qKpu+fbuTzHzIJV38+nhAE48cdNpn3rva24++5SpC4eDAbguwVxCVyW0LsF1CXAFXZdgXdtHzGtolilYjdqadud3da2hFLBxqodNMxYIUNYfl6BqxaxGqvtm/iFq3+FMfraAGCSSdC/5bT3ANLNB678cLpib9UkCJy/jNB8MogygHMgmQPk0OJsGZR0/Z2A0GmJ+cRELy0u2j6xuKyF0RTasU3ZjCkfylH8HEVRWgKiAyjqgrIvexCR+vH9095W/f8uV+/fP70pvLW+737m5hd1f/87Dnzxr57kXKjLsHyym8pMZHvbLteyom0IY99YI352sSBG01phfWsXiyiqmJrqYnZ7EZK+DPCvAnc0ANoG5BvQIVA+MhaiGIGgw1yCIh1rKDaQcQCxxZKIWLKxH76n57gd22OyY4moQzxOIiCoBQAaQ3VVKdaGyKSCbBLIugMznVKqqwmq/j8XFRSz3V82jepVCRlKbXTinoDjO8ysxwcOl7oPvt09HIbMd/Ve/O3fT3NzCnjZZtwKAmfX5z9z2yZc8b+frTj1h4vSRNo8q9H1O8R4R2v8Q+gswGz5ruz+gG3NXdidxZo3llQGWVwbodjJMT3YxMzGJbrdAnmXgbArIpgBssVygAukSzBWgxyCuwFwbDeMalAjVr2FoE2wEghaTESQd/Bo1SzLcM4hzK/QOmAqQKsCU2/2OnJAYVVVjNBpieWUFKysrGI6GYACKMjN27/mVECaH0T3JvXzL3Tlkt76xiqmI0S0UHj60+tD7b7r7k8zc+szzVgAAwI/uP/yDW765+xNveMU5byVLNjTcticiIIYDA/vfXaZQcwwUdv8oPG+IoTEajTAYDHBELaJbdNDrdjA10cVEr4siz6HyDMQdcNaNxWj9PfHxmnaX3WsBhTfxoo41qwtA8kJg540t+AHUdYWqLNEf9NFf7WM4HGA4GkEz++cV+8UzLm6H1HJBumVaPjhDI2wmfwuKQuJOM/DFO/Z/4kcPL/5grR5ZEwDMPDrjlK03XvacHb/8zJOmzhwMSotIKUiASdtciA4PFtHmd7/pmt1syi3T86SZLKDI7N/HWmM4HGIwHGB+wXRSt5Oj0ynQyQt0ux10igJZnpt9e+yun54mScIYDsRSdCYbciIni+PuXOnfJRjIGgbD6g2pr8GaUVUVRqMRxuMhxuMxxuMxhqMhtK6t8NyDqsO+BhT9BeH6wR0nbOcC4CZ2kM8GBhcQhrJ7RYb7960+8H///b03MnOy8PE4AAAADz1y5K6Pf+HeD//2NRf/x0xB1X67MvtHzgJY388CGBymYriZMspyBfN7SMiY4zZ5kZFItmgMBkMMBv3gjgk+JqbM5Coyu8ebmz4WBvRbhNmw+OKHRNvbGINfpwdAa8NHdF2hrmvUtQ7Wkcz0K2WnZplFIe7duVIW5hux8P0/cdxFA5HQ5bnmX6YIo5HWH//Kwx9+aG7prvVkvC4AmHlIRB978SU7f+HS87f8TL9i4YSM6TVTxxjaCpA1/DFHBp1pJWbzCD9tiCTbRRl+3gXI1OOshuUR8nEpDEBXNSpoYKRtO7UHm3wL+/05QEhaGgtaWgMHa3Jz0/wStlDSC8NqHbkkjc2YhVlUDLciyEzRtk8mkULmIFQ5+4qY7YrexFow/OMK3ZZ3bvIHKaBXKNx678K3/ujD93yMmYfryfh49sC+5z0fuuP9h+cHC0XhGh7MleKQ/PEZKWcgYJ5NEdBqGq+s1itm83QNCsOZGUSGi2AnNoZwKIPJSuYAcmWyjrki+6eQK4VMEXIF5BkhI/kbUGTyO9k65LtCoRQKZTJoeUbISaEghZzcZ0KmFHICcpjPCuTbnomEmMmimjZnpH3CLEPw18qVCZoHN/GGICxFZCEYisknh1zc38mBw4vjhfd87MfvB3DPsYSbH6sAM1dE9LlfvGTnjddecea/zWCic6ssIfRieKRr5faElFxAKqjTKKGkIE8OrfW35zhrEDonWi/qtnORMbmdHhyWiYfIhAXhczRBnhaZfQ6g9gcjD5MSwUA9nNYqRw6JvAXxTB+hXJr7aroCYz3gQOWAAUtASUOpDGDg5m/M3fjpWw99Ls37PyYAWBDsm5wsrr/gjA0XXnr+5uf3B6UgghpkHnjixOAfHGmNefT0cbE/dhAsw5UMEQICrgDyw78mKjYux/GQaIWtAEk0AdW5LVemLWEkg4IWzhAlGF3CUYAZbaGarYu8GxHPYYbrE4rLROTO3E9kARIu4O5soqNw64+Xvvn2D9x/PTPvOx7ZHvdjEAaD6o7f+6s7/uqRg6t7e4XbPpWF1lizxTZnbTy6B4cbvnSdZcw8+ZBFLhdQ7MozMmseM2tGXd2Zcm4E1hTK0Un3PZzjzotGMG1nKsTzH6Nr298zV47cnsPw52VAZP79+Qh1kQvPLCl2ShLmVIh+ce1js49CWsYv8tSBIvYKwiOHBnt/728f+KvBoLrjeOV63ABg5sFtPzj4+f/ykR/89eqgXO1kbvaJFpzAEjeSHUumDLE/FoDBotO0zWDp8MfynFBGgQVILIew3zOywnLfOQg3cwK3dThQxUAIgAhATf/MNSK+AjLtQJgyF8x8cBs+Vgdi4HEq/HhI3pl6+fAHsvfRyQj9gV79s0/u+evb7lv6fFvOf63XcbkAAYI5IvrYmdt7O//tFae+KSdkfgWBNclmWrVzCeZOzNNCNBgaRNqzef8IdEeaRIgVlhg4ZxKF8NZasHC8Ngx0vQy3KCQ2+/6hi4L3yzrCzfpT3Ik+GpGLlNxM6XCuWFnkXIkgyBCXSRZBheZAZAWdq3Eu1B50Vjcziz3rD3zxwIff9z8OfoyZ5/AoXo8KAPZ179v/+u7rT5wttvzKC7a9kslNigmd48gbeYIXHmniuJNZ/WuIjV8S4OTAgSi6/9nzB+0dNEdgENhwgo9WiwhCGLEMJMIn2Qhf3oHZy8mX1/ZwCA1BIZXrGLAUPiXgEFcWoDDWM+UgbsmbeQim2Un9k986ctPbP7DregD3PlphPmoAMHNNRLe/8T13vn/D5LMnfuFZsy8ra0Bb7Q8DQiKN7kbFHDnkQBZdNtC/e0vir2e1W7TBw0p2nYzPWZTlRieH31oOtpYXz0vgQNT8Z3vcA4OloGwZGx5Qgga/R5BrOydlRPOUsCxKEboZ4QvfXfjMG//bj98P4Dtr5fvXez2KZ2FFIBhXFf75mj/53vu+/L35z3XyYL7NZIfg49zYgfPncCNVzuxz8JM+7IKcjiZIofOh3ncGwmX8urYkNBA/xe7PXstfL/X5sjw8mQ08RvAKBNLppmhFJC4dk5f5M4h75ODrfYJHSNzVl2ydgAxANyN8+e7lz13z5/e9r6rwz+ule9d7tc4HOO6TiTZOdrIX3fB7573pFy6afVmlNaraTKgzZt1l6ORqHCNpl+oNM2p1OO7GHr0ZcOXiqTrxqCyL5EFsH0ShyFc3J5y6Y83yadRIog6z8VWw9WH/AitqhskQ+uV2NsNPIbCToz7xPsgq5BCUSTplRLjl+0ufueb/2vX+/rj+EjMvPmYZPh4AWBBsyoAXfvB3znn9K567+SqlkJelIXx+eZXz3SL/7x+VpnWYZOmSPmKKNAtyGWZ8oilsmWkKmQZEM4HaYn8kAAh00/5MCEvNOfqpUZ1YVx8edRfAQD7eVXBDpkQuhgi/u3SyB4AtURQZaqbqU99euOm6/7brQzXwdWaef1zye7wAsDe5AcDPvOeNp1117eVbXzPdy6aHYzNW7wXot6XRfs48W/egxXRoaSUCIKzAIcAieIKMQuB/l8JtF3njPta6QV6jgCWLIh/op2W5zSf9pkxuyRaUtwxG0O7ZBWH5tmH91gpYy9ErMqwMeOVDX5v/u9/90N6bAHyDmZcet+yeCACYG6dJAJe88UVbL3vnq3Ze+4zNnTOH49oTPhYrbd30ErLTyPykSe8aZFkOs4adVgN2uZSQhK/Dnh9ztqY0G3vdCRPhowC5QjrUHbSaPW9hMTXbCFzsxI+g+W5wy2BAwY3qyK3v463pFXodhd1Hygf++BMHP/SBry58GcC3mbn/hMjtiQKAbXgHwAXnntz76b9806mvvuTMicsJoFIMk7IWgmQLBQ7DwVqLp2DaocLI91sAuQUZIZ8gXIOP2xN73RbrU1oADTMve6ixT2UsZgGAZNqGm1dPIomrrEXg4Pt9WSJ0MpMiuu2B0Zd/4//Z95F79g9vB3A3M4+fMJk9kQAQQDgdwLPf+6sn/+Irn7fpX22ZVptHY41a1yG6Fy7Amf1A/Jw1CGPFLMw868TEOyviz5dSJGEN4sBRvuJeEJofx58yK9NCDENO2wvcBXiuGY7Q+bVcghDaY0qZvP6RFT76sdsWP/6W6w98DsAdzPzQEy6rJwMA5j5pE4B/cdlPTT3rP1+985cvOqX3/E7B+dAkDaxrsJqtA4uXhC5s1iS4QET0rFCdBXHfXUzZktQJuJDJBUn7zXcj62OQPnEw8ETp320E4LM7Qcjw/l8QPiL0CoWyouquvcNvvvPGQ5/8yj2D7wK48/GSvaccABYEBYCzAZz/9leccMmvXjb78pM35edlxBhXWph/wC26ZG0F6KaU+/SyjRqYwzRlNxJoz/cJJpEpbE38iO9tQvVybBW+zXY6nx+5DZ/OMY/fJZEfjKICu5zKEkIFhU5uRkD2HK1/9N+/tvSZ//MzR28D8EMA9zFziSfp9aQCwF+EaDOA82ansrP+4Mptz7vqp6deumM2P7NQwLjUqOVyKrih3sZivMgCyJ1/WLssYhCIU3wzMYG8MYhIIIRFJ7PDtjUjIquXcIM0qpSpA05yACR4gavQzeuzHKGbZyhrYN+SfuAf7lz9/B9/euHWhdX6fgA/YuajT7psngoAWBAQgJMAnL1tJjvtd35x9uJXPGvDZadsyc6bKKhTaY2ykkzfkT1J7uBzBPJhVf4Rav68xtXR0P+W0E7gJ17EnJ4X/WaBYwt64uen5lpQ+GdhkJlplBP6Ixo/Ml//6Obv9r/y5/+0/L1Dy/UuAPcB2MtPkWCeMgD4CxJlMEA4A8BJb3jhhme+5vlTl5y3vfOs2Qna2S2AWjOq2loGrcNgDALR8xrvxhlaNnz2nG2NFycabeoN/p+THc2ijZbTcICDmXeuwF08sw+gzhRhVAPzA973o7nquzfc2v/2B77R/zGAvQAehBH8o87nPy55PNUA8Bc2vbUdwKkAdpy5tdhx9fMmz/r5cycuPG1r9szZCd4+06Upp+2azZPGNAtTTyaXINm/CyelcKJ8ERxblwQvnEDp/xSb+shCJAEHgewcR/e4WbOCamWM1YU+H9h1tP7xLfeMvn/j7YP7Hzis5wDMAXgYwIGnSuMbcni6ABA1gmgawA4AOwFsLjLMvvyiie3PP6M49bmnFc88aVP2jG6GmYkObZzMMVlkgf0HwQdJeNn4vE6crYtfcoNI1yDEHoO5ca6zSvLMqmb0S/SHFRaHJZYfWdJ7vv1wdd83Hxg//Om7xwfKGgsAjgLYB2COmVee9r7/SQBA1CCiGQCzALYC2AJgBsD0WSeo6Qt2dmZP3Uwbzjkh37ZlmmZmezSTK85nujSNZFgnucsYEQBiSkhYx1MgpJtE/QxeHvNKVaFaGOrlI6u8fO+B+tDDR/XS3XPVwv0H9QqAFQDLAI4AOAxggZmX8RP0+okDQNQ44yZmAEwDmLLv0wAmYWeGw8ijeJqaWMKgo7J/fRihrwBYte/LT5d5P57XTzQA1my0IZIFDAAUHtvMpifiVcEkpSsA5VNN4J6I1/8HBq3Cgi6UwmIAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMDUtMDhUMTE6NTA6NTUrMDA6MDBXfw+IAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA1LTA4VDExOjUwOjU1KzAwOjAwJiK3NAAAAABJRU5ErkJggg==8.511.01.01.00.50.50.50.5111.01.03.31320.35244.62022.78231.65660.1762-0.03.31320.35241.65660.17621.65660.1762-0.0#FFFFFF#0000000.00.010.00625#ffffff1000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.17623.31320.17621.65660.01.65660.35243.31320.35241.65660.17621.65660.17620.0#ffffff#0000000.00.010.00625#ffffff1000.01111110an image to be used as an application logonullnull[]nullnull0null5solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull2.58810.63816.45597.44381.29410.3191-0.02.58810.63811.29410.31911.29410.3191-0.0#FFFFFF#0000000.00.010.00625#ffffff1000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#000000Arial0.138888888888888900.00.00.0-1.00.00.0000.00.319052.58810.319051.294050.01.294050.63812.58810.63811.294050.319051.294050.319050.0#ffffff#0000000.00.010.00625#ffffff1000.01111111contains all the script files required for an application deployment nullnull[]nullnull0null5solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull2.66246.8743.66376.8741.00120.03.1636.8740.50060.0-0.00.00.11250.12370.00.00.0-0.00.0125#8080801000.003125221101600.12370.00.00.00.12370.0Reposition Text1000.00.01.00120.0121.10819.72911.10814.01210.05.7171.10816.87060.02.8585-0.00.00.11250.05.01070.00.0-0.00.01875#8080801000.003125221101600.05.01070.00.00.05.0107Reposition Text1000.05.7170.00.0121.10818.39362.16188.41321.0538-0.01961.63498.40340.5269-0.0098-0.00.00.11250.1302-0.01720.00.0-0.00.01875#8080801000.003125221101600.1302-0.01720.00.00.1302-0.0172Reposition Text1000.0-0.01961.05380.0121.03881.03882.45598.43240.51940.5194-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.51941.03880.51940.51940.00.51941.03881.03881.03880.51940.51940.51940.51940.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn3.iconfinder.com/data/icons/vista-general/128/folder.pngnull0100nullnullnull[]nullfolder, open (128x128)nulliconfindernull1.10816.8742.16186.8741.05380.01.63496.8740.52690.0-0.00.00.11250.13020.00.00.0-0.00.01875#8080801000.003125221101600.13020.00.00.00.13020.0Reposition Text1000.00.01.05380.0121.00121.00122.43726.8740.50060.5006-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.50061.00120.50060.50060.00.50061.00121.00121.00120.50060.50060.50060.50060.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn3.iconfinder.com/data/icons/vista-general/128/folder.pngnull100nullnullnull[]nullfolder, open (128x128)nulliconfindernull1.62510.38832.47386.2950.81260.1942-0.01.62510.38830.81260.19420.81260.1942-0.0#8feaea#0000000.00.010.00625#00cccc1000.010#000000Arial0.1666666666666666600.00.00.0-1.00.00.0100.00.194151.62510.194150.812550.00.812550.38831.62510.38830.812550.194150.812550.194150.0#8feaea#0000000.00.010.00625#00cccc1000.01111111Resourcesnullnull[]nullnull0null3solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull1.62510.38422.47387.83630.81260.1921-0.01.62510.38420.81260.19210.81260.1921-0.0#8feaea#0000000.00.010.00625#00cccc1000.010#000000Arial0.1666666666666666600.00.00.0-1.00.00.0100.00.19211.62510.19210.812550.00.812550.38421.62510.38420.812550.19210.812550.19210.0#8feaea#0000000.00.010.00625#00cccc1000.01111111Classesnullnull[]nullnull0null3solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull1.10815.27722.10935.27721.00120.01.60875.27720.50060.0-0.00.00.11250.12370.00.00.0-0.00.01875#8080801000.003125221101600.12370.00.00.00.12370.0Reposition Text1000.00.01.00120.0121.00121.00122.41215.33480.50060.5006-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.50061.00120.50060.50060.00.50061.00121.00121.00120.50060.50060.50060.50060.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn3.iconfinder.com/data/icons/vista-general/128/folder.pngnull0100nullnullnull[]nullfolder, open (128x128)nulliconfindernull1.63250.41472.47754.74260.81620.2074-0.01.63250.41470.81620.20740.81620.2074-0.0#8feaea#0000000.00.010.00625#00cccc1000.010#000000Arial0.1666666666666666600.00.00.0-1.00.00.0100.00.207351.63250.207350.816250.00.816250.41471.63250.41470.816250.207350.816250.207350.0#8feaea#0000000.00.010.00625#00cccc1000.01111111UInullnull[]nullnull0null3solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull1.10814.05212.66244.05211.55440.01.88524.05210.77720.0-0.00.00.11250.1920.00.00.0-0.00.01875#8080801000.003125221101600.1920.00.00.00.1920.0Reposition Text1000.00.01.55440.0121.48340.34372.66292.30350.74170.1718-0.01.48340.34370.74170.17180.74170.1718-0.0#e6e6e6#0000000.00.010.00625#00cccc1000.010#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.171851.48340.171850.74170.00.74170.34371.48340.34370.74170.171850.74170.171850.0#e6e6e6#0000000.00.010.00625#00cccc1000.01111111logo.pngnullnull[]nullnull0null3solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull1.10812.76952.66242.76951.55440.01.88522.76950.77720.0-0.00.00.11250.1920.00.00.0-0.00.01875#8080802000.003125221101600.1920.00.00.00.1920.0Reposition Text1000.00.01.55440.0121.63910.37582.66293.59190.81960.1879-0.01.63910.37580.81960.18790.81960.1879-0.0#e6e6e6#0000000.00.010.00625#00cccc1000.010#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.18791.63910.18790.819550.00.819550.37581.63910.37580.819550.18790.819550.18790.0#e6e6e6#0000000.00.010.00625#00cccc1000.01111111manifest.yamlnullnull[]nullnull0null3solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull1.36590.3152.66291.09280.68290.1575-0.01.36590.3150.68290.15750.68290.1575-0.0#e6e6e6#0000000.00.010.00625#00cccc1000.010#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.15751.36590.15750.682950.00.682950.3151.36590.3150.682950.15750.682950.15750.0#e6e6e6#0000000.00.010.00625#00cccc1000.01111111images.lstnullnull[]nullnull0null3solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull1.10814.09011.10811.56730.02.52271.10812.82870.01.2614-0.00.00.11250.02.21110.00.0-0.00.01875#8080802000.003125221101600.02.21110.00.00.02.2111Reposition Text1000.02.52270.00.0121.10811.54452.58481.54451.47680.01.84641.54450.73840.0-0.00.00.11250.18240.00.00.0-0.00.01875#8080802000.003125221101600.18240.00.00.00.18240.0Reposition Text1000.00.01.47680.0123.66377.3873.66376.36090.01.02613.66376.8740.00.5131-0.00.0125#9999991000.00312522111121111000.01.02610.00.00.01.00.01.0NURBS(1.0,3,1,1,0.0,0.5131,0.0,1.0,0.0,0.5131,0.0,1.0)3.66377.3874.66497.3871.00120.04.16437.3870.50060.0-0.00.00.11250.12370.00.00.0-0.00.0125#8080801000.003125221101600.12370.00.00.00.12370.0Reposition Text1000.00.01.00120.0123.66376.36094.66496.36091.00120.04.16436.36090.50060.0-0.00.00.11250.12370.00.00.0-0.00.0125#8080801000.003125221101600.12370.00.00.00.12370.0Reposition Text1000.00.01.00120.0120.75090.75094.797.3870.37550.3755-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.375450.75090.375450.375450.00.375450.75090.75090.75090.375450.375450.375450.375450.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn3.iconfinder.com/data/icons/vista-general/128/folder.pngnull0100nullnullnull[]nullfolder, open (128x128)nulliconfindernull2.24610.38194.85175.90311.12310.191-0.02.24610.38191.12310.1911.12310.191-0.0#f7f4f4#0000000.00.010.00625#00cccc1000.010#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.190952.24610.190951.123050.01.123050.38192.24610.38191.123050.190951.123050.190950.0#f7f4f4#0000000.00.010.00625#00cccc1000.01111111execution_plan.templatenullnull[]nullnull0null3solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull1.59750.39814.85176.87340.79880.199-0.01.59750.39810.79880.1990.79880.199-0.0#96f8f8#0000000.00.010.00625#00cccc1000.010#000000Arial0.138888888888888900.00.00.0-1.00.00.0100.00.199051.59750.199050.798750.00.798750.39811.59750.39810.798750.199050.798750.199050.0#96f8f8#0000000.00.010.00625#00cccc1000.01111111scriptsnullnull[]nullnull0null3solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull3.79560.35264.79558.67151.89780.1763-0.03.79560.35261.89780.17631.89780.1763-0.0#FFFFFF#0000000.00.010.0#8080801000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.17633.79560.17631.89780.01.89780.35263.79560.35261.89780.17631.89780.17630.0#FFFFFF#0000000.00.010.0#8080801000.01111110contains MuranoPL class definitions (*.yaml files)nullnull[]nullnull0null5solid0nullnullnullnullfitnull100nullnullnullnull[]nullnullnull3.06660.35514.43335.44741.53330.1776-0.03.06660.35511.53330.17761.53330.1776-0.0#FFFFFF#0000000.00.010.00625#ffffff1000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.177553.06660.177551.53330.01.53330.35513.06660.35511.53330.177551.53330.177550.0#ffffff#0000000.00.010.00625#ffffff1000.01111110contains dynamic UI yaml definitionsnullnull[]nullnull0null5solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull3.62940.34634.77844.06911.81470.1732-0.03.62940.34631.81470.17321.81470.1732-0.0#FFFFFF#0000000.00.010.00625#ffffff1000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.173153.62940.173151.81470.01.81470.34633.62940.34631.81470.173151.81470.173150.0#ffffff#0000000.00.010.00625#ffffff1000.01111110an application entry point. The file name is fixed. nullnull[]nullnull0null5solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull1.82380.36743.88811.60260.91190.1837-0.01.82380.36740.91190.18370.91190.1837-0.0#FFFFFF#0000000.00.010.00625#ffffff1000.010#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.13888888888888890#4d4d4dArial0.138888888888888900.00.00.0-1.00.00.0000.00.18371.82380.18370.91190.00.91190.36741.82380.36740.91190.18370.91190.18370.0#ffffff#0000000.00.010.00625#ffffff1000.01111110lists images if requirednullnull[]nullnull0null5solid0nullnullnullnullfitnull0100nullnullnullnull[]nullnullnull0.4380.4384.85266.36090.2190.219-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.2190.4380.2190.2190.00.2190.4380.4380.4380.2190.2190.2190.2190.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn4.iconfinder.com/data/icons/Basic_set2_Png/64/document.pngnull100nullnullnull[]nulldocument, file, paper (64x64)nulliconfindernull0.49750.49752.66562.76160.24870.2487-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.248750.49750.248750.248750.00.248750.49750.49750.49750.248750.248750.248750.248750.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn4.iconfinder.com/data/icons/Basic_set2_Png/64/document.pngnull0100nullnullnull[]nulldocument, file, paper (64x64)nulliconfindernull0.49750.49752.66564.07190.24870.2487-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.248750.49750.248750.248750.00.248750.49750.49750.49750.248750.248750.248750.248750.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn4.iconfinder.com/data/icons/Basic_set2_Png/64/document.pngnull100nullnullnull[]nulldocument, file, paper (64x64)nulliconfindernull0.49750.49752.66241.54450.24870.2487-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.248750.49750.248750.248750.00.248750.49750.49750.49750.248750.248750.248750.248750.01nullnull[]1nullnullnull00solid0nullnullnullnull/imageProxy/cdn4.iconfinder.com/data/icons/Basic_set2_Png/64/document.pngnull100nullnullnull[]nulldocument, file, paper (64x64)nulliconfindernull0.96610.96611.10819.51690.48310.4831-0.0#b2b2b2#0000000.00.010.0#0000001000.01100.00.483050.96610.483050.483050.00.483050.96610.96610.96610.483050.483050.483050.483050.01nullnull[]1nullnullnull00solid0nullnullnullnullhttps://cdn2.iconfinder.com/data/icons/Qetto___icons_by_ampeross-d4njobq/128/zip (2).pngnull100nullnullnull[]nullzip (128x128)nulliconfindernull \ No newline at end of file diff --git a/doc/source/admin/appdev-guide/garbage_collection.rst b/doc/source/admin/appdev-guide/garbage_collection.rst deleted file mode 100644 index f6a0b0e92..000000000 --- a/doc/source/admin/appdev-guide/garbage_collection.rst +++ /dev/null @@ -1,117 +0,0 @@ -.. _garbage_collection: - -===================================== -Garbage collection system in MuranoPL -===================================== - -A garbage collection system (GC) manages the deallocation of resources in -murano. The garbage collection system implementation is based on the execution -of special ``.destroy()`` methods that you may define in MuranoPL classes. -These methods contain logic to deallocate any resources that were allocated -by MuranoPL objects. During deployment all objects that are not referenced by -any other object and that are not present in the object model anymore is deleted -by GC. - -* The ``.destroy()`` methods are executed for each class in the class hierarchy of - the object that has this method. Child classes cannot prevent parent classes - ``.destroy`` from being called and cannot call base classes - implementation manually - -* ``.destroy()`` methods for class hierarchy are called in reversed order from that - of ``.init()`` - starting from the actual object type and up to the - `io.murano.Object` class - -* If object `Bar` is owned (directly or indirectly) by object `Foo` then `Bar` - is going to be destroyed before `Foo`. There is a way for `Foo` to get - notified on `Bar`'s destruction so that it can prepare for it. See below for - details. - -* For objects that are not related to each other the destruction - order is undefined. However objects may establish destruction dependency between - them to establish the order. - -* Unrelated objects might be destroyed in different green threads. - -* Any exceptions thrown in the ``.destroy()`` methods are muted (but still logged). - -Destruction dependencies may be used to notify `Foo` of `Bar`'s destruction even if -`Bar` is not owned by `Foo`. If you subscribe `Foo` to `Bar`'s destruction, -the following will happen: - -* `Foo` will be notified when `Bar` is about to be destroyed. - -* If both `Foo` and `Bar` are going to be destroyed in the same garbage - collection execution, `Bar` will be destroyed before `Foo`. - -Garbage collector methods -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano garbage collector class (``io.murano.system.GC``) has -the following methods: - -``collect()`` - Initiates garbage collection of unreferenced objects of current deployment. - Usually, it is called by murano ``ObjectStore`` object during deployment. - However, it can be called from MuranoPL code like - ``io.murano.system.GC.collect()``. - -``isDestroyed(object)`` - Checks if the ``object`` was already destroyed during a GC session and thus - its methods cannot be called. - -``isDoomed(object)`` - Can be used within the ``.destroy()`` method to check if another object is - also going to be destroyed. - -``subscribeDestruction(publisher, subscriber, handler=null)`` - Establishes a destruction dependency from the ``subscriber`` to the object - passed as ``publisher``. This method may be called several times with the same - arguments. In this case, only a single destruction dependency will be established. - However, the same amount of calls of ``unsubscribeDestruction`` will be required to - remove it. - - The ``handler`` argument is optional. If passed, it should be the name of an - instance method defined by the caller class to handle the notification of - ``publisher`` destruction. The following argument will be passed to the - ``handler`` method: - - ``object`` - A target object that is going to be destroyed. It is not recommended - persisting the reference to this object anywhere. This will not prevent the - object from being garbage collected but the object will be moved to the - "destroyed" state. This is an advanced feature that should - not be used unless it is absolutely necessary. - -``unsubscribeDestruction(publisher, subscriber, handler=null)`` - Removes the destruction dependency from the ``subscriber`` to the object - passed as ``publisher``. The method may be called several times with the same - arguments without any side effects. If ``subscribeDestruction`` was called more - than once, the same (or more) amount of calls to ``unsubscribeDestruction`` is - needed to remove the dependency. - - The ``handler`` argument is optional and must correspond to the handler - passed during subscription if it was provided. - -Using destruction dependencies -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To use direct destruction dependencies in your murano applications, use the -methods from MuranoPL ``io.murano.system.GC``. To establish a -destruction dependency, call the -``io.murano.system.GC.subscribeDestruction`` method in you -application code: - -.. code-block:: console - - .init: - Body: - - If: $.publisher - Then: - - sys:GC.subscribeDestruction($.publisher, $this, onPublisherDestruction) - - -In the example above, ``onPublisherDestruction`` is a `Foo` object method that -will be called when `Bar` is destroyed. If you do not want to do something -specific with the destroyed object omit the third parameter. -The destruction dependencies will be persisted between deployments and -deserialized from the objects model to murano object. diff --git a/doc/source/admin/appdev-guide/hot_packages.rst b/doc/source/admin/appdev-guide/hot_packages.rst deleted file mode 100644 index 0735626a8..000000000 --- a/doc/source/admin/appdev-guide/hot_packages.rst +++ /dev/null @@ -1,147 +0,0 @@ -.. _hot-packages: - -============ -HOT packages -============ - -.. _compose_package: - -Compose a package -~~~~~~~~~~~~~~~~~ - -Murano is an Application catalog which intends to support applications defined in different formats. As a first step to universality, support of a heat orchestration template was added. -It means that any heat template could be added as a separate application into the Application Catalog. This could be done in two ways: manual and automatic. - -Automatic package composing ---------------------------- - -Before uploading an application into the catalog, it should be prepared and archived. -A Murano command line will do all preparation for you. -Just choose the desired Heat Orchestration Template and perform the following command: - -:: - - murano package-create --template wordpress/template.yaml - -Note, that optional parameters could be specified: - -:--name: an application name, copied from a template by default -:--logo: an application square logo, by default the heat logo will be used -:--description: text information about an application, by default copied from a template -:--author: a name of an application author -:--output: a name of an output file archive to save locally -:--full-name: a fully qualified domain name that specifies exact application location -:--resources-dir: a path to the directory containing application resources - -.. note:: - To performing this command python-muranoclient should be installed in the system - -As the result, an application definition archive will be ready for uploading. - -Manual package composing ------------------------- - -Application package could be composed manually. Follow the 5 steps below. - -* *Step 1. Choose the desired heat orchestration template* - - For this example - `chef-server.yaml `_ - template will be used. - -* *Step 2. Rename it to template.yaml* -* *Step 3. Prepare an application logo (optional step)* - - It could be any picture associated with the application. - -* *Step 4. Create manifest.yaml file* - - All service information about the application is contained here. Specify the following parameters: - - :Format: defines an application definition format; should be set to ``Heat.HOT/1.0`` - :Type: defines a manifest type, should be set to ``Application`` - :FullName: a unique name which will be used to identify the application in Murano Catalog - :Description: text information about an application - :Author: a name of an application author or a company - :Tags: keywords associated with the application - :Logo: a name of a logo file for an application - - Take a look at the example: - - .. code-block:: yaml - - Format: Heat.HOT/1.0 - Type: Application - FullName: com.example.Chef-Server - Name: Chef Server - Description: "Heat template to deploy Open Source CHEF server on a VM" - Author: Kate - Tags: - - hot-based - Logo: logo.png - -* *Step 5. Create a zip archive, containing the specified files:* ``template.yaml``, ``manifest.yaml``, ``logo.png`` - -`Browse` page looks like: - -.. image:: figures/chef_server.png - -The configuration form, where you can enter template parameters, will be generated automatically and looks as follows: - -.. image:: figures/chef_server_form.png - -After filling the form the application is ready to be deployed. - -Hot packages with nested Heat templates ---------------------------------------- - -In Murano HOT packages it is possible to allow Heat nested templates to be -saved and deployed as part of a Murano Heat applications. Such templates -should be placed in package under '/Resources/HotFiles'. Adding additional -templates to a package is optional. When a Heat generated package is being -deployed, if there are any Heat nested templates located in the package under -'/Resources/HotFiles', they are sent to Heat together with the main template -and params during stack creation. - -These nested templates can be referenced by putting the template name into the -``type`` attribute of resource definition, in the main template. This -mechanism then compose one logical stack with these multiple templates. The -following examples illustrate how you can use a custom template to define new -types of resources. These examples use a custom template stored in a -``sub_template.yaml`` file - - .. code-block:: yaml - - heat_template_version: 2015-04-30 - - parameters: - key_name: - type: string - description: Name of a KeyPair - - resources: - server: - type: OS::Nova::Server - properties: - key_name: {get_param: key_name} - flavor: m1.small - image: ubuntu-trusty - -Use the template filename as type ---------------------------------- - -The following main template defines the ``sub_template.yaml`` file as value for -the type property of a resource - - .. code-block:: yaml - - heat_template_version: 2015-04-30 - - resources: - my_server: - type: sub_template.yaml - properties: - key_name: my_key - -.. note:: - This feature is supported Liberty onwards. \ No newline at end of file diff --git a/doc/source/admin/appdev-guide/multi_region.rst b/doc/source/admin/appdev-guide/multi_region.rst deleted file mode 100644 index 4f6c0a502..000000000 --- a/doc/source/admin/appdev-guide/multi_region.rst +++ /dev/null @@ -1,148 +0,0 @@ -.. _multi_region: - -Multi-region application -~~~~~~~~~~~~~~~~~~~~~~~~ - -Since Newton release, Murano supports multi-region application deployment. -All MuranoPL resource classes are inherited from the -``io.murano.CloudResource`` class. -An application developer can set a custom region for ``CloudResource`` -subclasses deployment. - -Set a region for resources --------------------------- - -**To set a region for resources:** - -#. Specify a region for ``CloudResource`` subclasses deployment - through the ``regionName`` property. For example: - - .. code-block:: yaml - - Application: - ?: - type: com.example.apache.ApacheHttpServer - enablePHP: $.appConfiguration.enablePHP - - ... - - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - regionName: 'CustomRegion' - - ... - -#. Retrieve ``io.murano.CloudRegion`` objects: - - .. code-block:: yaml - - $region: $.instance.getRegion() - $regionName: $region.name - $regionLocalStack: $region.stack - $regionDefaultNetworks: $region.defaultNetworks - - -As a result, all region-local properties are moved from the ``io.murano.Environment`` -class to the new :ref:`cloud-region` class. -For backward compatibility, the ``io.murano.Environment`` class stores -region-specific properties of default region, except the ``defaultNetworks`` -in its own properties. -The ``Environment::defaultNetworks`` property contains templates for -the ``CloudRegion::defaultNetworks`` property. - -Through current UI, you cannot select networks, flavor, images -and availability zone from a non-default region. -We suggest using regular text fields to specify region-local resources. - -Networking and multi-region applications ----------------------------------------- - -By default, each region has its own separate network. -To ensure connectivity between the networks, create and configure networks in regions -before deploying the application and use ``io.murano.resources.ExistingNeutronNetwork`` -to connect the instance to an existing network. -Example: - -.. code-block:: yaml - - Application: - ?: - type: application.fully.qualified.Name - - ... - - instance_in_region1: - ?: - type: io.murano.resources.LinuxMuranoInstance - regionName: 'CustomRegion1' - networks: - useEnvironmentNetwork: false - useFlatNetwork: false - customNetworks: - - ?: - type: io.murano.resources.ExistingNeutronNetwork - regionName: 'CustomRegion1' - internalNetworkName: 'internalNetworkNameInCustomRegion1' - internalSubnetworkName: 'internalSubNetNameInCustomRegion1' - - instance_in_region2: - ?: - type: io.murano.resources.LinuxMuranoInstance - regionName: 'CustomRegion2' - networks: - useEnvironmentNetwork: false - useFlatNetwork: false - customNetworks: - - ?: - type: io.murano.resources.ExistingNeutronNetwork - regionName: 'CustomRegion2' - internalNetworkName: 'internalNetworkNameInCustomRegion2' - internalSubnetworkName: 'internalSubNetNameInCustomRegion2' - - ... - -Also, you can configure networks with the same name and use a template -for the region networks. -That is, describe ``io.murano.resources.ExistingNeutronNetwork`` only once -and assign it to the ``Environment::defaultNetworks::environment`` property. -The environment will create ``Network`` objects for regions from the -``ExistingNeutronNetwork`` template. -Example: - -.. code-block:: console - - OS_REGION_NAME="RegionOne" openstack network create - OS_REGION_NAME="RegionTwo" openstack network create - - # configure subnets - #... - - # add ExistingNeutronNetwork to environment object model - murano environment-create --join-net-id - - # also it is possible to specify subnet from - murano environment-create --join-net-id --join-subnet-id - - -Additionally, consider the ``[networking]`` section in the configuration -file. -Currently, ``[networking]`` settings are common for all regions. - -.. code-block:: ini - - [networking] - - external_network = %EXTERNAL_NETWORK_NAME% - router_name = %MURANO_ROUTER_NAME% - create_router = true - -If you choose an automatic neutron configuration, configure the external -network with identical names in all regions. -If you disable the automatic router creation, create routers with -identical names in all regions. -Also, the ``default_dns`` address must be reachable from all created networks. - -.. note:: - - To use regions, first configure them as described in :ref:`multi-region`. diff --git a/doc/source/admin/appdev-guide/murano_bundles.rst b/doc/source/admin/appdev-guide/murano_bundles.rst deleted file mode 100644 index bad3569ce..000000000 --- a/doc/source/admin/appdev-guide/murano_bundles.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _murano-bundles: - -============== -Murano bundles -============== - -A bundle is a collection of packages. In the Community App Catalog, you can find -such bundles as ``container-based-apps``, ``app-servers``, and so on. -The packages in the Application Catalog are sorted by usage. You can import -bundles from the catalog using Dashboard or CLI. You can read about this in -:ref:`Managing applications ` and :ref:`Using CLI `. -Specific information about *bundle-import* command can be found at -:ref:`Murano command-line client `. - -Bundle structure -~~~~~~~~~~~~~~~~ - -Bundle description is a JSON structure, that contains list of packages -in the bundle and bundle version. Here is the example: - - .. code-block:: javascript - - { - "Packages": [ - { - "Name": "com.example.apache.ApacheHttpServer", - "Version": "" - }, - { - "Name": "com.example.apache.Tomcat", - "Version": "" - } - ], - "Version": 1 - } - - .. - -``Name`` is a required parameter and should contain package fully qualified name. -``Version`` is not a mandatory parameter. Version for package entry specifies the -version of the package to look into :ref:`Murano package repository `. -If it is specified, murano client would look for a file with that version -specification in murano repository (for example ``com.example.MyApp.0.0.1.zip`` -for com.example.MyApp of version 0.0.1). If the version is omitted or left -blank client would search for ``com.example.MyApp.zip``. - -Create local bundle -~~~~~~~~~~~~~~~~~~~ - -However, you may need to create a local bundle. You may need it if you want to -setup your own :ref:`Murano package repository `. To create a new -bundle, perform the following steps: - - #. Navigate to the directory with the target packages. - - #. Create a ``.bundle`` file. List all the required packages in ``Packages`` - section. If needed, specify the bundle version in the ``Version`` section. diff --git a/doc/source/admin/appdev-guide/murano_packages.rst b/doc/source/admin/appdev-guide/murano_packages.rst deleted file mode 100644 index 75d44e6f8..000000000 --- a/doc/source/admin/appdev-guide/murano_packages.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _murano-packages: - -=============== -Murano packages -=============== - -.. toctree:: - :maxdepth: 1 - - muranopackages/package_structure - muranopackages/dynamic_ui - muranopackages/repository diff --git a/doc/source/admin/appdev-guide/murano_pl.rst b/doc/source/admin/appdev-guide/murano_pl.rst deleted file mode 100644 index 387da6c55..000000000 --- a/doc/source/admin/appdev-guide/murano_pl.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _murano-pl: - -================== -MuranoPL Reference -================== - -To develop applications, murano project refers to Murano Programming -Language (MuranoPL). It is represented by easily readable YAML and -YAQL languages. The sections below describe these languages. - -.. toctree:: - :maxdepth: 1 - - murano_pl/yaml - murano_pl/yaql - murano_pl/class_templ - murano_pl/core_lib - murano_pl/reflection - murano_pl/statics - murano_pl/metadata - murano_pl/versioning - murano_pl/actions \ No newline at end of file diff --git a/doc/source/admin/appdev-guide/murano_pl/actions.rst b/doc/source/admin/appdev-guide/murano_pl/actions.rst deleted file mode 100644 index 9033beb7e..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/actions.rst +++ /dev/null @@ -1,133 +0,0 @@ -.. _actions: - -============== -Murano actions -============== - -Murano action is a type of MuranoPL method. The differences from a regular -MuranoPL method are: - -* Action is executed on deployed objects. -* Action execution is initiated by API request, you do not have to call - the method manually. - -So murano action allows performing any operations on objects: - -* Getting information from the VM, like a config that is generated during the - deployment -* VM rebooting -* Scaling - -A list of available actions is formed during the environment deployment. -Right after the deployment is finished, you can call action asynchronously. -Murano engine generates a task for every action. Therefore, the action status -can be tracked. - -.. note:: - Actions may be called against any MuranoPL object, including ``Environment``, - ``Application``, and any other objects. - -.. note:: - Now murano doesn't support big files download during action execution. This is - because action results are stored in murano database and are limited by - approximately 10kb size. - -To mark a method as an action, use ``Scope: Public`` or ``Usage: Action``. -The latter option is deprecated for the package format versions > 1.3 and -occasionally will be no longer supported. Also, you cannot use both -``Usage: Action`` and ``Scope: Session`` in one method. - -The following example shows an action that returns an archive with a -configuration file: - -.. code-block:: yaml - - exportConfig: - Scope: Public - Body: - - $._environment.reporter.report($this, 'Action exportConfig called') - - $resources: new(sys:Resources) - - $template: $resources.yaml('ExportConfig.template') - - $result: $.masterNode.instance.agent.call($template, $resources) - - $._environment.reporter.report($this, 'Got archive from Kubernetes') - - Return: new(std:File, base64Content => $result.content, - filename => 'application.tar.gz') - -List of available actions can be found with environment details or application -details API calls. It's located in object model special data. -Take a look at the following example: - -Request: -``http://localhost:8082/v1/environments//services/`` - -Response: - -.. code-block:: json - - { - "name": "SimpleVM", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "SimpleApp" - }, - "type": "com.example.Simple", - "id": "e34c317a-f5ee-4f3d-ad2f-d07421b13d67", - "_actions": { - "e34c317a-f5ee-4f3d-ad2f-d07421b13d67_exportConfig": { - "enabled": true, - "name": "exportConfig" - } - } - } - } - - -============== -Static actions -============== - -Static methods (:ref:`static_methods_and_properties`) can also be called -through the API if they are exposed by specifying ``Scope: Public``, and the -result of its execution will be returned. - -Consider the following example of the static action that makes use both of -static class property and user's input as an argument: - -.. code-block:: yaml - - Name: Bar - - Properties: - greeting: - Usage: Static - Contract: $.string() - Default: 'Hello, ' - - Methods: - staticAction: - Scope: Public - Usage: Static - Arguments: - - myName: - Contract: $.string().notNull() - Body: - - Return: concat($.greeting, $myName) - -Request: -``http://localhost:8082/v1/actions`` - -Request body: - -.. code-block:: json - - { - "className": "ns.Bar", - "methodName": "staticAction", - "parameters": {"myName": "John"} - } - -Responce: - -.. code-block:: json - - "Hello, John" diff --git a/doc/source/admin/appdev-guide/murano_pl/class_templ.rst b/doc/source/admin/appdev-guide/murano_pl/class_templ.rst deleted file mode 100644 index d8fca9e1c..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/class_templ.rst +++ /dev/null @@ -1,702 +0,0 @@ -.. _class_templ: - -Common class structure -~~~~~~~~~~~~~~~~~~~~~~ - -Here is a common template for class declarations. Note, that it is in the YAML -format. - -.. code-block:: yaml - :linenos: - - Name: class name - Namespaces: namespaces specification - Extends: [list of parent classes] - Properties: properties declaration - Methods: - methodName: - Arguments: - - list - - of - - arguments - Body: - - list - - of - - instructions - -Thus MuranoPL class is a YAML dictionary with predefined key names, all keys except -for ``Name`` are optional and can be omitted (but must be valid if specified). - -Class name ----------- - -Class names are alphanumeric names of the classes. Traditionally, all class names -begin with an upper-case letter symbol and are written in PascalCasing. - -In MuranoPL all class names are unique. At the same time, MuranoPL -supports namespaces. So, in different namespaces you can have classes -with the same name. You can specify a namespace explicitly, like -`ns:MyName`. If you omit the namespace specification, ``MyName`` is -expanded using the default namespace ``=:``. Therefore, ``MyName`` -equals ``=:MyName`` if ``=`` is a valid namespace. - -Namespaces ----------- - -Namespaces declaration specifies prefixes that can be used in the class body -to make long class names shorter. - -.. code-block:: yaml - - Namespaces: - =: io.murano.services.windows - srv: io.murano.services - std: io.murano - -In the example above, the ``srv: Something`` class name is automatically -translated to ``io.murano.services.Something``. - -``=`` means the current namespace, so that ``MyClass`` means -``io.murano.services.windows.MyClass``. - -If the class name contains the period (.) in its name, then it is assumed -to be already fully namespace qualified and is not expanded. -Thus ``ns.Myclass`` remains as is. - - -.. note:: - To make class names globally unique, we recommend specifying a developer's - domain name as a part of the namespace. - -Extends -------- - -MuranoPL supports multiple inheritance. If present, the ``Extends`` section -shows base classes that are extended. If the list consists of a single entry, -then you can write it as a scalar string instead of an array. If you -do not specify any parents or omit the key, then the class extends -``io.murano.Object``. Thus, ``io.murano.Object`` is the root class -for all class hierarchies. - -.. _class_props: - - -Properties ----------- - -Properties are class attributes that together with methods create public -class interface. Usually, but not always, properties are the values, and -reference other objects that have to be entered in an environment -designer prior to a workflow invocation. - -Properties have the following declaration format: - -.. code-block:: yaml - - propertyName: - Contract: property contract - Usage: property usage - Default: property default - -Contract -++++++++ - -Contract is a YAQL expression that says what type of the value is expected for -the property as well as additional constraints imposed on a property. Using -contracts you can define what value can be assigned to a property or argument. -In case of invalid input data it may be automatically transformed to confirm -to the contract. For example, if bool value is expected and user passes any -not null value it will be converted to ``True``. If converting is impossible -exception ``ContractViolationException`` will be raised. - -The following contracts are available: - -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| Operation | Definition | -+===========================================================+=================================================================================================+ -| | $.int() | | an integer value (may be null). String values consisting of digits are converted to integers | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.int().notNull() | | a mandatory integer | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.string() | | a string. If the value is not a string, it is converted to a string | -| | $.string().notNull() | | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.bool() | | bools are true and false. ``0`` is converted to false, other integers to true | -| | $.bool().notNull() | | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.class(ns:ClassName) | | value must be a reference to an instance of specified class name | -| | $.class(ns:ClassName).notNull() | | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.template(ns:ClassName) | | value must be a dictionary with object-model representation of specified class name | -| | $.template(ns:ClassName).notNull() | | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.class(ns:ClassName, ns:DefaultClassName) | | create instance of the ``ns:DefaultClassName`` class if no instance provided | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.class(ns:Name).check($.p = 12) | | the value must be of the ``ns:Name`` type and have the ``p`` property equal to 12 | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.class(ns:Name).owned() | | a current object must be direct or indirect owner of the value | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.class(ns:Name).notOwned() | | the value must be owned by any object except current one | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | [$.int()] | | an array of integers. Similar to other types. | -| | [$.int().notNull()] | | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | [$.int().check($ > 0)] | | an array of the positive integers (thus not null) | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | [$.int(), $.string()] | | an array that has at least two elements, first is int and others are strings | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | [$.int(), 2] | | an array of ints with at least 2 items | -| | [$.int(), 2, 5] | | an array of ints with at least 2 items, and maximum of 5 items | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | { A: $.int(), B: [$.string()] } | | the dictionary with the ``A`` key of the int type and ``B`` - an array of strings | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $ | | any scalar or data structure as is | -| | [] | | any array | -| | {} | | any dictionary | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | { $.string().notNull(): $.int().notNull() } | | dictionary string -> int | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | A: StringMap | | the dictionary with the ``A`` key that must be equal to ``StringMap``, and other keys be | -| | $.string().notNull(): $ | | any scalar or data structure | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.check($ in $this.myStaticMethod()) | | the value must be equal to one of a member of a list returned by static method of the class | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ -| | $.check($this.myStaticMethod($)) | | the static method of the class must return true for the value | -+-----------------------------------------------------------+-------------------------------------------------------------------------------------------------+ - -In the example below property ``port`` must be int value greater than 0 and -less than 65536; ``scope`` must be a string value and one of 'public', 'cloud', -'host' or 'internal', and ``protocol`` must be a string value and either -'TCP' or 'UDP'. When user passes some values to these properties it will be checked -that values confirm to the contracts. - -.. code-block:: yaml - - Namespaces: - =: io.murano.apps.docker - std: io.murano - - Name: ApplicationPort - - Properties: - port: - Contract: $.int().notNull().check($ > 0 and $ < 65536) - - scope: - Contract: $.string().notNull().check($ in list(public, cloud, host, internal)) - Default: private - - protocol: - Contract: $.string().notNull().check($ in list(TCP, UDP)) - Default: TCP - - Methods: - getRepresentation: - Body: - Return: - port: $.port - scope: $.scope - protocol: $.protocol - - -The ``template`` contract does the same validation as the ``class`` contract, -but does not require the actual object to be passed as a property or argument. -Instead it allows to create an object from the given template later. Also you -can exclude some of the properties from validation and provide them later in -the body of the method. - -Consider the following example: - -.. code-block:: yaml - - Namespaces: - =: io.murano.applications - res: io.murano.resources - std: io.murano - - Name: TemplateServerProvider - - Properties: - template: - Contract: $.template(res:Instance, excludeProperties => [name]).notNull() - serverNamePattern: - Contract: $.string().notNull() - threshold: - Contract: $.int().check($ > 0) - - Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - Body: - - If: $index < $this.threshold - Then: - - $template: $this.template - - $template.name: $this.serverNamePattern.format($index) - - $template['?'].name: format('Server {0}', $index) - - Return: new($template, $owner) - Else: - - Return: null - -In the example above the class has the ``template`` property that is validated -by the ``template`` contract. It holds the template of the object of the -``Instance`` class or its inheritor. In the ``createReplica`` method -``template`` is used to dynamically create instances in runtime considering -some conditions and customizing the ``name`` property of an instance, as it -was excluded from validation. - -You still can pass an actual object to the property or argument with the -``template`` contract, but it will be automatically converted to its object -model representation. - -.. _property_usage: - -Property usage -++++++++++++++ - -Usage states the purpose of the property. This implies who and how can -access it. The following usages are available: - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - | Value - - | Explanation - - * - | In - - | Input property. Values of such properties are obtained from a user - and cannot be modified in MuranoPL workflows. This is the default - value for the Usage key. - - * - | Out - - | A value is obtained from executing MuranoPL workflow and cannot be - modified by a user. - - * - | InOut - - | A value can be modified both by user and by workflow. - - * - | Const - - | The same as ``In`` but once workflow is executed a property cannot be - changed neither by a user nor by a workflow. - - * - | Runtime - - | A property is visible only from within workflows. It is neither read - from input nor serialized to a workflow output. - - * - | Static - - | Property is defined on a class rather than on an instance. - See :ref:`static_methods_and_properties` for details. - - * - | Config - - | A property allows to have per-class configuration. A value is obtained - from the config file rather than from the object model. These config - files are stored in a special folder that is configured in the - ``[engine]`` section of the Murano config file under the - ``class_configs`` key. - -The usage attribute is optional and can be omitted (which implies ``In``). - -If the workflow tries to write to a property that is not declared with -one of the types above, it is considered to be private and accessible -only to that class (and not serialized to output and thus would be -lost upon the next deployment). An attempt to read the property that was -not initialized results in an exception. - - -Default -+++++++ - -Default is a value that is used if the property value is not mentioned in -the input object model, but not when it is set to null. -Default, if specified, must conform to a declared property contract. -If Default is not specified, then null is the default. - -For properties that are references to other classes, Default can modify -a default value of the referenced objects. For example: - -.. code-block:: yaml - - p: - Contract: $.class(MyClass) - Default: {a: 12} - -This overrides default for the ``a`` property of ``MyClass`` for instance -of ``MyClass`` that is created for this property. - -Workflow --------- - -Workflows are the methods that describe how the entities that are -represented by MuranoPL classes are deployed. - -In a typical scenario, the root object in an input data model is of -the ``io.murano.Environment`` type, and has the ``deploy`` method. -This method invocation causes a series of infrastructure activities -(typically, a Heat stack modification) and the deployment scripts -execution initiated by VM agents commands. The role of the workflow -is to map data from the input object model, or a result of previously -executed actions, to the parameters of these activities and to -initiate these activities in a correct order. - - -Methods -------- - -Methods have input parameters, and can return a value to a caller. -Methods are defined in the Workflow section of the class using the -following template:: - - methodName: - Scope: Public - Arguments: - - list - - of - - arguments - Body: - - list - - of - - instructions - -Public is an optional parameter that specifies methods to be executed -by direct triggering after deployment. - - -.. _method_arguments: - -Method arguments -++++++++++++++++ - -Arguments are optional too, and are declared using the same syntax -as class properties. Same as properties, arguments also have contracts and -optional defaults. - -Unlike class properties Arguments may have a different set of Usages: - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - | Value - - | Explanation - - * - | Standard - - | Regular method argument. Holds a single value based on its contract. - This is the default value for the Usage key. - - * - | VarArgs - - | A variable length argument. Method body sees it as a list of values, - each matching a contract of the argument. - - * - | KwArgs - - | A keywrod-based argument, Method body sees it as a dict of values, - with keys being valid keyword strings and values matching a contract - of the argument. - -Arguments example: - -.. code-block:: yaml - - scaleRc: - Arguments: - - rcName: - Contract: $.string().notNull() - - newSize: - Contract: $.int().notNull() - - rest: - Contract: $.int() - Usage: VarArgs - - others: - Contract: $.int() - Usage: KwArgs - -.. method_body: - -Method body -+++++++++++ - -The Method body is an array of instructions that get executed sequentially. -There are 3 types of instructions that can be found in a workflow body: - -* Expressions, -* Assignments, -* Block constructs. - -.. method_usage: - -Method usage -++++++++++++ - -Usage states the purpose of the method. This implies who and how can -access it. The following usages are available: - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - | Value - - | Explanation - - * - | Runtime - - | Normal instance method. - - * - | Static - - | Static method that does not require class instance. - See :ref:`static_methods_and_properties` for details. - - * - | Extension - - | Extension static method that extends some other type. - See :ref:`extension_methods` for details. - - * - | Action - - | Method can be invoked from outside (using Murano API). - This option is deprecated for the package format versions > 1.3 in - favor of ``Scope: Public`` and occasionally will be no longer - supported. - See :ref:`actions` for details. - -The ``Usage`` attribute is optional and can be omitted (which implies -``Runtime``). - -Method scope -++++++++++++ - -The ``Scope`` attribute declares method visibility. It can have two possible -values: - -* `Session` - regular method that is accessible from anywhere in the current - execution session. This is the default if the attribute is omitted; - -* `Public` - accessible anywhere, both within the session and from - outside through the API call. - -The ``Scope`` attribute is optional and can be omitted (which implies -``Session``). - -Expressions -+++++++++++ - -Expressions are YAQL expressions that are executed for their side effect. -All accessible object methods can be called in the expression using -the ``$obj.methodName(arguments)`` syntax. - -+-----------------------------------------+----------------------------------------------------------------+ -| Expression | Explanation | -+=========================================+================================================================+ -| | $.methodName() | | invoke method 'methodName' on this (self) object | -| | $this.methodName() | | -+-----------------------------------------+----------------------------------------------------------------+ -| | $.property.methodName() | | invocation of method on object that is in ``property`` | -| | $this.property.methodName() | | -+-----------------------------------------+----------------------------------------------------------------+ -| | $.method(1, 2, 3) | | methods can have arguments | -+-----------------------------------------+----------------------------------------------------------------+ -| | $.method(1, 2, thirdParameter => 3) | | named parameters also supported | -+-----------------------------------------+----------------------------------------------------------------+ -| | list($.foo().bar($this.property), $p) | | complex expressions can be constructed | -+-----------------------------------------+----------------------------------------------------------------+ - - -Assignment -++++++++++ - -Assignments are single key dictionaries with a YAQL expression as a key -and arbitrary structure as a value. Such a construct is evaluated -as an assignment. - -+------------------------------+---------------------------------------------------------------------------------+ -| Assignment | Explanation | -+==============================+=================================================================================+ -| | $x: value | | assigns ``value`` to the local variable ``$x`` | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x: value | | assign ``value`` to the object's property | -| | $this.x: value | | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x: $.y | | copies the value of the property ``y`` to the property ``x`` | -+------------------------------+---------------------------------------------------------------------------------+ -| | $x: [$a, $b] | | sets ``$x`` to the array of two values: ``$a`` and ``$b`` | -+------------------------------+---------------------------------------------------------------------------------+ -| | $x: | | structures of any level of complexity can be evaluated | -| | SomeKey: | | -| | NestedKey: $variable | | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x[0]: value | | assigns ``value`` to the first array entry of the ``x`` property | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x: $.x.append(value) | | appends ``value`` to the array in the ``x`` property | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x: $.x.insert(1, value) | | inserts ``value`` into position 1 of the array in the ``x`` property | -+------------------------------+---------------------------------------------------------------------------------+ -| | $x: list($a, $b).delete(0) | | sets ``$x`` to the list without the item at index 0 | -+------------------------------+---------------------------------------------------------------------------------+ -| | $.x.key.subKey: value | | deep dictionary modification | -| | $.x[key][subKey]: value | | -+------------------------------+---------------------------------------------------------------------------------+ - - -Block constructs -++++++++++++++++ - -Block constructs control a program flow. They are dictionaries that have -strings as all their keys. - -The following block constructs are available: - -+---------------------------+---------------------------------------------------------------------------------------+ -| Assignment | Explanation | -+===========================+=======================================================================================+ -| | Return: value | | Returns value from a method | -+---------------------------+---------------------------------------------------------------------------------------+ -| | If: predicate() | | ``predicate()`` is a YAQL expression that must be evaluated to ``True`` or ``False``| -| | Then: | | -| | - code | | The ``Else`` section is optional | -| | - block | | One-line code blocks can be written as scalars rather than an array. | -| | Else: | | -| | - code | | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ -| | While: predicate() | | ``predicate()`` must be evaluated to ``True`` or ``False`` | -| | Do: | | -| | - code | | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ -| | For: variableName | | ``collection`` must be a YAQL expression returning iterable collection or | -| | In: collection | evaluatable array as in assignment instructions, for example, ``[1, 2, $x]`` | -| | Do: | | -| | - code | | Inside a code block loop, a variable is accessible as ``$variableName`` | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Repeat: | | Repeats the code block specified number of times | -| | Do: | | -| | - code | | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Break: | | Breaks from loop | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Match: | | Matches the result of ``$valExpression()`` against a set of possible values | -| | case1: | (cases). The code block of first matched case is executed. | -| | - code | | -| | - block | | If no case matched and the default key is present | -| | case2: | than the ``Default`` code block get executed. | -| | - code | | The case values are constant values (not expressions). | -| | - block | | -| | Value: $valExpression() | | -| | Default: | | -| | - code | | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Switch: | | All code blocks that have their predicate evaluated to ``True`` are executed, | -| | $predicate1(): | but the order of predicate evaluation is not fixed. | -| | - code | | -| | - block | | -| | $predicate2(): | | -| | - code | | -| | - block | | -| | Default: | | The ``Default`` key is optional. | -| | - code | | -| | - block | | If no predicate evaluated to ``True``, the ``Default`` code block get executed. | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Parallel: | | Executes all instructions in code block in a separate green threads in parallel. | -| | - code | | -| | - block | | -| | Limit: 5 | | The limit is optional and means the maximum number of concurrent green threads. | -+---------------------------+---------------------------------------------------------------------------------------+ -| | Try: | | Try and Catch are keywords that represent the handling of exceptions due to data | -| | - code | or coding errors during program execution. A ``Try`` block is the block of code in | -| | - block | which exceptions occur. A ``Catch`` block is the block of code, that is executed if | -| | Catch: | an exception occurred. | -| | With: keyError | | Exceptions are not declared in Murano PL. It means that exceptions of any types can | -| | As: e | be handled and generated. Generating of exception can be done with construct: | -| | Do: | ``Throw: keyError``. | -| | - code | | -| | - block | | -| | Else: | | The ``Else`` is optional block. ``Else`` block is executed if no exception occurred.| -| | - code | | -| | - block | | -| | Finally: | | The ``Finally`` also is optional. It's a place to put any code that will | -| | - code | be executed, whether the try-block raised an exception or not. | -| | - block | | -+---------------------------+---------------------------------------------------------------------------------------+ - -Notice, that if you have more than one block construct in your workflow, you -need to insert dashes before each construct. For example:: - - Body: - - If: predicate1() - Then: - - code - - block - - While: predicate2() - Do: - - code - - block - - -.. _object-model: - -Object model ------------- - -Object model is a JSON serialized representation of objects and their -properties. Everything you do in the OpenStack dashboard is reflected -in an object model. The object model is sent to the Application catalog engine -when the user decides to deploy the built environment. On the engine -side, MuranoPL objects are constructed and initialized from the received -Object model, and a predefined method is executed on the root object. - -Objects are serialized to JSON using the following template: - -.. code-block:: json - :linenos: - - { - "?": { - "id": "globally unique object ID (UUID)", - "type": "fully namespace-qualified class name", - - "optional designer-related entries can be placed here": { - "key": "value" - } - }, - - "classProperty1": "propertyValue", - "classProperty2": 123, - "classProperty3": ["value1", "value2"], - - "reference1": { - "?": { - "id": "object id", - "type": "object type" - }, - - "property": "value" - }, - - "reference2": "referenced object id" - } - -Objects can be identified as dictionaries that contain the ``?`` entry. -All system fields are hidden in that entry. - -There are two ways to specify references: - -#. ``reference1`` as in the example above. This method allows inline - definition of an object. When the instance of the referenced object - is created, an outer object becomes its parent/owner that is responsible - for the object. The object itself may require that its parent - (direct or indirect) be of a specified type, like all applications - require to have ``Environment`` somewhere in a parent chain. - -#. Referring to an object by specifying other object ID. That object must - be defined elsewhere in an object tree. Object references distinguished - from strings having the same value by evaluating property contracts. - The former case would have ``$.class(Name)`` while the later - the - ``$.string()`` contract. diff --git a/doc/source/admin/appdev-guide/murano_pl/core_lib.rst b/doc/source/admin/appdev-guide/murano_pl/core_lib.rst deleted file mode 100644 index 75dabe9d6..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/core_lib.rst +++ /dev/null @@ -1,397 +0,0 @@ -.. _core_lib: - -MuranoPL Core Library -~~~~~~~~~~~~~~~~~~~~~ - -Some objects and actions can be used in several application deployments. -All common parts are grouped into MuranoPL libraries. -Murano core library is a set of classes needed in each deployment. -Class names from core library can be used in the application definitions. -This library is located under the `meta `_ -directory. - -Classes included in the Murano core library are as follows: - -**io.murano** - -- :ref:`object` -- :ref:`application` -- :ref:`security-group-manager` -- :ref:`environment` -- :ref:`cloud-region` - -**io.murano.resources** - -- :ref:`instance` -- :ref:`network` - -**io.murano.system** - -- :ref:`logger` -- :ref:`status-reporter` - - -.. _object: - -Class: Object -------------- - -A parent class for all MuranoPL classes. It implements the ``initialize``, -``setAttr``, and ``getAttr`` methods defined in the pythonic part of the Object class. -All MuranoPL classes are implicitly inherited from this class. - -.. seealso:: - - Source `Object.yaml - `_ - file. - - - -.. _application: - -Class: Application ------------------- - -Defines an application itself. All custom applications must be derived from -this class. - -.. seealso:: - - Source `Application.yaml - `_ - file. - - -.. _security-group-manager: - -Class: SecurityGroupManager ---------------------------- - -Manages security groups during an application deployment. - -.. seealso:: - - Source `SecurityGroupManager.yaml - `_ - file. - - -.. _cloud-region: - -Class: CloudRegion ------------------- - -Defines a CloudRegion and groups region-local properties - -.. list-table:: **CloudRegion class properties** - :widths: 10 35 7 - :header-rows: 1 - - * - Property - - Description - - Default usage - * - ``name`` - - A region name. - - ``In`` - * - ``agentListener`` - - A property containing the ``io.murano.system.AgentListener`` object - that can be used to interact with Murano Agent. - - ``Runtime`` - * - ``stack`` - - A property containing a HeatStack object that can be used to interact - with Heat. - - ``Runtime`` - * - ``defaultNetworks`` - - A property containing user-defined Networks - (``io.murano.resources.Network``) that can be used as default networks - for the instances in this environment. - - ``In`` - * - ``securityGroupManager`` - - A property containing the ``SecurityGroupManager`` object that can - be used to construct a security group associated with this environment. - - ``Runtime`` - - -.. seealso:: - - Source `CloudRegion.yaml - `_ - file. - -.. _environment: - -Class: Environment ------------------- - -Defines an environment in terms of the deployment process and -groups all Applications and their related infrastructures. It also able -to deploy them at once. - -Environments is intent to group applications to manage them easily. - -.. list-table:: **Environment class properties** - :widths: 10 35 7 - :header-rows: 1 - - * - Property - - Description - - Default usage - * - ``name`` - - An environment name. - - ``In`` - * - ``applications`` - - A list of applications belonging to an environment. - - ``In`` - * - ``agentListener`` - - A property containing the ``io.murano.system.AgentListener`` object - that can be used to interact with Murano Agent. - - ``Runtime`` - * - ``stack`` - - A property containing a HeatStack object in default region that can - be used to interact with Heat. - - ``Runtime`` - * - ``instanceNotifier`` - - A property containing the ``io.murano.system.InstanceNotifier`` object - that can be used to keep track of the amount of deployed instances. - - ``Runtime`` - * - ``defaultNetworks`` - - A property containing templates for user-defined Networks in regions - (``io.murano.resources.Network``). - - ``In`` - * - ``securityGroupManager`` - - A property containing the ``SecurityGroupManager`` object from default region - that can be used to construct a security group associated with this environment. - - ``Runtime`` - * - ``homeRegionName`` - - A property containing the name of home region from `murano` config - - ``Runtime`` - * - ``regions`` - - A property containing the map `regionName` -> `CloudRegion` instance. - - ``InOut`` - * - ``regionConfigs`` - - A property containing the map `regionName` -> `CloudRegion` config - - ``Config`` - -.. seealso:: - - Source `Environment.yaml - `_ - file. - - -.. _instance: - -Class: Instance ---------------- - -Defines virtual machine parameters and manages an instance lifecycle: spawning, -deploying, joining to the network, applying security group, and deleting. - -.. list-table:: **Instance class properties** - :widths: 10 35 7 - :header-rows: 1 - - * - Property - - Description - - Default usage - * - ``regionName`` - - Inherited from ``CloudResource``. Describe region for instance deployment - - ``In`` - * - ``name`` - - An instance name. - - ``In`` - * - ``flavor`` - - An instance flavor defining virtual machine hardware parameters. - - ``In`` - * - ``image`` - - An instance image defining operation system. - - ``In`` - * - ``keyname`` - - Optional. A key pair name used to connect easily to the instance. - - ``In`` - * - ``agent`` - - Configures interaction with the Murano agent using - ``io.murano.system.Agent``. - - ``Runtime`` - * - ``ipAddresses`` - - A list of all IP addresses assigned to an instance. Floating ip address - is placed in the list tail if present. - - ``Out`` - * - ``networks`` - - Specifies the networks that an instance will be joined to. - Custom networks that extend :ref:`Network class ` can be - specified. An instance will be connected to them and for the default - environment network or flat network if corresponding values are set - to ``True``. Without additional configuration, instance will be joined - to the default network that is set in the current environment. - - ``In`` - * - ``volumes`` - - Specifies the mapping of a mounting path to volume implementations - that must be attached to the instance. Custom volumes that extend - ``Volume`` class can be specified. - - ``In`` - * - ``blockDevices`` - - Specifies the list of block device mappings that an instance will use - to boot from. Each mapping defines a volume that must be an instance of - ``Volume`` class, device name, device type, and boot order. - Either the ``blockDevices`` property or ``image`` property must be - specified in order to boot an instance - - ``In`` - * - ``assignFloatingIp`` - - Determines if floating IP is required. Default is ``False``. - - ``In`` - * - ``floatingIpAddress`` - - IP addresses assigned to an instance after an application deployment. - - ``Out`` - * - ``securityGroupName`` - - Optional. A security group that an instance will be joined to. - - ``In`` - -.. seealso:: - - Source `Instance.yaml - `_ - file. - - -.. _instance-resources: - -Resources -+++++++++ - -Instance class uses the following resources: - -**Agent-v2.template** - Python Murano Agent template. - - .. note:: - - This agent is supposed to be unified. Currently, only Linux-based - machines are supported. Windows support will be added later. - -**linux-init.sh** - Python Murano Agent initialization script that sets up an agent with - valid information containing an updated agent template. - -**Agent-v1.template** - Windows Murano Agent template. - -**windows-init.sh** - Windows Murano Agent initialization script. - - -.. _network: - -Class: Network --------------- - -The basic abstract class for all MuranoPL classes representing networks. - -.. seealso:: - - Source `Network.yaml - `_ - file. - -.. _logger: - -Class: Logger -------------- - -Logging API is the part of core library since Liberty release. It was -introduced to improve debuggability of MuranoPL programs. - -You can get a logger instance by calling a ``logger`` function which -is located in ``io.murano.system`` namespace. The ``logger`` function takes -a logger name as the only parameter. It is a common recommendation to use full -class name as a logger name within that class. This convention avoids names -conflicts in logs and ensures a better logging subsystem configurability. - -Logger class instantiation: - -.. code-block:: yaml - - $log: logger('io.murano.apps.activeDirectory.ActiveDirectory') - - -.. list-table:: **Log levels prioritized in order of severity** - :widths: 10 35 - :header-rows: 1 - - * - Level - - Description - * - CRITICAL - - Very severe error events that will presumably lead the application - to abort. - * - ERROR - - Error events that might not prevent the application from running. - * - WARNING - - Events that are potentially harmful but will allow the application - to continue running. - * - INFO - - Informational messages highlighting the progress of the application - at the coarse-grained level. - * - DEBUG - - Detailed informational events that are useful when debugging an - application. - * - TRACE - - Even more detailed informational events comparing to the DEBUG level. - -There are several methods that fully correspond to the log levels you can use -for logging events. They are ``debug``, ``trace``, ``info``, ``warning``, -``error``, and ``critical``. - -Logging example: - -.. code-block:: yaml - - $log.info('print my info message {message}', message=>message) - -Logging methods use the same format rules as the YAQL :command:`format` -function. Thus the line above is equal to the: - -.. code-block:: yaml - - $log.info('print my info message {message}'.format(message=>message)) - -To print an exception stacktrace, use the :command:`exception` method. -This method uses the ERROR level: - -.. code-block:: yaml - - Try: - - Throw: exceptionName - Message: exception message - Catch: - With: exceptionName - As: e - Do: - - $log.exception($e, 'something bad happen "{message}"', message=>message) - -.. note:: - You can configure the logging subsystem through the ``logging.conf`` file - of the Murano Engine. - -.. seealso:: - - * Source `Logger.yaml - `_ - file. - - * `OpenStack networking logging - configuration `_. - -.. _status-reporter: - -Class: StatusReporter ---------------------- - -Provides feedback feature. To follow the deployment process in the UI, all status changes should be included -in the application configuration. - -.. seealso:: - - Source `StatusReporter.yaml - `_ - file. diff --git a/doc/source/admin/appdev-guide/murano_pl/metadata.rst b/doc/source/admin/appdev-guide/murano_pl/metadata.rst deleted file mode 100644 index 474ce1356..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/metadata.rst +++ /dev/null @@ -1,318 +0,0 @@ -.. _metadata: - -MuranoPL Metadata -~~~~~~~~~~~~~~~~~ - -MuranoPL metadata is a way to attach additional information to various MuranoPL -entities such as classes, packages, properties, methods, and method arguments. -That information can be used by both applications (to implement dynamic -programming techniques) or by the external callers (API consumers like UI or -even by the Murano Engine itself to impose some runtime behavior based on -well known meta values). Thus, metadata is a flexible alternative to adding new -keyword for every new feature. - -Work with metadata includes the following cases: - -* Defining your own metadata classes -* Attaching metadata to various parts of MuranoPL code -* Obtaining metadata and its usage - -Define metadata classes ------------------------ - -Define MuranoPL class with the description of arbitrary metadata. The class -that can be used as metadata differs from the regular class: - -* The ``Usage`` attribute of the former equals to ``Meta``, while the ``Usage`` - attribute of the latter equals to ``Class``. The default value of the - ``Usage`` attribute is ``Class``. - -* Metadata class has additional attributes (``Cardinality``, ``Applies`` and - ``Inherited``) to control how and where instances of that class can be - attached. - -Cardinality -+++++++++++ - -The ``Cardinality`` attribute can be set to either ``One`` or ``Many`` and -indicates the possibility to attach two or more instances of metadata to a -single language entity. The default value is ``One``. - -Applies -+++++++ - -The ``Applies`` attribute can be set to one of ``Package``, ``Type``, -``Method``, ``Property``, ``Argument`` or ``All`` and controls the possible -language entities which instances of metadata class can be attached to. It is -possible to specify several values using YAML list notation. The default value -is ``All``. - -Inherited -+++++++++ - -The ``Inherited`` attribute can be set to ``true`` or ``false`` and specifies -if there is metadata retained for child classes, overridden methods and -properties. The default value is ``false``. - -Using of ``Inherited: true`` has the following consequences. - -If some class inherits from two classes with the same metadata attached and -this metadata has ``Cardinality: One``, it will lead to emerging of two -metadata objects with ``Cardinality: One`` within a single entity and will -throw an exception. However, if the child class has this metadata attached -explicitly, it will override the inherited metas and there is no conflict. - -If the child class has the same meta as its parent (attached explicitly), -then in case of ``Cardinatity: One`` the meta of the child overrides the -meta of the parent as it is mentioned above. And in case of -``Cardinatity: Many`` meta of the parent is added to the list of the child's -metas. - -Example -+++++++ - -The following example shows a simple meta-class implementation: - -.. code-block:: yaml - - Name: MetaClassOne - Usage: Meta - Cardinality: One - Applies: All - - Properties: - description: - Contract: $.string() - Default: null - - count: - Contract: $.int().check($ >= 0) - Default: 0 - -``MetaClassOne`` is defined as a metadata class by setting the ``Usage`` -attribute to ``Meta``. The ``Cardinality`` and ``Applies`` attributes determine -that only one instance of ``MetaClassOne`` can be attached to object of any -type. The ``Inherited`` attribute is omitted so there is no metadata -retained for child classes, overridden methods and properties. In the -example above, ``Cardinality`` and ``Applies`` can be omitted as well, as -their values are set to default but in this case the author wants to be -explicit. - -The following example shows metadata class with different values of attributes: - -.. code-block:: yaml - - Name: MetaClassMany - Usage: Meta - Cardinality: Many - Applies: [Property, Method] - Inherited: true - - Properties: - description: - Contract: $.string() - Default: null - - count: - Contract: $.int().check($ >= 0) - Default: 0 - -An instance (or several instances) of ``MetaClassMany`` can be attached to -either property or method. Overridden methods and properties inherit -metadata from its parents. - -Attach metadata to a MuranoPL entity ------------------------------------- - -To attach metadata to MuranoPL class, package, property, method or method -argument, add the ``Meta`` keyword to its description. Under the -description, specify a list of metadata class instances which you want to -attach to the entity. To attach only one metadata class instance, use a single -scalar instead of a list. - -Consider the example of attaching previously defined metadata to different -entities in a class definition: - -.. code-block:: yaml - - Namespaces: - =: io.murano.bar - std: io.murano - res: io.murano.resources - sys: io.murano.system - - - Name: Bar - - Extends: std:Application - - Meta: - MetaClassOne: - description: "Just an empty application class with some metadata" - count: 1 - - Properties: - name: - Contract: $.string().notNull() - Meta: - - MetaClassOne: - description: "Name of the app" - count: 1 - - MetaClassMany: - count: 2 - - MetaClassMany: - count: 3 - - Methods: - initialize: - Body: - - $._environment: $.find(std:Environment).require() - Meta: - MetaClassOne: - description: "Method for initializing app" - count: 1 - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $._environment.reporter.report($this, 'Deploy started') - - $._environment.reporter.report($this, 'Deploy finished') - - $.setAttr(deployed, true) - -The ``Bar`` class has an instance of metadata class ``MetaClassOne`` attached. -For this, the ``Meta`` keyword is added to the ``Bar`` class description and -the instance of the ``MetaClassOne`` class is specified under it. This -instance's properties are ``description`` and ``count``. - -There are three meta-objects attached to the ``name`` property of the ``Bar`` -class. One of it is a ``MetaclassOne`` object and the other two are -``MetaClassMany`` objects. There can be more than one instance of -``MetaClassMany`` attached to a single entity since the ``Cardinality`` -attribute of ``MetaClassMany`` is set to ``Many``. - -The ``initialize`` method of ``Bar`` also has its metadata. - -To attach metadata to the package, add the ``Meta`` keyword to -``manifest.yaml`` file. - -Example: - -.. code-block:: yaml - - Format: 1.0 - Type: Application - FullName: io.murano.bar.Bar - Name: Bar - Description: | - Empty Description - Author: author - Tags: [bar] - Classes: - io.murano.bar.Bar: Bar.yaml - io.murano.bar.MetaClassOne: MetaClassOne.yaml - io.murano.bar.MetaClassMany: MetaClassMany.yaml - Supplier: - Name: Name - Description: Description - Summary: Summary - Meta: - io.murano.bar.MetaClassOne: - description: "Just an empty application with some metadata" - count: 1 - -Obtain metadata in runtime --------------------------- - -Metadata can be accessed from MuranoPL using reflection capabilities and -from Python code using existing YAQL mechanism. - -The following example shows how applications can access attached metadata: - -.. code-block:: yaml - - Namespaces: - =: io.murano.bar - std: io.murano - res: io.murano.resources - sys: io.murano.system - - Name: Bar - - Extends: std:Application - - Meta: - MetaClassOne: - description: "Just an empty application class with some metadata" - - Methods: - sampleAction: - Scope: Public - Body: - - $._environment.reporter.report($this, typeinfo($).meta. - where($ is MetaClassOne).single().description) - -The ``sampleAction`` method is added to the ``Bar`` class definition. This -makes use of metadata attached to the ``Bar`` class. - -The information about the ``Bar`` class is received by calling the -``typeinfo`` function. Then metadata is accessed through the ``meta`` -property which returns the collection of all meta attached to the property. -Then it is checked that the meta is a ``MetaClassOne`` object to ensure that -it has ``description``. While executing the action, the phrase "Just an -empty application class with some metadata" is reported to a log. Some -advanced usages of MuranoPL reflection capabilities can be found in the -corresponding section of this reference. - -By using metadata, an application can get information of any type attached -to any object and use this information to change its own behavior. The most -valuable use-cases of metadata can be: - -* Providing information about capabilities of application and its parts -* Setting application requirements - -Capabilities can include version of software, information for use in UI or -CLI, permissions, and any other. Metadata can also be used in requirements as -a part of the contract. - -The following example demonstrates the possible use cases for the metadata: - -.. code-block:: yaml - - Name: BlogApp - - Meta: - m:SomeFeatureSupport: - support: true - - Properties: - volumeName: - Contract: $.string().notNull() - Meta: - m:Deprecated: - text: "volumeName property is deprecated" - server: - Contract: $.class(srv:CoolServer).notNull().check(typeinfo($).meta. - where($ is m:SomeFeatureSupport and $.support = true).any()) - - Methods: - importantAction: - Scope: Public - Meta: - m:CallerMustBeAdmin - -Note, that the classes in the example do not exist as of Murano Mitaka, and -therefore the example is not a real working code. - -The ``SomeFeatureSupport`` metadata with ``support: true`` says that the -``BlogApp`` application supports some feature. The ``Deprecated`` metadata -attached to the ``volumeName`` property informs that this -property has a better alternative and it will not be used in the future -versions anymore. The ``CallerMustBeAdmin`` metadata attached to the -``importantAction`` method sets permission to execute this method to the -admin users only. - -In the contract of the ``server`` property it is specified that the server -application must be of the ``srv:CoolServer`` class and must have the -attached meta-object of the ``m:SomeFeatureSupport`` class with the -``support`` property set to ``true``. diff --git a/doc/source/admin/appdev-guide/murano_pl/reflection.rst b/doc/source/admin/appdev-guide/murano_pl/reflection.rst deleted file mode 100644 index e628c84f4..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/reflection.rst +++ /dev/null @@ -1,269 +0,0 @@ -.. _reflection: - -Reflection capabilities in MuranoPL. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Reflection provides objects that describes MuranoPL classes and packages. - -The first important function is ``typeinfo`` . Usage: - -.. code-block:: yaml - - $typeInfo: typeinfo($someObject) - - - -Now ``$typeInfo`` variable contains instance of type of ``$someObject`` (``MuranoClass`` instance). - -MuranoPL provide following abilities to reflection: - - -.. _types_reflection: - -Types ------ - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Property - - Description - * - ``name`` - - name of MuranoPL class - * - ``version`` - - version (`SemVer`_) of MuranoPL class. - * - ``ancestors`` - - list of class ancestors - * - ``properties`` - - list of class properties. See :ref:`properties_reflection` - * - ``package`` - - package information. See :ref:`package_reflection` - * - ``methods`` - - list of methods. See :ref:`methods_reflection` - * - ``type`` - - reference to type, which can be used as argument in engine functions - - - -*Example* - -.. code-block:: yaml - - - $typeInfo: typeinfo($) - ... - # log name, version and package name of this class - - $log.info("This is "{class_name}/{version} from {package}", - class_name => $typeInfo.name, - version => str($typeInfo.version), - package => $typeInfo.package.name)) - - $log.info("Ancestors:") - - For: ancestor - In: $typeInfo.ancestors - Do: - #log all ancestors names - - $log.info("{ancestor_name}", ancestor_name => $ancestor.name) - # log full class version - - $log.info("{version}", version => str($typeInfo.version)) - # create object with same class - - $newObject = new($typeInfo.type) - - -.. _properties_reflection: - -Properties ----------- - - -Property introspection -++++++++++++++++++++++ - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Property - - Description - * - ``name`` - - name of property - * - ``hasDefault`` - - boolean value. `True`, if property has default value, `False` otherwise - * - ``usage`` - - `Usage` property's field. See :ref:`property_usage` for details - * - ``declaringType`` - - type - owner of declared property - - -Property access -+++++++++++++++ - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Methods - - Description - * - ``$property.setValue($target, $value)`` - - set value of ``$property`` for object ``$target`` to ``$value`` - * - ``$property.getValue($target)`` - - get value of ``$property`` for object ``$target`` - -*Example* - -.. code-block:: yaml - - - $typeInfo: typeinfo($) - ... - # select first property - - $selectedPropety: $typeInfo.properties.first() - # log property name - - $log.info("Hi, my name is {p_name}, p_name => $selectedProperty.name) - # set new property value - - $selectedProperty.setValue($, "new_value") - # log new property value using reflection - - $log.info("My new value is {value}", value => $selectedProperty.getValue($)) - # also, if property static, $target can be null - - $log.info("Static property value is {value}, - value => $staticProperty.getValue(null)) - - - -.. _package_reflection: - -Packages --------- - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Property - - Description - * - ``types`` - - list of types, declared in package - * - ``name`` - - package name - * - ``version`` - - package version - - -*Example* - -.. code-block:: yaml - - - $typeInfo: typeinfo($) - ... - - $packageRef: $typeInfo.package - - $log.info("This is package {p_name}/{p_version}", - p_name => $packageRef.name, - p_version => str($packageRef.version)) - - $log.info("Types in package:") - - For: type_ - In: $packageRef.types - Do: - - $log.info("{typename}", typename => type_.name) - - -.. _methods_reflection: - -Methods -------- - -Methods properties -++++++++++++++++++ - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Property - - Description - * - ``name`` - - method's name - * - ``declaringType`` - - type - owner of declared method - * - ``arguments`` - - list of method's arguments. See :ref:`arguments_reflection` - - -Method invoking -+++++++++++++++ - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Methods - - Description - * - ``$method.invoke($target, $arg1, ... $argN, kwarg1 => value1, ..., kwargN => valueN)`` - - call ``$target``'s method $method with ``$arg1``, ..., ``$argN`` positional arguments and ``kwarg1``, .... ``kwargN`` named arguments - -*Example* - -.. code-block:: yaml - - - $typeInfo: typeinfo($) - ... - # select single method by name - - $selectedMethod: $typeInfo.methods.where($.name = sampleMethodName).single() - # log method name - - $log.info("Method name: {m_name}", m_name => $selectedMethod.name) - # log method arguments names - - For: argument - In: $selectedMethod.arguments - Do: - - $log.info("{name}", name => $argument.name) - # call method with positional argument 'bar' and named `baz` == 'baz' - - $selectedMethod.invoke($, 'bar', baz => baz) - - -.. _arguments_reflection: - -Method arguments ----------------- - -.. list-table:: - :header-rows: 1 - :widths: 20 80 - :stub-columns: 0 - :class: borderless - - * - Property - - Description - * - ``name`` - - argument's name - * - ``hasDefault`` - - `True` if argument has default value, `False` otherwise - * - ``declaringMethod`` - - method - owner of argument - * - ``usage`` - - argument's usage type. See :ref:`method_arguments` for details - -.. code-block:: yaml - - - $firstArgument: $selectedMethod.arguments.first() - # store argument's name - - $argName: $firstArgument.name - # store owner's name - - $methodName: $firstArgument.declaringMethod.name - - $log.info("Hi, my name is {a_name} ! My owner is {m_name}", - a_name => $argName, - m_name => $methodName) - - -.. Links: -.. _`SemVer`: http://semver.org - - diff --git a/doc/source/admin/appdev-guide/murano_pl/statics.rst b/doc/source/admin/appdev-guide/murano_pl/statics.rst deleted file mode 100644 index 93b970ae8..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/statics.rst +++ /dev/null @@ -1,183 +0,0 @@ -.. _static_methods_and_properties: - -Static methods and properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In MuranoPL, static denotes class methods and class properties (as opposed to -instance methods and instance properties). These methods and properties can be -accessed without an instance present. - -Static methods are often used for helper methods that are not bound to any object -(that is, do not maintain a state) or as a convenient way to write a class factory. - -Type objects ------------- - -Usually static methods and properties are accessed using `type object`. That -is, an object that represents the class rather than class instance. - -For any given class `foo.Bar` its type object may be retrieved using -any of the following ways: - -* Using ``ns:Bar`` notation considering that `ns` is declared in `Namespaces` - section (and it is `foo` in this case), -* Using ``:Bar`` syntax if `Bar` is in the current namespace (that is, what - ``=:Bar`` would mean if ``=`` was a valid namespace prefix), -* Using ``type()`` function with a fully qualified class name: ``type('foo.Bar')``, -* By obtaining a type of class instance: ``type($object)`` (available for - packages with format version starting from `1.3`), -* Through reflection: ``typeinfo($object).type``. - -No matter what method was used to get type object, the returned object will -be the same because there can be only one type object per class. - -All functions that accept type name, for example ``new()`` function, also -accept type objects. - - -Accessing static methods and properties ---------------------------------------- - -Static methods can be invoked using one of the two ways: - - * Using `type object`: ``ns:Bar.foo(arg)``, ``:Bar.foo(arg)``, and so on, - * On a class instance similar to normal methods: ``$obj.foo(arg)``. - -Access to properties is similar to that: - * Using `type object`: ``ns:Bar.property``, ``:Bar.property``, and so on, - * On a class instance: ``$obj.property``. - -Static properties are defined on a class rather than on an instance. -Therefore, their values will be the same for all class instances (for -particular version of the class). - - -Declaration of static methods and properties --------------------------------------------- - -Methods and properties are declared to be static by specifying -``Usage: Static`` on them. - -For example: - -.. code-block:: yaml - - Properties: - property: - Contract: $.string() - Usage: Static - - Methods: - foo: - Usage: Static - Body: - - Return: $.property - -Static properties are never initialized from object model but can be modified -from within MuranoPL code (i.e. they are not immutable). -Static methods also can be executed as an action from outside using -``Scope: Public``. Within static method `Body` ``$this`` (and ``$`` if not -set to something else in expression) are set to type object rather than to -instance, as it is for regular methods. - - -Static methods written in Python --------------------------------- - -For MuranoPL classes entirely or partially written in Python, all methods -that have either ``@staticmethod`` or ``@classmethod`` decorators are -automatically imported as static methods and work as they normally do in -Python. - - -.. _extension_methods: - -Extension methods -~~~~~~~~~~~~~~~~~ - -Extension methods are a special kind of static methods that can act as if they -were regular instance methods of some other type. - -Extension methods enable you to "add" methods to existing types without -modifying the original type. - - -Defining extension methods --------------------------- - -Extension methods are declared with the ``Usage: Extension`` modifier. - -For example: - -.. code-block:: yaml - - Name: SampleClass - Methods: - mul: - Usage: Extension - Arguments: - - self: - Contract: $.int().notNull() - - arg: - Contract: $.int().notNull() - Body: - Return: $self * $arg - -Extension method are said to extend some other type and that type is deducted -from the first method argument contract. Thus extension methods must have -at least one argument. - -Extension methods can also be written in Python just the same way as static -methods. However one should be careful in method declaration and use precise -YAQL specification of the type of first method argument otherwise the method -will become an extension of any type. - -To turn Python static method into extension method it must be decorated with -``@yaql.language.specs.meta('Usage', 'Extension')`` decorator. - - -Using extension methods ------------------------ - -The example above defines a method that extends integer type. Therefore, with -the method above it becomes possible to say ``2.mul(3)``. However, the most -often usage is to extend some existing MuranoPL class using ``class()`` -contract. - -If the first argument contract does not have ``notNull()``, then the method -can be invoked on the ``null`` object as well (like ``null.foo()``). - -Extension methods are static methods and, therefore,can be invoked in a usual -way on type object: ``:SampleClass.mul(2, 3)``. However, unlike regular static -methods extensions cannot be invoked on a class instance because this can -result in ambiguity. - - -Using extension lookup order ----------------------------- - -When somewhere in the code the ``$foo.bar()`` expression is encountered, MuranoPL -uses the following order to locate bar() ``implementation``: - -* If there is an instance or static method in ``$foo``'s class, it will be used. -* Otherwise if the current class (where this expression was encountered) has - an extension method called ``bar`` and ``$foo`` satisfies the contract of - its first argument, then this method will be called. - -Normally, if no method was found an exception will be raised. However, -additional extension methods can be imported into the current context. This is -done using the ``Import`` keyword on a class level. The ``Import`` section -specifies either a list or a single type name (or type object) which extension -methods will be available anywhere within the class code: - -.. code-block:: yaml - - Name: MyClass - Import: - - ns:SomeOtherType - - :ClassFomCurrentContext - - 'io.murano.foo.Bar' - -If no method was found with the algorithm above, the search continues on -extension methods of all classes listed in the ``Import`` section in the order -types are listed. diff --git a/doc/source/admin/appdev-guide/murano_pl/versioning.rst b/doc/source/admin/appdev-guide/murano_pl/versioning.rst deleted file mode 100644 index c73fdeb56..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/versioning.rst +++ /dev/null @@ -1,185 +0,0 @@ -.. _versioning: - -Versioning -~~~~~~~~~~ - -Versioning is an ability to assign a version number to some particular package -(and, in turn, to a class) and then distinguish packages with different -versions. - -Package version ---------------- - -It is possible to specify a version for packages. You can import several -versions of the same package simultaneously and even deploy them inside a -single environment. To do this, you should use Glare as a storage for packages. -But if you're going to keep only the latest version API is still good enough -and both FormatVersion and Version rules will still be there. For more -information about using Glare, refer to :ref:`glare_usage`. - -To specify the version of your package, add a new section to the manifest file: - - .. code-block:: yaml - - Version: 0.1.0 - - .. - -It should be standard SemVer format version string consisting of 3 parts: -``Major.Minor.Patch`` and optional SemVer suffixes -``[-dev-build.label[+metadata.label]]``. -All MuranoPL classes have the version of the package they are contained in. -If no version is specified, the package version is *0.0.0*. - -.. note:: - It is impossible to show multiple versions of the same application in murano - dashboard: only the last one is shown if the multiple versions are present. - -Package requirements --------------------- - -In some cases, packages may require other packages for their work. -You need to list such packages in the `Require` section of the manifest -file: - - .. code-block:: yaml - - Require: - package1_FQN: version_spec_1 - ... - packageN_FQN: version_spec_N - - .. - -``version_spec`` here denotes the allowed version range. It can be either in -semantic_version specification pip-like format or as a partial version string. -If you do not want to specify the package version, leave this value empty: - - .. code-block:: yaml - - Require: - package1_FQN: '>=0.0.3' - package2_FQN: - - .. - -In this case, version specification is equal to *0*. - - -.. note:: - All packages depend on the `io.murano` package (Core Library). If you do not - specify this requirement in the list (or the list is empty, or there is - no ``Require`` key in the package manifest), then dependency *io.murano: 0* - will be automatically added. - - -Object version --------------- - -You can specify the version of the objects in UI definition when your -application requires a specific version of some class. To do this, add a new key -``classVersion`` to section ``?`` describing the object: - - .. code-block:: yaml - - ?: - type: io.test.apps.TestApp - classVersion: version_spec - - .. - - -Side-by-side versioning of packages ------------------------------------ - -In some cases it might happen that several different versions of the same class -are simultaneously present in a single environment: - - * There are different versions of the same MuranoPL class inside a single - object model (environment). - * Several class versions encounter within class parents. For example, class A - extends B and C and class C inherits B2, where B and B2 are two different - versions of the same class. - -The first case, when two different versions of the same class need to communicate -with each other, is handled by the fact that in order to do that there is a -``class()`` contract for that value. ``class()`` contract validates object -version against package requirements. If class A has a property with contract -$.class(B), then an object passed in this property when upcasted to B must have a -version compatible with requirement specification in A's package (requesting -B's package). - -For the second case, where a single class attempts to inherit from two -different versions of the same class engine (DSL), it attempts to find a -version of this class which satisfies all parties and use it instead. -However, if it is impossible, all remained different versions of the same class -are treated as if they are unrelated classes. - -For example: classA inherits classB from packageX and classC from packageY. -Both classB and classC inherit from classD from packageZ; however, packageX -depends on the version 1.2.0 of packageZ, while packageY depends on the -version 1.3.0. This leads to a situation when classA transitively inherits -classD of both versions 1.2 and 1.3. Therefore, an exception is thrown. -However, if packageY's dependency would be just "1" (which means any of the -1.x.x family), the conflict would be resolved and the 1.2 would be used as it -satisfies both inheritance chains. - -Murano engine is free to use any package version that is valid for the spec. -For example, one application requires packageX with version spec < 0.3 and -another package with the spec > 0. If both packages are get used in the same -environment and the engine already loaded version 0.3 it can still use it for -the second requirement even if there is a package with version 0.4 in the -catalog and the classes from both classes are never interfere. In other words, -engine always tries to minimize the number of versions in use for -the single package to avoid conflicts and unnecessary package downloads. -However, it also means that packages not always get the latest requirements. - -.. _ManifestFormat: - -Manifest format versioning --------------------------- - -The manifests of packages are versioned using *Format* attribute. Currently, -available versions are: `1.0`, `1.1`, `1.2` and `1.3`. -The versioning of manifest format is directly connected with YAQL and version -of murano itself. - -The short description of versions: - -================== =========================================================== - Format version Description -================== =========================================================== - **1.0** supported by all versions of murano. Use this version - if you are planning to use *yaql 0.2* in your - application - - **1.1** supported since Liberty. *yaql 0.2* is supported in - legacy mode. Specify it, if you want to use features - from *yaql 0.2* and *yaql 1.0.0* at the same time in - your application. - - **1.2** supported since Liberty. Do not use *yaql 0.2* in - applications with this format. - - **1.3** supported since Mitaka. *yaql 1.1* is available. It's - recommended specifying this format in new applications, - where compatibility with older versions of murano is not - required. - - **1.4** supported since Newton. Keyword ``Scope`` is introduced - for class methods to declare method's accessibility from - outside through the API call. -================== =========================================================== - -UI forms versioning -------------------- - -UI forms are versioned using Format attribute inside YAML definition. -For more information, refer to :ref:`corresponding documentation`. - -Execution plan format versioning --------------------------------- - -Format of an execution plan can be specified using property ``FormatVersion``. -More information can be found :ref:`here`. - diff --git a/doc/source/admin/appdev-guide/murano_pl/yaml.rst b/doc/source/admin/appdev-guide/murano_pl/yaml.rst deleted file mode 100644 index 1e89f523b..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/yaml.rst +++ /dev/null @@ -1,35 +0,0 @@ -.. _yaml: - -YAML -~~~~ - -YAML is an easily readable data serialization format that is a superset -of JSON. Unlike JSON, YAML is designed to be read and written by humans -and relies on visual indentation to denote nesting of data structures. -This is similar to how Python uses indentation for block structures -instead of curly brackets in most C-like languages. Also YAML may -contain more data types as compared to JSON. See http://yaml.org/ -for a detailed description of YAML. - -MuranoPL is designed to be representable in YAML so that MuranoPL code could -remain readable and structured. Usually MuranoPL files are YAML encoded documents. -But MuranoPL engine itself does not deal directly with YAML documents, and it is up to -the hosting application to locate and deserialize the definitions of particular classes. -This gives the hosting application the ability to control where those definitions can be -found (a file system, a database, a remote repository, etc.) and possibly use some other -serialization formats instead of YAML. - -MuranoPL engine relies on a host deserialization code when detecting YAQL -expressions in a source definition. It provides them as instances of the YaqlExpression -class rather than plain strings. Usually, YAQL expressions can be distinguished by the -presence of $ (the dollar sign) and operators, but in YAML, a developer can always -state the type by using YAML tags explicitly. For example: - -.. code-block:: yaml - :linenos: - - Some text - a string - $.something() - a YAQL expression - "$.something()" - a string because quotes are used - !!str $ - a string because a YAML tag is used - !yaql "text" - a YAQL expression because a YAML tag is used diff --git a/doc/source/admin/appdev-guide/murano_pl/yaql.rst b/doc/source/admin/appdev-guide/murano_pl/yaql.rst deleted file mode 100644 index f9a754490..000000000 --- a/doc/source/admin/appdev-guide/murano_pl/yaql.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. _yaql: - -YAQL -~~~~ - -YAQL (Yet Another Query Language) is a query language that was also -designed as a part of the murano project. MuranoPL makes an extensive -use of YAQL. A description of YAQL can be found `here `_. - -Simply speaking, YAQL is the language for expression evaluation. -The following examples are all valid YAQL expressions: -``2 + 2, foo() > bar(), true != false``. - -The interesting thing in YAQL is that it has no built in list of -functions. Everything YAQL can access is customizable. YAQL cannot call -any function that was not explicitly registered to be accessible by YAQL. -The same is true for operators. So the result of the expression 2 * -foo(3, 4) completely depends on explicitly provided implementations -of "foo" and "operator_*". - -YAQL uses a dollar sign ($) to access external variables, which are also -explicitly provided by the host application, and function arguments. -``$variable`` is a syntax to get a value of the variable "$variable", -$1, $2, etc. are the names for function arguments. "$" is a name for current object: -data on which an expression is evaluated, or a name of a single argument. Thus, -"$" in the beginning of an expression and "$" in the middle of it can refer -to different things. - -By default, YAQL has a lot of functions that can be registered in a YAQL -context. This is very similar to how SQL works but uses more Python-like -syntax. For example: :code:`$.where($.myObj.myScalar > 5`, -:code:`$.myObj.myArray.len() > 0`, and :code:`$.myObj.myArray.any($ = 4)).select($.myObj.myArray[0])` can be executed on :code:`$ = array` of objects, -and result in another array that is a filtration and projection of a source data. - -.. note:: - There is no assignment operator in YAQL, and ``=`` means - comparison, the same what ``==`` means in Python. - -As YAQL has no access to underlying operating system resources and -is fully controllable by the host, it is secure to execute YAQL expressions -without establishing a trust to the executed code. Also, because functions -are not predefined, different methods can be accessible in different -context. So, YAQL expressions that are used to specify property -contracts are not necessarily valid in workflow definitions. - - diff --git a/doc/source/admin/appdev-guide/muranopackages/dynamic_ui.rst b/doc/source/admin/appdev-guide/muranopackages/dynamic_ui.rst deleted file mode 100644 index 9d36970d6..000000000 --- a/doc/source/admin/appdev-guide/muranopackages/dynamic_ui.rst +++ /dev/null @@ -1,612 +0,0 @@ -.. _DynamicUISpec: - -Dynamic UI definition specification -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The main purpose of Dynamic UI is to generate application creation -forms "on-the-fly". The Murano dashboard does not know anything about -applications that will be presented in the catalog and which web forms are -required to create an application instance. So all application definitions -should contain an instruction, which tells the dashboard how to create an -application and what validations need to be applied. This document will help -you to compose a valid UI definition for your application. - -The UI definition should be a valid YAML file and may contain the following -sections (for version 2.x): - -* **Version** - Points out the syntax version in use. *Optional* -* **Templates** - An auxiliary section, used together with an Application section - to help with object model composing. *Optional* -* **Parameters** - An auxiliary section for evaluated once parameters. *Optional* -* **ParametersSource** - A static action name (ClassName.methodName) to call for additional - parameters. *Optional* -* **Application** - Object model description passed to murano engine and used for application - deployment. *Required* -* **Forms** - Web form definitions. *Required* - -.. _DynamicUIversion: - -Version -------- - -The syntax and format of dynamic UI definitions may change over time, so the -concept of *format versions* is introduced. Each UI definition file may contain -a top-level section called *Version* to indicate the minimum version of Murano -Dynamic UI platform which is capable to process it. -If the section is missing, the format version is assumed to be latest supported. - -The version consists of two non-negative integer segments, separated by a dot, -i.e. has a form of *MAJOR.MINOR*. -Dynamic UI platforms having the same MAJOR version component are compatible: -i.e. the platform having the higher version may process UI definitions with -lower versions if their MAJOR segments are the same. -For example, Murano Dynamic UI platform of version 2.2 is able to process UI -definitions of versions 2.0, 2.1 and 2.2, but is unable to process 3.0 or -1.9. - -Currently, the latest version of Dynamic UI platform is 2.3. It is incompatible -with UI definitions of Version 1.0, which were used in Murano releases before -Juno. - -.. note:: - - Although the ``Version`` field is considered to be optional, its default - value is the latest supported version. So if you intent to use applications - with the previous stable murano version, verify that the version - is set correctly. - -Version history -~~~~~~~~~~~~~~~ - -+---------+-------------------------------------------------------------------+-------------------+ -| Version | Changes | OpenStack Version | -+=========+===================================================================+===================+ -| 1.0 | - Initial Dynamic UI implementation | Icehouse | -+---------+-------------------------------------------------------------------+-------------------+ -| 2.0 | - *instance* field support is dropped | Juno, Kilo | -| | - New *Application* section that describes engine object model | | -| | - New *Templates* section for keeping reusable pieces of Object | | -+---------+-------------------------------------------------------------------+-------------------+ -| 2.1 | - New *network* field provides a selection of networks and | Liberty | -| | their subnetworks as a dropdown populated with those which are | | -| | available to the current tenant. | | -+---------+-------------------------------------------------------------------+-------------------+ -| 2.2 | - Now *application name* is added automatically to the last | Liberty | -| | service form. It is needed for a user to recognize one | | -| | created application from another in the UI. Previously all | | -| | application definitions contained the *name* property. So to | | -| | support backward compatibility, you need to manually remove | | -| | *name* field from class properties. | | -+---------+-------------------------------------------------------------------+-------------------+ -| 2.3 | - Now *password* field supports ``confirmInput`` flag and | Mitaka | -| | validator overloading with single ``regexpValidator`` or | | -| | multiple *validators* attribute. | | -+---------+-------------------------------------------------------------------+-------------------+ -| 2.4 | - Parameters and ParametersSource sections were added | Ocata | -| | - ref() YAQL function were added to Application DSL | | -| | - YAQL expressions can be used anywhere in the form definition | | -| | - choice control accepts choices in dictionary format | | -+---------+-------------------------------------------------------------------+-------------------+ - -Application ------------ - -The Application section describes an *application object model*. -The model is a dictionary (document) of application property values (inputs). -Property value might be of any JSON-serializable type (including lists and -maps). In addition the value can be of an object type (another application, -application component, list of components etc.). Object properties are -represented either by the object model of the component (i.e. dictionary) or -by an object ID (string) if the object was already defined elsewhere. -Each object definition (including the one in Application itself) must have a -special ``?`` key called ``object header``. This key holds object metadata most -important of which is the object type name. Thus the Application might look -like this: - -.. code-block:: yaml - - Application: - ?: - type: "com.myCompany.myNamespace.MyClass" - property1: "string property value" - property2: 123 - property3: - key1: value1 - key2: [1, false, null] - property4: - ?: - type: "com.myCompany.myNamespace.MyComponent" - property: value - -However in most cases the values in object model should come from input fields -rather than being static as in example above. To achieve this, object model -values can also be of a `YAQL ` -expression type. With expressions language it becomes possible to retrieve -input control values, do some calculations and data transformations (queries). -Any YAML value that is not enclosed in quote marks and conforms to the YAQL -syntax is considered to be a YAQL expression. There is also an explicit -YAML tag for the YAQL expressions: ``!yaql``. - -So with the YAQL addition ``Application`` section might look like this: - -.. code-block:: yaml - - Application: - ?: - type: "com.myCompany.myNamespace.MyClass" - property1: $.formName.controlName - property2: 100 + 20 + 3 - property3: - !yaql "'KEY1'.toLower()'": !yaql "value1 + '1'" - key2: [$parameter, not true] - property4: null - -When evaluating YAQL expressions ``$`` is set to the forms data (list of -dictionaries with cleaned validated forms' data) and templates and parameters -are available using $templateName ($parameterName) syntax. See below on -templates and parameters. - -YAQL comes with hundreds of functions bundled. In addition to that there are -another four functions provided by murano dashboard: - -* **generateHostname(pattern, index)** is used for a machine hostname template - generation. It accepts two arguments: name pattern (string) and index - (integer). If '#' symbol is present in name pattern, it will be replaced - with the index provided. If pattern is an empty string, a random name will be - generated. -* **repeat(template, times)** is used to produce a list of data snippets, given - the template snippet (first argument) and number of times it should be - reproduced (second argument). Inside that template snippet current step can - be referenced as *$index*. -* **name()** returns current application name. -* **ref(templateName [, parameterName] [, idOnly])** is used to generate object - definition from the template and then reference it several times in the - object model. This function evaluates template ``templateName`` and - fixes the result in parameters under ``parameterName`` key (or - ``templateName`` if the second parameter was omitted). Then it generates - object ID and places it into ``?/id`` field. On the first use of - ``parameterName`` or if ``idOnly`` is ``false`` the function will return - the whole object structure. On subsequent calls or if ``idOnly`` is - ``true`` it will return the ID that was generated upon the first call. - -Templates ---------- - -It is often that application object model contains number of similar instances -of the same component/class. For example it might be list of servers for -multi-server application or list of nodes or list of components. For such cases -UI definition markup allow to give the repeated object model snippet a name -and then refer to it by the name in the application object model. -Such snippets are placed into ``Templates`` section: - -.. code-block:: yaml - - Templates: - primaryController: - ?: - type: "io.murano.windows.activeDirectory.PrimaryController" - host: - ?: - type: "io.murano.windows.Host" - adminPassword: $.appConfiguration.adminPassword - name: generateHostname($.appConfiguration.unitNamingPattern, 1) - flavor: $.instanceConfiguration.flavor - image: $.instanceConfiguration.osImage - - secondaryController: - ?: - type: "io.murano.windows.activeDirectory.SecondaryController" - host: - ?: - type: "io.murano.windows.Host" - adminPassword: $.appConfiguration.adminPassword - name: generateHostname($.appConfiguration.unitNamingPattern, $index + 1) - flavor: $.instanceConfiguration.flavor - image: $.instanceConfiguration.osImage - -Then the template can be inserted into application object model or to another -template using ``$templateName`` syntax. It is often case that it is used -together with ``repeat`` function to put several instances of template. In -this case templates may use of ``$index`` variable which will hold current -iteration number: - -.. code-block:: yaml - - Application: - ?: - type: io.murano.windows.activeDirectory.ActiveDirectory - primaryController: $primaryController - secondaryControllers: repeat($secondaryController, $.appConfiguration.dcInstances - 1) - - -It is important to remember that templates are evaluated upon each access or -``repeat()`` iteration. Thus if the template has some properties set to a -random or generated values they are going to be different for each instance -of the template. - -Another use case for templates is when single object is referenced several -times within application object model: - -.. code-block:: yaml - - Templates: - instance: - ?: - type: "io.murano.resources.LinuxMuranoInstance" - image: myImage - flavor: "m1.small" - - Application: - ?: - type: "com.example.MyApp" - components: - - ?: - type: "com.example.MyComponentType1" - instance: ref(instance) - - ?: - type: "com.example.MyComponentType2" - instance: ref(instance) - -In example above there are two components that uses the same server instance. -If this example had ``$instance`` instead of ``ref(instance)`` that would -be two unrelated servers based on the same template i.e. with the same image -and flavor, but not the same VM. - - -Parameters and ParametersSource -------------------------------- - -Parameters are values that are used to parametrize the UI form and/or -application object model. Parameters are put into ``Parameters`` section and -accessed using ``$parameterName`` syntax: - -.. code-block:: yaml - - Parameters: - param1: "Hello!" - - Application: - ?: - type: "com.example.MyApp" - stringProperty: $param1 - -Parameters are very similar to Templates with two differences: - -#. Parameter values are evaluated only once per application instance at the - very beginning whereas templates are evaluated on each access. - -#. Parameter values can be used to initialize UI control attributes (e.g. - initial text box value, list of choices for a drop down etc.) - -However the most powerful feature about parameters is that their values -might be obtained from the application class. Here is how to do it: - -#. In one of the classes in the MuranoPL package (usually the main application - class define a static action method without arguments that returns a - dictionary of variables: - - .. code-block:: yaml - - Name: "com.example.MyApp" - Methods: - myMethod: - Usage: Static - Scope: Public - Body: - # arbitrary MuranoPL code can be used here - Return: - var1: value1 - var2: 123 - -#. In UI definition file add - .. code-block:: yaml - - ParametersSource: "com.example.MyApp.myMethod" - - The class name may be omitted. In this case the dashboard will try to use - the type of Application object or package FQN for that purpose. - -The values returned by the method are going to be merged into Parameters -section like if they were defined statically. - - - - - -Forms ------ - -This section describes markup elements for defining forms, which are currently -rendered and validated with Django. Each form has a name, field definitions -(mandatory), and validator definitions (optionally). - -Note that each form is split into 2 parts: - -* **input area** - left side, where all the controls are located -* **description area** - right side, where descriptions of the controls are located - -Each field should contain: - -* **name** - system field name, could be any -* **type** - system field type - -Currently supported options for **type** attribute are: - -* *string* - text field (no inherent validations) with one-line text input -* *boolean* - boolean field, rendered as a checkbox -* *text* - same as string, but with a multi-line input -* *integer* - integer field with an appropriate validation, one-line text input -* *choice* - drop-down list of variants. Each variant has a display string that - is going to be displayed to the user and associated key that is going to be - a control value -* *password* - text field with validation for strong password, rendered as two - masked text inputs (second one is for password confirmation) -* *clusterip* - specific text field, used for entering cluster IP address - (validation for valid IP address syntax) -* *databaselist* - specific field, a list of databases (comma-separated list of - databases' names, where each name has the following syntax first symbol - should be latin letter or underscore; subsequent symbols can be latin - letter, numeric, underscore, at the sign, number sign or dollar sign), - rendered as one-line text input -* *image* - specific field, used for filtering suitable images by image type - provided in murano metadata in glance properties. -* *flavor* - specific field, used for selection instance flavor from a list -* *keypair* - specific field, used for selecting a keypair from a list -* *azone* - specific field, used for selecting instance availability zone from - a list -* *network* - specific field, used to select a network and subnet from a list - of the ones available to the current user -* *securitygroup* - specific field, used for selecting a custom security group - to assign to the instance -* *volume* - specific field, used for selecting a volume or a volume snapshot - from a list of available volumes (and volume snapshots) -* any other value is considered to be a fully qualified name for some - Application package and is rendered as a pair of controls: one for selecting - already existing Applications of that type in an Environment, second - for - creating a new Application of that type and selecting it - -Other arguments (and whether they are required or not) depends on a -field's type and other attributes values. Most of them are standard Django -field attributes. The most common attributes are the following: - -* **label** - name, that will be displayed in the form; defaults to **name** - being capitalized. -* **description** - description, that will be displayed in the description area. - Use YAML line folding character ``>-`` to keep the correct formatting during - data transferring. -* **descriptionTitle** - title of the description, defaults to **label**; - displayed in the description area -* **hidden** whether field should be visible or not in the input area. - Note that hidden field's description will still be visible in the - descriptions area (if given). Hidden fields are used storing some data to be - used by other, visible fields. -* **minLength**, **maxLength** (for string fields) and **minValue**, - **maxValue** (for integer fields) are transparently translated into django - validation properties. -* **choices** - a choices for the ``choice`` control type. The format is - ``[["key1", "display value1"], ["key2", "display value2"]]``. Starting from - version 2.4 this can also be passed as a - ``{key1: "display value1", key2: "display value2"}`` -* **regexpValidator** - regular expression to validate user input. Used with - *string* or *password* field. -* **errorMessages** - dictionary with optional 'invalid' and 'required' keys - that set up what message to show to the user in case of errors. -* **validators** is a list of dictionaries, each dictionary should at least - have *expr* key, under that key either some - `YAQL `_ - expression is stored, either one-element dictionary with *regexpValidator* - key (and some regexp string as value). - Another possible key of a validator dictionary is *message*, and although - it is not required, it is highly desirable to specify it - otherwise, when - validator fails (i.e. regexp doesn't match or YAQL expression evaluates to - false) no message will be shown. Note that field-level validators use YAQL - context different from all other attributes and section: here *$* root object - is set to the value of field being validated (to make expressions shorter). - - .. code-block:: yaml - - - name: someField - type: string - label: Domain Name - validators: - - expr: - regexpValidator: '(^[^.]+$|^[^.]{1,15}\..*$)' - message: >- - NetBIOS name cannot be shorter than 1 symbol and - longer than 15 symbols. - - expr: - regexpValidator: '(^[^.]+$|^[^.]*\.[^.]{2,63}.*$)' - message: >- - DNS host name cannot be shorter than 2 symbols and - longer than 63 symbols. - helpText: >- - Just letters, numbers and dashes are allowed. - A dot can be used to create subdomains - - Using of *regexpValidator* and *validators* attributes with *password* - field was introduced in version 2.3. By default, password should have at - least 7 characters, 1 capital letter, 1 non-capital letter, 1 digit, and 1 - special character. If you do not want password validation to be so strong, - you can override it by setting a custom validator or multiple validators for - password. For that add *regexpValidator* or *validators* to the *password* - field and specify custom regexp string as value, just like with any *string* - field. - - *Example* - - .. code-block:: yaml - - - name: password - type: password - label: Password - descriptionTitle: Password - description: >- - Please, provide password for the application. Password should be - 5-50 characters long and consist of alphanumeric characters - regexpValidator: '^[a-zA-Z0-9]{5,50}?$' - -* **confirmInput** is a flag used only with password field and defaults to - ``true``. If you decided to turn off automatic password field cloning, you - should set it to ``false``. In this case password confirmation is not - required from a user. - -* **widgetMedia** sets some custom *CSS* and *JavaScript* used for the field's - widget rendering. Note, that files should be placed to Django static folder - in advance. Mostly they are used to do some client-side field - enabling/disabling, hiding/unhiding etc. - -* **requirements** is used only with flavor field and prevents user to pick - unstable for a deployment flavor. - It allows to set minimum ram (in MBs), disk space (in GBs) or virtual CPU - quantity. - - Example that shows how to hide items smaller than regular *small* flavor - in a flavor select field: - - .. code-block:: yaml - - - name: flavor - type: flavor - label: Instance flavor - requirements: - min_disk: 20 - min_vcpus: 2 - min_memory_mb: 2048 - -* **include_snapshots** is used only with the volume field. ``True`` by default. - If ``True``, the field list includes available volumes and volume snapshots. - If set to ``False``, only available volumes are shown. - -* **include_subnets** is used only with network field. ``True`` by default. - If ``True``, the field list includes all the possible combinations of network - and subnet. E.g. if there are two available networks X and Y, and X has two - subnets A and B, while Y has a single subnet C, then the list will include 3 - items: (X, A), (X, B), (Y, C). If set to ``False`` only network names will be - listed, without their subnets. - -* **filter** is used only with network field. ``None`` by default. If set to a - regexp string, will be used to display only the networks with names matching - the given regexp. - -* **murano_networks** is used only with network field. ``None`` by default. May - have values ``None``, ``exclude`` or ``translate``. Defines the handling of - networks which are created by murano. - Such networks usually have very long randomly generated names, and thus look - ugly when displayed in the list. If this value is set to ``exclude`` then these - networks are not shown in the list at all. If set to ``translate`` the - names of such networks are replaced by a string ``Network of %env_name%``. - - .. note:: - This functionality is based on the simple string matching of the - network name prefix and the names of all the accessible murano - environments. If the environment is renamed after the initial deployment - this feature will not be able to properly translate or exclude its network - name. - -* **allow_auto** is used only with network field. ``True`` by default. Defines if - the default value of the dropdown (labeled "Auto") should be present in the - list. The default value is a tuple consisting of two ``None`` values. The logic - on how to treat this value is up to application developer. It is suggested to - use this field to indicate that the instance should join default environment - network. For use-cases where such behavior is not desired, this parameter - should be set to ``False``. - -*Network* field and its specific attributes (*include_subnets*, *filter*, -*murano_networks*, *allow_auto*) are available since version 2.1. -Before that, there was no way for the end user to select existing network in -the UI. The only way to change the default networking behavior was the usage -of networking.yaml file. It allows to override the networking setting at -the environment level, for all the murano environments of all the tenants. -Now you can simple add a *network* field to your form definition and provide -the ability to select the desired network for the specific application. - -*Example* - -.. code-block:: yaml - - - instanceConfiguration: - fields: - - name: network - type: network - label: Network - description: Select a network to join. 'Auto' corresponds to a default environment's network. - murano_networks: translate - -Besides field-level validators, form-level validators also exist. They -use **standard context** for YAQL evaluation and are required when -there is a need to validate some form's constraint across several -fields. - -*Example* - -.. code-block:: yaml - - Forms: - - appConfiguration: - fields: - - name: dcInstances - type: integer - hidden: true - initial: 1 - required: false - maxLength: 15 - helpText: Optional field for a machine hostname template - - name: unitNamingPattern - type: string - label: Instance Naming Pattern - required: false - maxLength: 64 - regexpValidator: '^[a-zA-Z][-_\w]*$' - errorMessages: - invalid: Just letters, numbers, underscores and hyphens are allowed. - helpText: Just letters, numbers, underscores and hyphens are allowed. - description: >- - Specify a string that will be used in a hostname instance. - Just A-Z, a-z, 0-9, dash, and underline are allowed. - - - - instanceConfiguration: - fields: - - name: title - type: string - required: false - hidden: true - descriptionTitle: Instance Configuration - description: Specify some instance parameters based on which service will be created. - - name: flavor - type: flavor - label: Instance flavor - description: >- - Select a flavor registered in OpenStack. Consider that service performance - depends on this parameter. - required: false - - name: osImage - type: image - imageType: windows - label: Instance image - description: >- - Select valid image for a service. Image should already be prepared and - registered in glance. - - name: availabilityZone - type: azone - label: Availability zone - description: Select an availability zone, where service will be installed. - required: false - validators: - # if unitNamingPattern is given and dcInstances > 1, then '#' should occur in unitNamingPattern - - expr: $.appConfiguration.dcInstances < 2 or not $.appConfiguration.unitNamingPattern.bool() - or '#' in $.appConfiguration.unitNamingPattern - message: Incrementation symbol "#" is required in the Instance Naming Pattern - -Control attributes might be initialized with a YAQL expression. However prior -to version 2.4 it only worked for forms other than the first. It was designed -to initialize controls with values input on the previous step. Starting with -version 2.4 this limitation was removed and it become possible to use -arbitrary YAQL expressions for any of control fields on any forms and use -parameter values as part of these expressions. diff --git a/doc/source/admin/appdev-guide/muranopackages/package_structure.rst b/doc/source/admin/appdev-guide/muranopackages/package_structure.rst deleted file mode 100644 index 941c90dc4..000000000 --- a/doc/source/admin/appdev-guide/muranopackages/package_structure.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _package_structure: - -Package structure -~~~~~~~~~~~~~~~~~ - -The structure of the Murano application package is predefined. An -application could be successfully uploaded to an application catalog. - -The application package root folder should contain the following: - -**manifest.yaml** file - is an application entry point. - - .. note:: the filename is fixed, do not use any custom names. - -**Classes** folder - contains MuranoPL class definitions. - -**Resources** folder - contains execution plan templates and the **scripts** - folder with all the files required for an application - deployment located in it. - -**UI** folder - contains the dynamic UI YAML definitions. - -**logo.png** file (optional) - is an image file associated to your application. - - .. note:: - There are no any special limitations regarding an image filename. - Though, if it differs from the default ``logo.png``, specify it - in an application manifest file. - -**images.lst** file (optional) - contains a list of images required by an application. - -Here is the visual representation of the Murano application -package structure: - -.. image:: ../figures/structure.png diff --git a/doc/source/admin/appdev-guide/muranopackages/repository.rst b/doc/source/admin/appdev-guide/muranopackages/repository.rst deleted file mode 100644 index 0f514877d..000000000 --- a/doc/source/admin/appdev-guide/muranopackages/repository.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _repository: - -Murano package repository -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano client and dashboard can install both packages and bundles of packages from murano repository. To do so you should set MURANO_REPO_URL settings in murano dashboard or MURANO_REPO_URL env variable for the CLI client, and use a respective command to import the package. These commands automatically import all the prerequisites required to install the application along with any images mentioned in the applications. - -Setting up your own repository ------------------------------- - -It is fairly easy to set up your own murano package repository. To do so you need a web server that would serve 3 directories: - * /apps/ - * /bundles/ - * /images/ - -When importing an application by name, the client appends any version info, if present to the application name, ``.zip`` file extension and searches for that file in the ``apps`` directory. - -When importing a bundle by name, the client appends ``.bundle`` file extension to the bundle name and searches it in the bundles directory. A bundle file is a JSON or a YAML file with the following structure: - -.. code-block:: json - - {"Packages": - [ - {"Name": "com.example.ApacheHttpServer"}, - {"Version": "", "Name": "com.example.Nginx"}, - {"Version": "0.0.1", "Name": "com.example.Lighttpd"} - ] - } - -Glance images can be auto-imported by the client, when mentioned in ``images.lst`` inside the package. Please see :ref:`step-by-step` for more information about package composition. -When importing images from the ``image.lst`` file, the client simply searches for a file with the same name as the name attribute of the image in the ``images`` directory of the repository. diff --git a/doc/source/admin/appdev-guide/step-by-step/configure-step1.png b/doc/source/admin/appdev-guide/step-by-step/configure-step1.png deleted file mode 100644 index 8b0dc0744..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/configure-step1.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/configure-step2.png b/doc/source/admin/appdev-guide/step-by-step/configure-step2.png deleted file mode 100644 index 5114dbdfe..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/configure-step2.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/hello-world-desc.png b/doc/source/admin/appdev-guide/step-by-step/hello-world-desc.png deleted file mode 100644 index 1d99928b3..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/hello-world-desc.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-1.png b/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-1.png deleted file mode 100644 index fdf5d2302..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-1.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-2.png b/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-2.png deleted file mode 100644 index 01f6baba7..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/hello-world-screen-2.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/new-env-1.png b/doc/source/admin/appdev-guide/step-by-step/new-env-1.png deleted file mode 100644 index 1b4fe36c1..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/new-env-1.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/new-env-2.png b/doc/source/admin/appdev-guide/step-by-step/new-env-2.png deleted file mode 100644 index ab25dc87e..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/new-env-2.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/new-env-3.png b/doc/source/admin/appdev-guide/step-by-step/new-env-3.png deleted file mode 100644 index 03d1fb720..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/new-env-3.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/part1.rst b/doc/source/admin/appdev-guide/step-by-step/part1.rst deleted file mode 100644 index 39301a1a7..000000000 --- a/doc/source/admin/appdev-guide/step-by-step/part1.rst +++ /dev/null @@ -1,395 +0,0 @@ - -Part 1: Creating your first Application Package ------------------------------------------------ - -All tutorials on programming languages start with a "Hello, World" example, -and since Murano provides its own programming language, this guide will start -the same way. Let's do a "Hello, World" application. It will not do anything -useful yet, but will provide you with an understanding of how things work -in Murano. We will add more logic to the package at later stages. Now let's -start with the basics: - - -Creating package manifest -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Let's start with creating an empty Murano Package. All packages consist of -multiple files (two at least) organized into a special structure. So, let's -create a directory somewhere in our file system and set it as our current -working directory. This directory will contain our package: - -.. code-block:: shell - - $ mkdir HelloWorld - $ cd HelloWorld - - -The main element of the package is its `manifest`. It is a description of the -package, telling Murano how to display the package in the catalog. It is -defined in a yaml file called ``manifest.yaml`` which should be placed right in -the main package directory. Let's create this file and open it with any text -editor: - -.. code-block:: shell - - $ vim manifest.yaml - - -This file may contain a number of sections (we will take a closer look at some -of them later), but the mandatory ones are ``FullName`` and ``Type``. - -The ``FullName`` should be a unique identifier of the package, the name which -Murano uses to distinguish it among other packages in the catalog. It is very -important for this name to be globally unique: if you publish your package and -someone adds it to their catalog, there should be no chances that someone -else's package has the same name. That's why it is recommended to give your -packages Full Names based on the domain you (or the company your work for) own. -We recommend using "reversed-domain-name" notation, similar to the one used in -the world of Java development: if the `yourdomain.com` is the domain name you -own, then you could name your package ``com.yourdomain.HellWorld``. This way -your package name will not duplicate anybody else's, even if they also named -their package "HelloWorld", because theirs will begin with a different -domain-specific prefix. - -``Type`` may have either of two values: ``Application`` or ``Library``. -``Application`` indicates the standard package to deploy an application with -Murano, while a ``Library`` is bundle of reusable scenarios which may be used -by other packages. For now we just need a single standalone app, so let's -choose an ``Application`` type. -The ``Description`` is a text attribute, providing detailed info about your -package. - -Enter these values and save the file. You should have something like this: - - -.. code-block:: yaml - - FullName: com.yourdomain.HelloWorld - Type: Application - Description: | - A package which demonstrates - development for Murano - by greeting the user. - -This is the minimum required to start. We'll add more manifest data later. - -Adding a class -~~~~~~~~~~~~~~ - -While `manifests` describe Murano packages in the catalog, the actual logic of -packages is put into `classes`, which are plain YAML files placed into the -``Classes`` directory of the application package. So, let's create a directory -to store the logic of our application, then create and edit the file to contain -the first class of the package. - -.. code-block:: shell - - $ mkdir Classes - $ vim Classes/HelloWorld.yaml - - -Murano classes follow standard patterns of object-oriented programming: they -define the types of the objects which may be instantiated by Murano. The types -are composed of `properties`, defining the data structure of objects, and -`methods`, containing the logic that defines the way in which Murano executes -the former. The types may be `extended`: the extended class contains all the -methods and properties of the class it extends, or it may override some of -them. - -Let's type in the following YAML to create our first class: - -.. code-block:: yaml - :linenos: - - Name: com.yourdomain.HelloWorld - - Extends: io.murano.Application - - Methods: - deploy: - Body: - - $reporter: $this.find('io.murano.Environment').reporter - - $reporter.report($this, "Hello, World!") - - -Let's walk through this code line by line and see what this code does. -The first line is pretty obvious: it states the name of our class, -``com.yourdomain.HelloWorld``. Note that this name matches the name of the -package - that's intentional. Although it is not mandatory, it is strongly -recommended to give the main class of your application package the same name as -the package itself. - -Then, there is an ``Extends`` directive. It says that our class extends (or -inherits) another class, called ``io.murano.Application``. That is the base -class for all classes which should deploy Applications in Murano. As many other -classes it is shipped with Murano itself, thus its name starts with -`io.murano.` prefix: `murano.io` domain is controlled by the Murano development -team and no one else should create packages or classes having names in that -namespace. - -Note that ``Extends`` directive may contain not only a single value, but a -list. In that case the class we create will inherit multiple base classes. -Yes, Murano has multiple inheritance, yay! - -Now, the ``Methods`` block contains all the logic encapsulated in our class. In -this example there is just one method, called ``deploy``. This method is -defined in the base class we've just inherited - the ``io.murano.Application``, -so here we `override` it. ``Body`` block of the method contains the -implementation, the actual logic of the method. It's a list of instructions -(note the dash-prefixed lines - that's how YAML defines lists), each executed -one by one. - -There are two instruction statements here. The first one declares a `variable` -named ``$reporter`` (note the ``$`` character: all the words prefixed with it -are variables in Murano language) and assigns it a value. Unlike other -languages Murano uses colon (``:``) as an assignment operator: this makes it -convenient to express Murano statements as regular YAML mappings. -The expression to the right of the colon is executed and the result value is -assigned to a variable to the left of the colon. - -Let's take a closer look at the right-hand side of the expression in the first -statement: - -.. code-block:: yaml - - - $reporter: $this.find('io.murano.Environment').reporter - - -It takes a value of a special variable called ``$this`` (which always contains -a reference to the current object, i.e. the instance of our class for which the -method was called; it is same as ``self`` in python or ``this`` in Java) and -calls a method named ``find`` on it with a string parameter equal -to 'io.murano.Environment'; from the call result it takes a "reporter" -attribute; this value is assigned to the variable in the left-hand side of the -expression. - -The meaning of this code is simple: it `finds` the object of class -``io.murano.Environment`` which owns the current application and returns its -"reporter" object. This ``io.murano.Environment`` is a special object which -groups multiple deployed applications. When the end-user interacts with Murano -they create these `Environments` and place applications into them. So, every -Application is able to get a reference to this object by calling ``find`` -method like we just did. Meanwhile, the ``io.murano.Environment`` class has -various methods to interact with the "outer world", for example to report -various messages to the end-user via the deployment log: this is done by the -"reporter" property of that class. - -So, our first statement just retrieved that reporter. The second one uses it to -display a message to a user: it calls a method "report", passes the reference -to a reporting object and a message as the arguments of the method: - -.. code-block:: yaml - - - $reporter.report($this, "Hello, World!") - -Note that the second statement is not a YAML-mapping: it does not have a colon -inside. That's because this statement just makes a method call, it does not -need to remember the result. - -That's it: we've just made a class which greets the user with a traditional -"Hello, World!" message. Now we need to include this class into the package we -are creating. Although it is placed within a ``Classes`` subdirectory of the -package, it still needs to be explicitly added to the package. To do that, add -a ``Classes`` section to your manifest.yaml file. This should be a YAML -mapping, having class names as keys and relative paths of files within the -``Classes`` directory as the values. So, for our example class it should look -like this: - -.. code-block:: yaml - - Classes: - com.yourdomain.HelloWorld: HelloWorld.yaml - -Paste this block anywhere in the ``manifest.yaml`` - -Pack and upload your app -~~~~~~~~~~~~~~~~~~~~~~~~ - -Our application is ready. It's very simplistic and lacks many features required -for real-world applications, but it already can be deployed into Murano and run -there. -To do that we need to pack it first. We use good old zip for it. -That's it: just zip everything inside your package directory into a zip -archive, and you'll get a ready-to-use Murano package: - -.. code-block:: shell - - $ zip -r hello_world.zip * - -This will add all the contents of our package directory to a zip archive called -``hello_world.zip``. Do not forget the ``-r`` argument to include the files in -subdirectories (the class file in our case). - -Now, let's upload the package to murano. Ensure that your system has a -murano-client installed and your OpenStack cloud credentials are exported as -environmnet variables (if not, sourcing an `openrc` file, downloadable from -your horizon dashboard will do the latter). Then execute the following command: - -.. code-block:: shell - - $ murano package-import ./hello_world.zip - Importing package com.yourdomain.HelloWorld - +----------------------------------+---------------------------+---------------------------+-----------+--------+-----------+-------------+---------+ - | ID | Name | FQN | Author | Active | Is Public | Type | Version | - +----------------------------------+---------------------------+---------------------------+-----------+--------+-----------+-------------+---------+ - | 251a409645d1444aa1ead8eaac451a1d | com.yourdomain.HelloWorld | com.yourdomain.HelloWorld | OpenStack | True | | Application | | - +----------------------------------+---------------------------+---------------------------+-----------+--------+-----------+-------------+---------+ - -As you can see from the output, the package has been uploaded to Murano catalog -and is now available there. Let's now deploy it. - -Deploying your application -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To deploy an application with Murano one needs to create an `Environment` and -add configured instances of your applications into it. It may be done either -with the help of user interface (but that requires some extra effort from -package developer) or by providing an explicit JSON, describing the exact -application instance and its configuration. Let's do the latter option for now. - -First, let's create a json snippet for our application. Since the app is very -basic, the snippet is simple as well: - -.. code-block:: json - - [ - { - "op": "add", - "path": "/-", - "value": { - "?": { - "name": "Demo", - "type": "com.yourdomain.HelloWorld", - "id": "42" - } - } - } - ] - -This json follows a standard json-patch notation, i.e. it defines a number of -operations to edit a large json document. This particular one `adds` (note the -value of ``op`` key) an object described in the ``value`` of the json to the -`root` (note the ``path`` equal to ``/-`` - that's root) of our environment. -The object we add has the `type` of ``com.yourdomain.HelloWorld`` - that's the -class we just created two steps ago. Other keys in this json parameterize the -object we create: they add a `name` and an `id` to the object. Id is mandatory, -name is optional. Note that since the id, name and type are the `system -properties` of our object, they are defined in a special section of the json - -the so-called `?-header`. Non-system properties, if they existed, would be -defined at a top-level of the object. We'll add them in a next chapter to see -how they work. - -For now, save this JSON to some local file (say, ``input.json``) and let's -finally deploy the thing. - -Execute the following sequence of commands: - -.. code-block:: shell - - $ murano environment-create TestHello - +----------------------------------+-----------+--------+---------------------+---------------------+ - | ID | Name | Status | Created | Updated | - +----------------------------------+-----------+--------+---------------------+---------------------+ - | 34bf673a26a8439d906827dea328c99c | TestHello | ready | 2016-10-04T13:19:12 | 2016-10-04T13:19:12 | - +----------------------------------+-----------+--------+---------------------+---------------------+ - - $ murano environment-session-create 34bf673a26a8439d906827dea328c99c - Created new session: - +----------+----------------------------------+ - | Property | Value | - +----------+----------------------------------+ - | id | 6d4a8fa2a5f4484fbc07740ef3ab60dd | - +----------+----------------------------------+ - - $ murano environment-apps-edit --session-id 6d4a8fa2a5f4484fbc07740ef3ab60dd 34bf673a26a8439d906827dea328c99c ./input.json - -This first command creates a murano environment named ``TestHello``. Note the -`id` of the created environment - we use it to reference it in subsequent -operations. - -The second command creates a "configuration session" for this environment. -Configuration sessions allow one to edit environments in transactional isolated -manner. Note the `id` of the created sessions: all subsequent calls to modify -or deploy the environment use both ids of environment and session. - -The third command applies the json-patch we've created before to our -environment within the configuration session we created. - -Now, let's deploy the changes we made: - -.. code-block:: shell - - $ murano environment-deploy --session-id 6d4a8fa2a5f4484fbc07740ef3ab60dd 34bf673a26a8439d906827dea328c99c - +------------------+---------------------------------------------+ - | Property | Value | - +------------------+---------------------------------------------+ - | acquired_by | 7b0fe7c67ede443da9840adb2d518d5c | - | created | 2016-10-04T13:39:34 | - | description_text | | - | id | 34bf673a26a8439d906827dea328c99c | - | name | TestHello | - | services | [ | - | | { | - | | "?": { | - | | "name": "Demo", | - | | "status": "deploying", | - | | "type": "com.yourdomain.HelloWorld", | - | | "id": "42" | - | | } | - | | } | - | | ] | - | status | deploying | - | tenant_id | 60b7b5f7d4e64ff0b1c5f047d694d7ca | - | updated | 2016-10-04T13:39:34 | - | version | 0 | - +------------------+---------------------------------------------+ - -This will deploy the environment. You may check for its status by executing -the following command: - -.. code-block:: shell - - $ murano environment-show 34bf673a26a8439d906827dea328c99c - +------------------+-----------------------------------------------------------------------------+ - | Property | Value | - +------------------+-----------------------------------------------------------------------------+ - | acquired_by | None | - | created | 2016-10-04T13:39:34 | - | description_text | | - | id | 34bf673a26a8439d906827dea328c99c | - | name | TestHello | - | services | [ | - | | { | - | | "?": { | - | | "status": "ready", | - | | "name": "Demo", | - | | "type": "com.yourdomain.HelloWorld/0.0.0@com.yourdomain.HelloWorld", | - | | "_actions": {}, | - | | "id": "42", | - | | "metadata": null | - | | } | - | | } | - | | ] | - | status | ready | - | tenant_id | 60b7b5f7d4e64ff0b1c5f047d694d7ca | - | updated | 2016-10-04T13:40:29 | - | version | 1 | - +------------------+-----------------------------------------------------------------------------+ - -As you can see, the status of the Environment has changed to ``ready``: it -means that the application has been deployed. Open Murano Dashboard, navigate -to Environment list and browse the contents of the ``TestHello`` environment -there. -You'll see that the 'Last Operation' column near the "Demo" component says -"Hello, World!" - that's the reporting made by our application: - -.. image:: hello-world-screen-1.png - -This concludes the first part of our course. We've created a Murano Application -Package, added a manifest describing its contents, written a class which -reports a "Hello, World" message, packed all of these into a package archive -and uploaded it to Murano Catalog and finally deployed an Environment with this -application added. - -In the next part we will learn how to improve this application in various -aspects, both from users' and developers' perspectives. diff --git a/doc/source/admin/appdev-guide/step-by-step/part2.rst b/doc/source/admin/appdev-guide/step-by-step/part2.rst deleted file mode 100644 index 346c2bcb0..000000000 --- a/doc/source/admin/appdev-guide/step-by-step/part2.rst +++ /dev/null @@ -1,445 +0,0 @@ -Part 2: Customizing your Application Package --------------------------------------------- - -We've built a classic "Hello, World" application during the first part of -this tutorial, now let's play a little with it and customize it for better -user and developer experience - while learning some more Murano features, -of course. - -Adding user input -~~~~~~~~~~~~~~~~~ - -Most deployment scenarios for cloud applications require user input. It may -be various options which should be applied in software configuration files, -passwords for default administrator's accounts, IP addresses of external -services to register with and so on. Murano Application Packages may define -the user inputs they expect, prompt the end-users to pass the values as these -inputs, so that they may utilize these values during application lifecycle -workflows. - -In Murano user input is defined for each class as `input properties`. -`Properties` are object-level variables of the class, they may be of different -kinds, and the `input properties` are the ones which are expected to contain -user input. See :ref:`class_props` for details on other kinds of them. - -To define properties of the class you should add a ``Properties`` block -somewhere in the YAML file of that class. - -.. note:: - Usually it is better to place this block after the ``Name`` and ``Extends`` - blocks but before the ``Methods`` block. Following this suggestion will - improve the overall readability of your code. - -The ``Properties`` block should contain a YAML dictionary, mapping the names of -the properties to their descriptions. These descriptions may specify the kind -of properties, the restrictions on the type and value of the property -(so-called `contracts`), provide default value for the property and so on. - -Let's add some user input to our "Hello, World" application. Let's ask the end -user to provide their name, so the application will greet the user instead of -the whole world. To do that, we need to edit our ``com.yourdomain.HelloWorld`` -class to look the following way: - -.. code-block:: yaml - :linenos: - :emphasize-lines: 5-8 - - Name: com.yourdomain.HelloWorld - - Extends: io.murano.Application - - Properties: - username: - Usage: In - Contract: $.string().notNull() - - Methods: - deploy: - Body: - - $reporter: $this.find('io.murano.Environment').reporter - - $reporter.report($this, "Hello, World!") - -On line 6 we declare a property named ``username``, on line 7 we specify that -it is an input property, and on line 8 we provide a contract, i.e. a -restriction on the value. This particular one states that the property's value -should be a string and should not be null (i.e. should be provided by the -user). - -.. note:: - Although there are a total of 7 different kinds of properties, it turns - out that the input ones are the most common. So, for input properties you - may omit the ``Usage`` part - all the properties without an explicit usage - are considered to be input properties. - - -Once the property is declared within the ``Properties`` block, you may access -it in the code of the class methods. Since the properties are object-level -variables they may be accessed by calling a ``$this`` variable (which is a -reference to a current instance of your class) followed by a dot and a property -name. So, our ``username`` property may be accessed as ``$this.username``. - -Let's modify the ``deploy`` method of our class to make use of the property to -greet the user by name: - -.. code-block:: yaml - - Methods: - deploy: - Body: - - $reporter: $this.find('io.murano.Environment').reporter - - $reporter.report($this, "Hello, " + $this.username + "!") - -OK, let's try it. Save the file and archive your package directory again, then -re-import your zip-file to the Murano Catalog as a package. -You'll probably get a warning, since the package with the same name already -exists in the catalog (we imported it there in the previous part of the -tutorial), so murano CLI will ask you if you want to update it. In production -it is better to make a newer version of our application and thus to have both -in the catalog, but for now let's just overwrite the old package with the new -one. - -But you cannot deploy it with the old json input we used in the previous part: -since the property's contract has that ``.notNull()`` part it means that the -input should contain the value for the property. If you attempt to deploy an -application without this value, you'll get an error. - -So, let's edit the ``input.json`` file we created in the previous part and add -the value of the property to the input: - -.. code-block:: json - :linenos: - :emphasize-lines: 11 - - [ - { - "op": "add", - "path": "/-", - "value": { - "?": { - "name": "Demo", - "type": "com.yourdomain.HelloWorld", - "id": "42" - }, - "username": "Alice" - } - } - ] - -Save the json file and repeat the steps from the previous part to create an -environment, open a configuration session, add an application and deploy it. -Now in the 'Last Operation' of Murano Dashboard you will see the updated -reporting message, containing the username: - -.. image:: hello-world-screen-2.png - :width: 100% - - -Adding user interface -~~~~~~~~~~~~~~~~~~~~~ - -As you can see in all the examples above, deploying applications via Murano -CLI is quite a cumbersome process: the user has to create environments and -sessions and provide the appropriate json-based input for the application. - -This is inconvenient for a real user, of course. The CLI is intended to be used -by various external automation systems which interact with Murano via scripts, -but the human users will use Murano Dashboard which simplifies all those -actions and provides a nice interface for them. - -Murano Dashboard provides a nice interface to create and deploy environments -and manages sessions transparently for the end users, but when it comes to the -generation of input JSON it can't do it out of the box: it needs some hints -from the package developer. By having hints, Murano Dashboard will be able to -generate nicely looking wizard-like dialogs to configure applications and add -them to an environment. In this section we'll learn how to create these UI -hints. - -The UI hints (also called `UI definitions`) should be defined in a separate -YAML file (yeah, YAML again) in your application package. The file should be -named ``ui.yaml`` and placed in a special directory of your package called -``UI``. - -The main section which is mandatory for all the UI definitions is called -``Application``: it defines the object structure which should be passed as the -input to Murano. That's it: it is equivalent to the JSON ``input.json`` we were -creating before. The data structure remains the same: ?-header is for system -properties and all other properties belong inside the top level of the object. - -The ``Application`` section for our modified "Hello, World" application should -look like this: - -.. code-block:: yaml - :linenos: - - Application: - ?: - type: com.yourdomain.HelloWorld - username: Alice - -This input is almost the same as the ``input.json`` we used last time, except -that the data is expressed in a different format. However, there are several -important differences: there are not JSON-Patch related keywords ("op", "path" -and "value") - that's because Murano Dashboard will generate them -automatically. - -Same is true for the missing ``id`` and ``name`` from the ?-header of the -object: the dashboard will generate the id on its own and ask the end-user for -the name, and then will insert both into the structure it sends to Murano. - -However, there is one problem in the example above: it has the ``username`` -hardcoded to be Alice. Of course we do not want the user input to be hardcoded: -it won't be an input then. So, let's define a user interface which will ask the -end user for the actual value of this parameter. - -Since Murano Dashboard works like a step-by-step wizard, we need to define at -least one wizard step (so-called `form`) and place a single text-box control -into it, so the end-user will be able to enter his/her name there. - -These steps are defined in the ``Forms`` section of our ui definition file. -This section should contain a list of key-value pairs. Keys are the identifiers -of the forms, while values should define a list of `field` objects. Each field -may define a name, a type, a description, a requirement indicator and some -other attributes intended for advanced usage. - -For our example we need a single step with a single text field. The ``Forms`` -section should look like this: - -.. code-block:: yaml - :linenos: - - Forms: - - step1: - fields: - - name: username - type: string - description: Username of the user to say 'hello' to - required: true - -This defines the needed textbox control in the ui. Finally, we need to bind -the value user puts into that textbox to the appropriate position in our -``Application`` section. To do that we replace the hardcoded value with an -expression of form ``$..``. In our case this will be -``$step1.username``. - -So, our final UI definition will look like this: - -.. code-block:: yaml - :linenos: - - Application: - ?: - type: com.yourdomain.HelloWorld - username: $.step1.username - - Forms: - - step1: - fields: - - name: username - type: string - description: Username of the user to say 'hello' to - required: true - -Save this code into your ``UI/ui.yaml`` file and then re-zip your package -directory and import the resulting archive to Murano Catalog again. - -Now, let's deploy this application using Murano Dashboard. - -Open Murano Dashboard with your browser, navigate to -"Applications/Catalog/Environments" panel, click the "Create Environment" -button, enter the name for your environment and click "Create". You'll be -taken to the contents of your environment: you'll see that it is empty, but on -top of the screen there is a list of components you may add to it. If your -Murano Catalog was empty when you started this tutorial, this list will -contain just one item: your "Hello, World" application. The screen should look -like this: - -.. image:: new-env-1.png - :width: 100% - -Drag-n-drop your "com.yourdomain.HelloWorld" application from the list on top -of the screen to the "Drop components here" panel beneath it. You'll see a -dialog, prompting you to enter a username: - -.. image:: configure-step1.png - :width: 100% - -Enter the name and click "Next". Although you've configured just one step of -the wizard, the actual interface will consist of two: the dashboard always adds -a final step to prompt the user to enter the name of the application instance -within the environment: - -.. image:: configure-step2.png - :width: 100% - -When you click "Create" button an instance of your application will be added to -the environment, you'll see it in the list of components: - -.. image:: new-env-2.png - :width: 100% - -So, now you may click the "Deploy this Environment" button and the application -will greet the user with the name you've entered. - -.. image:: new-env-3.png - :width: 100% - - -Simplifying code: namespaces -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Now that we've learned how to simplify the user's life by adding a UI -definition, let's simplify the developer's life a bit. - -When you were working with Murano classes in the previous part you probably -noticed that the long class names with all those domain-name-based segments -were hard to write and that it was easy to make a mistake: - -.. code-block:: yaml - :linenos: - - Name: com.yourdomain.HelloWorld - - Extends: io.murano.Application - - Methods: - deploy: - Body: - - $reporter: $this.find('io.murano.Environment').reporter - - $reporter.report($this, "Hello, World!") - - -To simplify the code we may use the concept of `namespaces` and `short names`. -All but last segments of a long class name are namespaces, while the last -segment is a short name of a class. In our example ``com.yourdomain`` is a -namespace while the ``HelloWorld`` is a short name. - -Short names have to be unique only within their namespace, so they tend to be -expressive, short and human readable, while the namespaces are globally unique -and thus are usually long and too detailed. - -Murano provides a capability to abbreviate long namespaces with a short alias. -Unlike namespaces, aliases don't need to be globally unique: they have -to be unique only within a single file which uses them. So, they may be very -short. So, in your file you may abbreviate your ``com.yourdomain`` namespace -as ``my``, and standard Murano's ``io.murano`` as ``std``. Then instead of a -long class name you may write a namespace alias followed by a colon character -and then a short name, e.g. ``my:HelloWorld`` or ``std:Application``. This -becomes very helpful when you have lots of class names in your code. - -To use this feature, declare a special section called ``Namespaces`` in your -class file. Inside that section provide a mapping of namespace aliases to full -namespaces, like this: - -.. code-block:: yaml - - Namespaces: - my: com.yourdomain - std: io.murano - -.. note:: - - Since namespaces are often used in all other sections of files it is - considered good practice to declare this section at a very top of your - class file. - -Quite often there is a namespace which is used much more often than others in a -given file. In this case it would be beneficial to declare this namespace as a -`default namespace`. Default namespace does not need a prefix at all: you just -type short name of the class and Murano will interpret it as being in your -default namespace. Use '=' character to declare the default namespace in your -namespaces block: - -.. code-block:: yaml - :linenos: - :emphasize-lines: 2,5 - - Namespaces: - =: com.yourdomain - std: io.murano - - Name: HelloWorld - - Extends: std:Application - - Methods: - deploy: - Body: - - $reporter: $this.find(std:Environment).reporter - - $reporter.report($this, "Hello, World!") - - -Notice that ``Name`` definition at line 5 uses the default namespace: the -``HelloWorld`` is not prefixed with any namespaces, but is properly resolved -to ``com.yourdomain.HelloWorld`` because of the default namespace declaration -at line 2. Also, because Murano recognizes the ``ns:Class`` syntax there is -no need to enclose ``std:Environment`` in quote marks, though it will also -work. - - -Adding more info for the catalog -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -As you could see while browsing Murano Catalog your application entry in it is -not particularly informative: the user can't get any description about your -app, and the long domain-based name is not very user-friendly aither. - -This can easily be improved. The ``manifest.yaml`` which we wrote in the first -part contained only mandatory fields. This is how it should look by now: - - -.. code-block:: yaml - :linenos: - - FullName: com.yourdomain.HelloWorld - Type: Application - Description: | - A package which demonstrates - development for Murano - by greeting the user. - Classes: - com.yourdomain.HelloWorld: HelloWorld.yaml - -Let's add more fields here. - -First, you can add a ``Name`` attribute. Unlike ``FullName``, it is not a -unique identifier of the package. But, if specified, it overrides the name of -the package that is displayed in the catalog. - -Then an ``Author`` field: here you can put your name or the name of your -company, so it will be displayed in catalog as the name of the package -developer. If this field is omitted, the catalog will consider the package to -be made by "OpenStack", so don't forget this field if you care about your -copyright. - -When you add these fields your manifest may look like this: - -.. code-block:: yaml - :linenos: - - FullName: com.yourdomain.HelloWorld - Type: Application - Name: 'Hello, World' - Description: | - A package which demonstrates - development for Murano - by greeting the user. - Author: John Doe - Classes: - com.yourdomain.HelloWorld: HelloWorld.yaml - - -You may also add an icon to be displayed for your application. To do that just -place a ``logo.png`` file with an appropriate image into the root folder of -your package. - -Zip the package directory and re-upload the file to the catalog. Then use -Murano Dashboard and navigate to Applications/Catalog/Browse panel. You'll see -that your app gets a logo, a more appropriate name and a description: - -.. image:: hello-world-desc.png - :width: 50% - -So, here we've learned how to improve both the user's and developer's -experience with developing Murano application packages. That was all we could -do with the oversimplistic "Hello World" app. Let's move forward and touch -some real-life applications. diff --git a/doc/source/admin/appdev-guide/step-by-step/part3.rst b/doc/source/admin/appdev-guide/step-by-step/part3.rst deleted file mode 100644 index 1f9f95870..000000000 --- a/doc/source/admin/appdev-guide/step-by-step/part3.rst +++ /dev/null @@ -1,799 +0,0 @@ -Part 3: Creating a Plone CMS application package ------------------------------------------------- - -If you've completed "Hello, World" scenarios in the previous parts and are -ready for some serious tasks, we've got a good example here. - -Let's automate the deployment of some real application. We've chosen a "Plone -CMS" for this purpose. Plone is a simple, but powerful and flexible Content -Management System which can efficiently run on cloud. Its deployment scenario -can be very simple for demo cases and can become really complicated for -production-grade usage. So it's a good playground: in this part we'll create a -Murano application to address the simplest scenario, then we will gradually add -more features of production-grade deployments. - -.. note:: - To learn more about Plone, its features, capabilities and deployment - scenarios you may visit the `Official website of Plone Foundation - `_. - -The goal -~~~~~~~~ - -Simplest deployment of Plone CMS requires a single server, or, in the case of -OpenStack, a Virtual Machine, to run on. Then a software should be downloaded -and configured to run on that server. - -So, as a bare minimum our Plone application package for Murano should automate -the following steps: - -#. Provision a virtual machine in OpenStack (VM); -#. Configure ths VM's network connectivity and security; -#. Download a distribution of Plone from Internet to the virtual machine; -#. Install the distribution and configure some of its parameters with user - input. - - -Preparation -~~~~~~~~~~~ - -First let's revisit what we've learned in previous parts and create a new -application package with its manifest and create a class file to contain the -logic of your app. - -Create a new directory for a package, call it ``PloneApp``. Create a -``manifest.yaml`` file as described in part 1 of this tutorial in the root of -the package and fill it with data: name your package ``com.yourdomain.Plone``, -set its type to ``Application``, give it a display name of "Plone CMS" and put -your name as the author of the package: - -.. code-block:: yaml - :linenos: - - FullName: com.yourdomain.Plone - Name: Plone CMS - Description: Simple Plone Deployment - Type: Application - Author: John Doe - -Then create a ``Classes`` sub directory inside your package directory and -create a ``plone.yaml`` there. This will be your application class. - -At the top of this file declare a `Namespace` section: this will simplify the -code and save time on typing long class names. Make your namespace -(``com.yourdomain``) a default namespace of the file, also include the standard -namespace for Murano applications - ``io.murano``, alias it as ``std``. - -Don't forget to include the ``Name`` of your class. Since you've declared a -default namespace for a file you can name your class without a need to type its -long part, just using the shortname. - -Also include the ``Extends`` section: same as in our "Hello, World" example -this application will inherit the ``io.murano.Application`` class, but since -we've aliased this namespace as well, it may be shortened to -``std:Application`` - -By now your class file should look like this: - -.. code-block:: yaml - - Namespaces: - =: com.yourdomain - std: io.murano - - Name: Plone - - Extends: std:Application - - -We'll add the actual logic in the next section. Now, save the file and include -it into the ``Classes`` section of your manifest.yaml, which should now look -like this: - -.. code-block:: yaml - :linenos: - :emphasize-lines: 6-7 - - FullName: com.yourdomain.Plone - Name: Plone CMS - Description: Simple Plone Deployment - Type: Application - Author: John Doe - Classes: - com.yourdomain.Plone: plone.yaml - - -You are all set and ready to go. Let's add the actual deployment logic. - -Library classes -~~~~~~~~~~~~~~~ - -Murano comes bundled with a so-called "Murano Core Library" - a Murano Package -containing the classes to automate different scenarios of interaction with -other entities such as OpenStack services or virtual machines. They follow -object-oriented design: for example, there is a Murano class called -``Instance`` which represents an OpenStack virtual machine: if you create an -object of this class and execute a method called ``deploy`` for it Murano will -do all the needed system calls to OpenStack Services to orchestrate the -provisioning of a virtual machine and its networking configuration. Then this -object will contain information about the state and configuration of the VM, -such as its hostname, ip addresses etc. After the VM is provisioned you can use -its object to send the configuration scripts to the VM to install and configure -software for your application. - -Other OpenStack resources such as Volumes, Networks, Ports, Routers etc also -have their corresponding classes in the core library. - - -Provisioning a VM -~~~~~~~~~~~~~~~~~ - -When creating your application package you can `compose` your application out -of the components of core library. For example for an application which -should run on a VM you can define an input property called ``instance`` and -restrict the value type of this property to the aforementioned ``Instance`` -class with a contract. - -Let's do that in the ``plone.yaml`` class file you've created. -First, add a new namespace alias to your ``Namespaces`` section: -shorten ``io.murano.resources`` as ``res``. This namespace of the core -library contains all the resource classes, including the -``io.murano.resources.Instance`` which we need to define the virtual machine: - -.. code-block:: yaml - :emphasize-lines: 4 - - Namespaces: - =: com.yourdomain - std: io.murano - res: io.murano.resources - -Now, let's add an input property to your class: - -.. code-block:: yaml - :linenos: - - Properties: - instance: - Usage: In - Contract: $.class(res:Instance) - -Notice the contract at line 4: it limits the values of this property to the -objects of class ``io.murano.resources.Instance`` or its subclasses. - -This defines that your application needs a virtual machine. Now let's ensure -that it is provisioned - or provision it otherwise. Add a ``deploy`` method to -your application class and call instance's deploy method from it: - -.. code-block:: yaml - :linenos: - - Methods: - deploy: - Body: - - $this.instance.deploy() - -That's very simple: you just access the ``instance`` property of your current -object and run a method ``deploy`` for it. The core library defines this method -of the ``Instance`` class in an `idempotent` manner: you may call it as many -times as you want: the first call will actually provision the virtual machine -in the cloud, while all the subsequent calls will no nothing, thus you may -always call this method to ensure that the VM was properly provisioned. It's -important since we define it as an input property: theoretically a user can -pass an already-provisioned VM object as input, but you need to be sure. -Always calling the ``deploy`` method is the best practice to follow. - -Running a command on the VM -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once the VM has been provisioned you may execute various kinds of software -configuration scenarios on it to install and configure the actual application -on the VM. Murano supports different types of software configuration tools to -be run on a VM, but the simplest and the most common type is just a shell -script. - -To run a shell script on a virtual machine you may use a `static method` -``runCommand`` of class ``io.murano.configuration.Linux``. Since this method is -static you do not need to create any objects of its class: you can just do -something like: - -.. code-block:: yaml - - - type('io.murano.configuration.Linux').runCommand($server.agent, 'sudo apt-get update') - -or, if we declare another namespace prefix - -.. code-block:: yaml - - Namespaces: - ... - conf: io.murano.configuration - -this may be shortened to - -.. code-block:: yaml - - - conf:Linux.runCommand($server.agent, 'sudo apt-get update') - -In this case ``$server`` should be a variable containing an object of -``io.murano.resources.Instance`` class, everything you pass as a second -argument (``apt get update`` in the example above) is the shell command to be -executed on a VM. You may pass not just a single line, but a multi-line text: -it will be treated as a shell script. - -.. note:: - The shell scripts and commands you send to a VM are executed by a special - software component running on the VM - a `murano agent`. For the most - popular distributions of Linux (Debian, Ubuntu, Centos, Fedora, etc.) it - automatically gets installed on the VM once it is provisioned, but for other - distribution and non-Linux OSes it has to be manually pre-installed in the - image. See :ref:`Building Murano Image ` for details. - - -Loading a script from a resource file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Passing strings as a second argument of a ``runCommand`` method is convenient -for short commands like the ``apt-get update`` shown in an example above. -However for larger scripts it is not that useful. Instead it is preferable -to load a script text from a file and run it. You can do that in Murano. - -For example, let's make a script which downloads, unpacks, installs and -configures Plone CMS on our VM. First, create a directory called ``Resources`` -inside your package directory. Then, create a file named ``install-plone.sh`` -and put the following script there: - -.. code-block:: shell - - #!/bin/bash - - #input parameters - - PL_PATH="$1" - PL_PASS="$2" - PL_PORT="$3" - - - # Write log. Redirect stdout & stderr into log file: - exec &> /var/log/runPloneDeploy.log - # echo "Update all packages." - sudo apt-get update - - # Install the operating system software and libraries needed to run Plone: - sudo apt-get install python-setuptools python-dev build-essential libssl-dev libxml2-dev libxslt1-dev libbz2-dev libjpeg62-dev - - # Install optional system packages for the handling of PDF and Office files. Can be omitted: - sudo apt-get install libreadline-dev wv poppler-utils - - # Download the latest Plone unified installer: - wget --no-check-certificate https://launchpad.net/plone/5.0/5.0.4/+download/Plone-5.0.4-UnifiedInstaller.tgz - - # Unzip the latest Plone unified installer: - tar -xvf Plone-5.0.4-UnifiedInstaller.tgz - cd Plone-5.0.4-UnifiedInstaller - - # Set the port that Plone will listen to on available network interfaces. Editing "http-address" param in buildout.cfg file: - sed -i "s/^http-address = [0-9]*$/http-address = ${PL_PORT}/" buildout_templates/buildout.cfg - - # Run the Plone installer in standalone mode - ./install.sh --password="${PL_PASS}" --target="${PL_PATH}" standalone - - # Start Plone - cd "${PL_PATH}/zinstance" - bin/plonectl start - -.. note:: - As you can see, this script uses apt to install the prerequisite software - packages, so it expects a Debian-compatible Linux distro as the VM operating - system. This particular script was tested on Ubuntu 14.04. Other distros - may have a different set of preinstalled software and thus require different - additional prerequisites. - - -The comments in the script give the needed explanation: the script installs all -the prerequisites, downloads a targz archive with a distribution of Plone, -unpacks it, edits the ``buildout.cfg`` file to specify the port Plone will -listen at, then runs the installation script which is included in the -distribution. When that script is finished, the Plone daemon is started. - -Save the file as ``Resources/install-plone.sh``. Now you may load its contents -into a string variable in your class file. To do that, you need to use another -static method: a ``string()`` method of a ``io.murano.system.Resources`` class: - -.. code-block:: yaml - - - $script: type('io.murano.system.Resources').string('install-plone.sh') - -or, with the introduction of another namespace prefix - -.. code-block:: yaml - - - $script: sys:Resources.string('install-plone.sh') - -But before sending this script to a VM, it needs to be parametrized: as you -can see in the script snippet above, it declares three variables which are -used to set the installation path in the VM's filesystem, a default -administrator's password and a listening port. In the script these values are -initialized with stubs ``$1``, ``$2`` and ``$3``, now we need to replace these -stubs with the actual user input. To do that our class needs to define the -appropriate input properties and then do string replacement. - -First, let's define the appropriate input properties in the ``Properties`` -block of the class, right after the ``instance`` property: - - -.. code-block:: yaml - :linenos: - :emphasize-lines: 6-18 - - Properties: - instance: - Usage: In - Contract: $.class(res:Instance) - - installationPath: - Usage: In - Contract: $.string().notNull() - Default: '/opt/plone' - - defaultPassword: - Usage: In - Contract: $.string().notNull() - - listeningPort: - Usage: In - Contract: $.int().notNull() - Default: 8080 - -Now, let's replace the stub values in that script value we've loaded into the -``$script`` variable. This may be done using a ``replace`` function: - - -.. code-block:: yaml - - - $script: $script.replace({"$1" => $this.installationPath, - "$2" => $this.defaultPassword, - "$3" => $this.listeningPort}) - -Finally, the resulting ``$script`` variable may be passed as a second argument -of a ``runCommand`` method, while the first one should be the ``instance`` -property, containing our VM-object: - -.. code-block:: yaml - - - conf:Linux.runCommand($this.instance.agent, $script) - - -Configuring OpenStack Security -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By now we've got code which provisions a VM and a script which deploys and -configures Plone on it. However, in most OpenStack clouds this is not enough: -usually all incoming traffic to all the VMs is blocked by default, so we need -to configure security group of OpenStack to allow the incoming http calls to -our VM on the port our Plone server listens at. - -To do that we need to use a ``securityGroupManager`` property of the -``Environment`` class which owns our application. That property contains an -object of type ``io.murano.system.SecurityGroupManager``, which defines a -``addGroupIngress`` method. This method allows us to add a security group rule -to allow incoming traffic of some type through a specific port within a port -range. It accepts a list of YAML objects, each having four keys: ``FromPort`` -and ``ToPort`` to define the boundaries of the port range, ``IpProtocol`` to -define the type of the protocol and ``External`` boolean flag to indicate if -the incoming traffic should be be allowed to originate from outside of the -environment (if this flag is false, the traffic will be accepted only from the -VMs deployed by the application in the same Murano environment). - -Let's do this in code: - -.. code-block:: yaml - :linenos: - - - $environment: $this.find(std:Environment) - - $manager: $environment.securityGroupManager - - $rules: - - FromPort: $this.listeningPort - ToPort: $this.listeningPort - IpProtocol: tcp - External: true - - $manager.addGroupIngress($rules) - - $environment.stack.push() - -It's quite straightforward, just notice the last line. It is required, because -current implementation of ``SecurityGroupManager`` relies on Heat underneath - -it modifies the `Heat Stack` associated with our environment, but does not -apply the changes to the actual cloud. To apply them the stack needs to be -`pushed`, i.e. submitted to Heat Orchestration service. The last line does -exactly that. - - -Notifying end-user on Plone location -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When the deployment is completed and our instance of Plone server starts -listening on a provisioned virtual machine, the end user has one last question -to solve: to find out where it is. Of course, the user may use OpenStack -Dashboard to list all the provisioned VMs, find the one which has just been -created and look for its IP address. But that's inconvenient. It would be much -better if Murano notified the end-user on where to find Plone once it is ready. - -We may utilize the same approach we used in the previous parts to say "Hello, -World" - call a ``report`` method of ``reporter`` attribute of the -``Environment`` class. The tricky part is getting the IP address. - -Class ``io.murano.resources.Instance`` has an `output property` called -``ipAddresses``. Unlike input properties the output ones are not provided by -users but are set by objects themselves while their methods are executed. The -``ipAddresses`` is assigned during the execution of ``deploy`` method of the -VM. The value is the list of ip addresses assigned to different interfaces of -the machine. Also, if the ``assignFloatingIp`` input property is set to -``true``, another output property will be set during the execution of -``deploy`` - a ``floatingIpAddress`` will contain the floating ip attached to -the VM. - -Let's use this knowledge and build a proper report message: - -.. code-block:: yaml - :linenos: - - - $message: 'Plone is up and running at ' - - If: $this.instance.assignFloatingIp - Then: - - $message: $message + $this.instance.floatingIpAddress - Else: - - $message: $message + $this.instance.ipAddresses.first() - - $message: $message + ":" + str($this.listeningPort) - - $environment.reporter.report($this, $message) - -Note the usage of ``If`` expression: it is similar to other programming -languages, just uses YAML keys to define the "if" and "else" blocks. - -This code creates a string variable called ``$message``, initializes it with -the beginning of the message string, then appends either a floating ip address -of the VM (if it's set) or the first of the regular ips otherwise. Then it -appends a listening port after a colon character - and reports the resulting -message to the user. - -Completing the Plone class -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We've got all the pieces to deploy our Plone application, now let's combine -them together. Our final class file should look like this: - - -.. code-block:: yaml - - Namespaces: - =: com.yourdomain - std: io.murano - res: io.murano.resources - sys: io.murano.system - - Name: Plone - - Extends: std:Application - - Properties: - instance: - Usage: In - Contract: $.class(res:Instance) - - installationPath: - Usage: In - Contract: $.string().notNull() - Default: '/opt/plone' - - defaultPassword: - Usage: In - Contract: $.string().notNull() - - listeningPort: - Usage: In - Contract: $.int().notNull() - Default: 8080 - - Methods: - deploy: - Body: - - $this.instance.deploy() - - $script: sys:Resources.string('install-plone.sh') - - $script: $script.replace({ - "$1" => $this.installationPath, - "$2" => $this.defaultPassword, - "$3" => $this.listeningPort - }) - - type('io.murano.configuration.Linux').runCommand($this.instance.agent, $script) - - $environment: $this.find(std:Environment) - - $manager: $environment.securityGroupManager - - $rules: - - FromPort: $this.listeningPort - ToPort: $this.listeningPort - IpProtocol: tcp - External: true - - $manager.addGroupIngress($rules) - - $environment.stack.push() - - $formatString: 'Plone is up and running at {0}:{1}' - - If: $this.instance.assignFloatingIp - Then: - - $address: $this.instance.floatingIpAddress - Else: - - $address: $this.instance.ipAddresses.first() - - $message: format($formatString, $address, $this.listeningPort) - - $environment.reporter.report($this, $message) - - -That's all, our class is ready. - - -Providing a UI definition -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Last but not least, we need to add a UI definition file to define a template -for the user input and create wizard steps. - -This time both are a bit more complicated than they were for the "Hello, World" -app. - -First, let's create the wizard steps. It's better to decompose the UI into two -steps: the first one will define the properties of a Virtual Machine, and the -second one the configuration properties of the Plone application itself. - -.. code-block:: yaml - :linenos: - - Forms: - - instanceConfiguration: - fields: - - name: hostname - type: string - required: true - - name: image - type: image - imageType: linux - - name: flavor - type: flavor - - name: assignFloatingIp - type: boolean - - ploneConfiguration: - fields: - - name: installationPath - type: string - - name: defaultPassword - type: password - required: true - - name: listeningPort - type: integer - -This is familiar to what we had on the previous step, however there are several -new types of fields: while the types ``integer`` and ``boolean`` are quite -obvious - they will render a numeric up-and-down textbox and checkbox controls -respectively - other field types are more specific. - -Field of type ``image`` will render a drop-down list allowing you to choose an -image for your VM, and the list of images will contain only the ones having -appropriate metadata associated (the type of metadata is defined by the -``imageType`` attribute: this particular example requires it to be tagged as -"Generic Linux"). - -Field of type ``flavor`` will render a drop-down list allowing you to choose a -flavor for your VM among the ones registered in Nova. - -Field of type ``password`` will render a pair of text-boxes in a password -input mode (i.e. hiding all the input with '*'-characters). The rendered field -will have appropriate validation: it will ensure that the values entered in -both fields are identical (thus providing a "repeat password" functionality) -and will also enforce password complexity check. - -This defines the basic UI, but it is not particularly user friendly: when -MuranoDashboard renders the wizard it will label appropriate controls with the -names of the fields, but they usually don't look informative and pretty. - -So, to improve the user experience you may add additional attributes to field -descriptors here. ``label`` attribute allows you to define a custom label to be -rendered next to appropriate control, ``description`` allows you to provide a -longer text to be displayed on the form as a description of the control, and, -finally, an ``initial`` attribute allows you define the default value to be -entered into the control when it is shown to the end-user. - -Modify the ``Forms`` section to use these attributes: - -.. code-block:: yaml - :linenos: - :emphasize-lines: 6-9,14-17,20-23,26-28,33-36,38-39,44-46 - - Forms: - - instanceConfiguration: - fields: - - name: hostname - type: string - label: Host Name - description: >- - Enter a hostname for a virtual machine to be created - initial: plone-vm - required: true - - name: image - type: image - imageType: linux - label: Instance image - description: >- - Select valid image for the application. Image should already be prepared and - registered in glance. - - name: flavor - type: flavor - label: Instance flavor - description: >- - Select registered in Openstack flavor. Consider that application performance - depends on this parameter. - - name: assignFloatingIp - type: boolean - label: Assign Floating IP - description: >- - Check to assign floating IP automatically - - ploneConfiguration: - fields: - - name: installationPath - type: string - label: Installation Path - initial: '/opt/plone' - description: >- - Enter the path on the VM filesystem to deploy Plone into - - name: defaultPassword - label: Admin password - description: Default administrator's password - type: password - required: true - - name: listeningPort - type: integer - label: Listening Port - description: Port to listen at - initial: 8080 - - -Now, let's add an ``Application`` section to provide templated input for our -app: - -.. code-block:: yaml - :linenos: - - Application: - ?: - type: com.yourdomain.Plone - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - name: $.instanceConfiguration.hostname - image: $.instanceConfiguration.image - flavor: $.instanceConfiguration.flavor - assignFloatingIp: $.instanceConfiguration.assignFloatingIp - installationPath: $.ploneConfiguration.installationPath - defaultPassword: $.ploneConfiguration.defaultPassword - listeningPort: $.ploneConfiguration.listeningPort - -Note the ``instance`` part here: since our ``instance`` input property is not -a scalar value but rather an object, we are placing another object template -inside the appropriate section. Note that the type of this object is not -``io.murano.resources.Instance`` as you could expect based on the property -contract, but a more specific class: ``LinuxMuranoInstance`` in the same -namespace. Since this class inherits the former, it matches the contract, but -it provides a more appropriate implementation than the base one. - - -Let's combine the two snippets together, we'll get the final UI definition of -our app: - -.. code-block:: yaml - :linenos: - - Application: - ?: - type: com.yourdomain.Plone - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - name: $.instanceConfiguration.hostname - image: $.instanceConfiguration.image - flavor: $.instanceConfiguration.flavor - assignFloatingIp: $.instanceConfiguration.assignFloatingIp - installationPath: $.ploneConfiguration.installationPath - defaultPassword: $.ploneConfiguration.defaultPassword - listeningPort: $.ploneConfiguration.listeningPort - Forms: - - instanceConfiguration: - fields: - - name: hostname - type: string - label: Host Name - description: >- - Enter a hostname for a virtual machine to be created - initial: 'plone-vm' - required: true - - name: image - type: image - imageType: linux - label: Instance image - description: >- - Select valid image for the application. Image should already be prepared and - registered in glance. - - name: flavor - type: flavor - label: Instance flavor - description: >- - Select registered in Openstack flavor. Consider that application performance - depends on this parameter. - - name: assignFloatingIp - type: boolean - label: Assign Floating IP - description: >- - Check to assign floating IP automatically - - ploneConfiguration: - fields: - - name: installationPath - type: string - label: Installation Path - initial: '/opt/plone' - description: >- - Enter the path on the VM filesystem to deploy Plone into - - name: defaultPassword - label: Admin password - description: Default administrator's password - type: password - required: true - - name: listeningPort - type: integer - label: Listening Port - description: Port to listen at - initial: 8080 - - -Save this file as a ``ui.yaml`` in a ``UI`` folder of your package. As a final -touch add a logo to the package - save the image below to the root directory of -your package as ``logo.png``: - -.. image:: plone-logo.png - :width: 100 - -The package is ready. Zip it and import to Murano catalog. We are ready to try -it. - -Deploying the package -~~~~~~~~~~~~~~~~~~~~~ - -Go to Murano Dashboard, create an environment and add a "Plone CMS" application -to it. You'll see the nice wizard with all the field labels and descriptions -you've added to the ui definition file: - -.. image:: plone-simple-step1.png - :width: 50% - -.. image:: plone-simple-step2.png - :width: 50% - -After the app is added to the environment, click the "Deploy this environment" -button. The deployment will take about 10 minutes, depending on the speed of -the VM's internet connection and the amount of packages to be updated. When it -is over, check the "Last operation" column in the environment's list of -components near the Plone component. It should contain a message "Plone is up -and running at ..." followed by ip address and port: - -.. image:: plone-ready.png - :width: 50% - -Enter this address to the address bar of your browser. You'll see the default -management interface of Plone: - -.. image:: plone-admin.png - :width: 50% - -If you click a "Create a new Plone site" button you'll be prompted for username -and password. Use ``admin`` username and the password which you entered in the -Wizard. See `Plone Documentation `_ for details on how -to operate Plone. - -This concludes this part of the course. The application package we created -demonstrates the basic capabilities of Murano for the deployments of real-world -applications. However, the deployed configuration of Plone is not of -production-grade service: it is just a single VM with all-in-one service -topology, which is not a scalable or fault-tolerant solution. -In the next part we will learn some advanced features which may help to bring -more production-grade capabilities to our package. diff --git a/doc/source/admin/appdev-guide/step-by-step/part4.rst b/doc/source/admin/appdev-guide/step-by-step/part4.rst deleted file mode 100644 index dd9c39e62..000000000 --- a/doc/source/admin/appdev-guide/step-by-step/part4.rst +++ /dev/null @@ -1,355 +0,0 @@ -Part 4: Refactoring code to use the Application Framework ---------------------------------------------------------- - -Up until this point we wrote the Plone application in a manner that was common -to all applications that were written before the application framework was -introduced. - -In this last tutorial step we are going to refactor the Plone code in order -to take advantage of the framework. - -Application framework was written in order to simplify the application -development and encapsulate common deployment workflows. This gives things -primitives for application scaling and high availability without the need to -develop them over and over again for each application. - -When using the frameworks, an application developer only has to inherit the -class that best suits him and provide it only with the code that is specific -to the application, while leaving the rest to the framework. -This typically includes: - -* instructions on how to provision the software on each node (server) -* instructions on how to configure the provisioned software -* server group onto which the software should be installed. This may be a - fixed server list, a shared server pool, or a scalable server group that - creates servers using the given instance template, or one of the several - other implementations provided by the framework - -The framework is located in a separate library package -``io.murano.applications`` that is shipped with Murano. We are going to use -the ``apps`` namespace prefix to refer to this namespace through the code. - -Step 1: Add dependency on the App Framework -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In order to use one Murano Package from another, the former must be explicitly -specified as a requirement for the latter. This is done by filling the -``Require`` section in the package's manifest file. - -Open the Plone's manifest.yaml file and append the following lines: - -.. code-block:: yaml - - Require: - io.murano.applications: - -Requirements are specified as a mapping from package name to the desired -version of that package (or version range). The missing value indicates -the dependency on the latest ``0.*.*`` version of the package which is exactly -what we need since the current version of the app framework library is 0. - -Step 2: Get rid of the instance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Since we are going to have a multi-sever Plone application there won't be -a single instance belonging to the application. Instead, we are going to -provide it with the server group that abstracts the server management from -the application. - -So instead of - -.. code-block:: yaml - - Properties: - instance: - Contract: $.class(res:Instance) - -we are going to have - -.. code-block:: yaml - - Properties: - servers: - Contract: $.class(apps:ServerGroup).notNull() - - -Step 3: Change the base classes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Another change that we are going to make to the main application class is -to change its base classes. Regular applications inherit from the -``std:Application`` which only has the method ``deploy`` that does all the -work. - -Application framework provides us with its own implementation of that class and -method. Instead of one monolithic method that does everything, with the -framework, the application provides only the code needed to provision and -configure the software on each server. - -So instead of ``std:Application`` class we are going to inherit two of -the framework classes: - -.. code-block:: yaml - - Extends: - - apps:MultiServerApplicationWithScaling - - apps:OpenStackSecurityConfigurable - -The first class tells us that we are going to have an application that runs -on multiple servers. In the following section we are going to split out -``deploy`` method into two smaller methods that are going to be invoked by -the framework to install the software on each of the servers. By inheriting the -``apps:MultiServerApplicationWithScaling``, the application automatically gets -all the UI buttons to scale it out and in. - -The second class is a mix-in class that tells the framework that we are going -to provide the OpenStack-specific security group configuration for the -application. - - -Step 4: Split the deployment logic -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In this step we are going to split the installation into two phases: -provisioning and configuration. - -Provisioning is implemented by overriding the ``onInstallServer`` method, -which is called every time a new server is added to the server group. In this -method we are going to install the Plone software bits onto the server -(which is provided as a method parameter). - -Configuration is done through the ``onConfigureServer``, which is called -upon the first installation on the server, and every time any of the -application settings change, and ``onCompleteConfiguration`` which is -executed on each server after everything was configured so that we can -perform post-configuration steps like starting application daemons and -reporting messages to the user. - -Thus we are going to split the ``install-plone.sh`` script into two scripts: -``installPlone.sh`` and ``configureServer.sh`` and execute each one in their -corresponding methods: - -.. code-block:: yaml - - onInstallServer: - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(apps:ServerGroup).notNull() - Body: - - $file: sys:Resources.string('installPlone.sh').replace({ - "$1" => $this.deploymentPath, - "$2" => $this.adminPassword - }) - - conf:Linux.runCommand($server.agent, $file) - - onConfigureServer: - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(apps:ServerGroup).notNull() - Body: - - $primaryServer: $serverGroup.getServers().first() - - If: $server = $primaryServer - Then: - - $file: sys:Resources.string('configureServer.sh').replace({ - "$1" => $this.deploymentPath, - "$2" => $primaryServer.ipAddresses[0] - }) - Else: - - $file: sys:Resources.string('configureClient.sh').replace({ - "$1" => $this.deploymentPath, - "$2" => $this.servers.primaryServer.ipAddresses[0], - "$3" => $this.listeningPort}) - - conf:Linux.runCommand($server.agent, $file) - - - onCompleteConfiguration: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(apps:ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $startCommand: format('{0}/zeocluster/bin/plonectl start', $this.deploymentPath) - - $primaryServer: $serverGroup.getServers().first() - - If: $primaryServer in $servers - Then: - - $this.report('Starting DB node') - - conf:Linux.runCommand($primaryServer.agent, $startCommand) - - conf:Linux.runCommand($primaryServer.agent, 'sleep 10') - - - $otherServers: $servers.where($ != $primaryServer) - - If: $otherServers.any() - Then: - - $this.report('Starting Client nodes') - # run command on all other nodes in parallel with pselect - - $otherServers.pselect(conf:Linux.runCommand($.agent, $startCommand)) - - # build an address string with IPs of all our servers - - $addresses: $serverGroup.getServers(). - select( - switch($.assignFloatingIp => $.floatingIpAddress, - true => $.ipAddresses[0]) - + ':' + str($this.listeningPort) - ).join(', ') - - $this.report('Plone listeners are running at ' + str($addresses)) - -During configuration phase we distinguish the first server in the server group -from the rest of the servers. The first server is going to be the primary -node and treated differently from the others. - -Step 5: Configuring OpenStack security group -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The last change to the main class is to set up the security group rules. -We are going to do this by overriding the ``getSecurityRules`` method -that we inherited from the ``apps:OpenStackSecurityConfigurable`` class: - -.. code-block:: yaml - - getSecurityRules: - Body: - - Return: - - FromPort: $this.listeningPort - ToPort: $this.listeningPort - IpProtocol: tcp - External: true - - FromPort: 8100 - ToPort: 8100 - IpProtocol: tcp - External: false - -The code is very similar to that of the old ``deploy`` method with the only -difference being that it returns the rules rather than sets them on its own. - -Step 6: Provide the server group instance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Do you remember, that previously we replaced the ``instance`` property with -``servers`` of type ``apps:ServerGroup``? Since the object is coming from the -UI definition, we must change the latter in order to provide -the class with the ``apps:ServerReplicationGroup`` instance rather than -``resources:Instance``. - -To do this we are going to replace the ``instance`` property in the -Application template with the following snippet: - -.. code-block:: yaml - - servers: - ?: - type: io.murano.applications.ServerReplicationGroup - numItems: $.ploneConfiguration.numNodes - provider: - ?: - type: io.murano.applications.TemplateServerProvider - template: - ?: - type: io.murano.resources.LinuxMuranoInstance - flavor: $.instanceConfiguration.flavor - image: $.instanceConfiguration.osImage - assignFloatingIp: $.instanceConfiguration.assignFloatingIP - serverNamePattern: $.instanceConfiguration.unitNamingPattern - -If you take a closer look at the code above you will find out that the -new declaration is very similar to the old one. But now instead of providing -the ``Instance`` property values directly, we are providing them as a template -for the ``TemplateServerProvider`` server provider. ``ServerReplicationGroup`` -is going to use the provider each time it requires another server. In turn, -the provider is going to use the familiar template for the new instances. - -Besides the instance template we also specify the initial number of Plone -nodes using the ``numItems`` property and the name pattern for the servers. -Thus we must also add it to the list of our controls: - -.. code-block:: yaml - - Forms: - - instanceConfiguration: - fields: - ... - - name: unitNamingPattern - type: string - label: Instance Naming Pattern - required: false - maxLength: 64 - initial: 'plone-{0}' - description: >- - Specify a string, that will be used in instance hostname. - Just A-Z, a-z, 0-9, dash and underline are allowed. - - - ploneConfiguration: - fields: - ... - - name: numNodes - type: integer - label: Initial number of Client Nodes - initial: 1 - minValue: 1 - required: true - description: >- - Select the initial number of Plone Client Nodes - -Step 6: Using server group composition -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By this step we should already have a working Plone application. But let's -go one step further and enhance our sample application. - -Since we are running the database on the first server group server only, -we might want it to have different properties. For example we might want -to give it a bigger flavor or just a special name. This is a perfect -opportunity for us to demonstrate how to construct complex server groups. -All we need to do is to just use another implementation of -``apps:ServerGroup``. Instead of ``apps:ServerReplicationGroup`` we are going -to use the ``apps:CompositeServerGroup`` class, which allows us to compose -several server groups together. One of them is going to be a single-server -server group consisting of our primary server, and the second is going to be -the scalable server group that we used to create in the previous step. - -So again, we change the ``Application`` section of our UI definition file -with even a more advanced ``servers`` property definition: - -.. code-block:: yaml - - servers: - ?: - type: io.murano.applications.CompositeServerGroup - serverGroups: - - ?: - type: io.murano.applications.SingleServerGroup - server: - ?: - type: io.murano.resources.LinuxMuranoInstance - name: format($.instanceConfiguration.unitNamingPattern, 'db') - image: $.instanceConfiguration.image - flavor: $.instanceConfiguration.flavor - assignFloatingIp: $.instanceConfiguration.assignFloatingIp - - ?: - type: io.murano.applications.ServerReplicationGroup - numItems: $.ploneConfiguration.numNodes - provider: - ?: - type: io.murano.applications.TemplateServerProvider - template: - ?: - type: io.murano.resources.LinuxMuranoInstance - flavor: $.instanceConfiguration.flavor - image: $.instanceConfiguration.osImage - assignFloatingIp: $.instanceConfiguration.assignFloatingIP - serverNamePattern: $.instanceConfiguration.unitNamingPattern - -Here the instance definition for the ``SingleServerGroup`` (our primary -server) differs from the servers in the ``ServerReplicationGroup`` by its name -only. However the same technique might be used to customize other properties -as well as to create even more sophisticated server group topologies. For -example, we could implement region bursting by composing several scalable -server groups that allocate servers in different regions. And all of that -without making any changes to the application code itself! diff --git a/doc/source/admin/appdev-guide/step-by-step/plone-admin.png b/doc/source/admin/appdev-guide/step-by-step/plone-admin.png deleted file mode 100644 index a613b8812..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/plone-admin.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/plone-logo.png b/doc/source/admin/appdev-guide/step-by-step/plone-logo.png deleted file mode 100644 index 423f5b468..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/plone-logo.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/plone-ready.png b/doc/source/admin/appdev-guide/step-by-step/plone-ready.png deleted file mode 100644 index d7e0df5e7..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/plone-ready.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/plone-simple-step1.png b/doc/source/admin/appdev-guide/step-by-step/plone-simple-step1.png deleted file mode 100644 index 11b670537..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/plone-simple-step1.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/plone-simple-step2.png b/doc/source/admin/appdev-guide/step-by-step/plone-simple-step2.png deleted file mode 100644 index 956593ee8..000000000 Binary files a/doc/source/admin/appdev-guide/step-by-step/plone-simple-step2.png and /dev/null differ diff --git a/doc/source/admin/appdev-guide/step-by-step/step_by_step.rst b/doc/source/admin/appdev-guide/step-by-step/step_by_step.rst deleted file mode 100644 index 48607dbee..000000000 --- a/doc/source/admin/appdev-guide/step-by-step/step_by_step.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _step-by-step: - -Developing Murano Packages 101 -============================== - -Murano provides a very powerful and flexible platform to automate the -provisioning, deployment, configuration and lifecycle management of -applications in OpenStack clouds. However, the flexibility comes at cost: to -manage an application with Murano one has to design and develop special -scenarios which will tell Murano how to handle different aspects of application -lifecycle. These scenarios are usually called "Murano Applications" or "Murano -Packages". It is not hard to build them, but it requires some time to get -familiar with Murano's DSL to define these scenarios and to learn the common -patterns and best practices. This article provides a basic introductory course -of these aspects and aims to be the starting point for the developers willing -to learn how to develop Murano Application packages with ease. - -The course consists of the following parts: - -.. toctree:: - :maxdepth: 2 - - part1 - part2 - part3 - part4 - -.. #. Creating your first Application Package -.. #. Adding User Interface to your package and other improvements -.. #. Modifying the application to do something useful -.. #. Adding scalability scenarios -.. #. Learning some advanced stuff for production-grade deployments - -Before you proceed, please ensure that you have an OpenStack cloud -(devstack-based will work just fine) and the latest version of Murano deployed. -This guide assumes that the reader has a basic knowledge of some programming -languages and object-oriented design and is a bit familiar with the scripting -languages used to configure Linux servers. Also it would be beneficial to be -familiar with YAML format: lots of software configuration tools nowadays use -YAML, and Murano is no different. - diff --git a/doc/source/admin/appdev-guide/use_cases.rst b/doc/source/admin/appdev-guide/use_cases.rst deleted file mode 100644 index dde1b9a8d..000000000 --- a/doc/source/admin/appdev-guide/use_cases.rst +++ /dev/null @@ -1,272 +0,0 @@ -.. _use-cases: - -========= -Use-cases -========= - -Performing application interconnections -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano can handle application interconnections installed on virtual machines. -The decision of how to combine applications is made by the author of -an application. - -To illustrate the way such interconnection can be configured, -let's analyze the mechanisms applied in WordPress application, which -uses MySql. - -MySql is a very popular database and can be used in quite a number of various -applications. Instead of the creation of a database inside definition of the -WordPress application, it calls the methods from the MySQL class. At the same -time MySQL remains an independent application. - -MySql has a number of methods: - -* ``deploy`` -* ``createDatabase`` -* ``createUser`` -* ``assignUser`` -* ``getConnectionString`` - -In the ``com.example.WordPress`` class definition the database property is a -contact for the ``com.example.databases.MySql`` class. So, the database -configuration methods can be called with the parameters passed by the user -in the main method: - -.. code-block:: yaml - - - $.database.createDatabase($.dbName) - - $.database.createUser($.dbUser, $.dbPassword) - - $.database.assignUser($.dbUser, $.dbName) - -Any other methods of any other class can be invoked the same way to -make the proposal application installation algorithm clear and -constructive. Also, it allows not to duplicate the code in new applications. - -Abstract dependencies between applications ------------------------------------------- -In the example above it is also possible to specify a generic class in the -contract ``com.example.databases.SqlDatabase`` instead of -``com.example.databases.MySql``. It means that an object of any class inherited -from ``com.example.databases.SqlDatabase`` can be passed to a parameter. In -this case you should also use this generic class as a type for a field in -the file ``ui.yaml``: - -.. code-block:: yaml - - Forms: - - appConfiguration: - fields: - - name: database - type: com.example.databases.SqlDatabase - label: Database Server - description: >- - Select a database server to host the application`s database - -After that you can choose any database package in a drop-down box. -The last place, which should be changed in the WordPress package to enable this -feature, is manifest file. It should contain the full name of SQL Library -package and optionally packages inherited from SQL library if you want them to -be downloaded as dependencies. For example: - -.. code-block:: yaml - - Require: - com.example.databases: - com.example.databases.MySql: - com.example.databases.PostgreSql: - -.. note:: - To use this feature you have to enable Glare as a storage for your packages - and a version of your murano-dashboard should be not older than newton. - -Using application already installed on the image -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Suppose you have everything already prepared on image. And you want to share this -image with others. This problem can be solved in several ways. - -Let's use the -`HDPSandbox `_ -application to illustrate how this can be done with Murano. - -.. note:: - An image may not contain murano-agent at all. - -Prepare an application package of the structure: - -:: - - |_ Classes - | |_ HDPSandbox.yaml - | - |_ UI - | |_ ui.yaml - | - |_ logo.png - -.. note:: - - The ``Resources`` folder is not included in the package since the image - contains everything that user expects. So no extra instructions are needed - to be executed on murano-agent. - -UI is provided for specifying the application name, which is used for the application -recognition in logging. And what is more, it contains the image name as a deployment -instruction template (object model) in the ``Application`` section: - -.. code-block:: yaml - :linenos: - - Application: - ?: - type: com.example.HDPSandbox - name: $.appConfiguration.name - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - name: generateHostname($.instanceConfiguration.unitNamingPattern, 1) - flavor: $.instanceConfiguration.flavor - image: 'hdp-sandbox' - assignFloatingIp: true - -Moreover, the unsupported flavors can be specified here, so that the user can -select only from the valid ones. Provide the requirements in the -corresponding section to do this: - -.. code-block:: yaml - - requirements: - min_disk: 50 (Gb) - min_memory_mb: 4096 (Mb) - min_vcpus: 1 - -After the UI form creation, and the HDPSandbox application deployment, -the VM with the predefined image is spawned. Such type of applications may -interact with regular applications. Thus, if you have an image with Puppet, -you can call the ``deploy`` method of the Puppet application and then puppet -manifests or any shell scripts on the freshly spawned VM. - -The presence of the logo.png should never be underestimated, since it helps to make -your application recognizable among other applications included in the catalog. - -Interacting with non-OpenStack services -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section tells about the interaction between an application and any non-OpenStack -services, that have an API. - -External load-balancer ----------------------- -Suppose, you have powerful load-balancer on a real server. And you want to run -the application on an OpenStack VM. Murano can set up new applications to be managed -by that external load-balancer (LB). Let's go into more details. - -To implement this case the following apps are used: - -* ``LbApp``: its class methods call LB API - -* ``WebApp``: runs on the real LB - -Several instances of ``WebApp`` are deployed with each of them calling -two methods: - -.. code-block:: yaml - - - $.loadBalancer.createPool() - - $.loadBalancer.addMember($instance) - # where $.loadBalancer is an instance of the LbApp class - -The first method creates a pool and associates it with a virtual server. -This happens once only. The second one registers a member in the newly created pool. - -It is also possible to perform other modifications to the LB configuration, -which are only restricted by the LB API functionality. - -So, you need to specify the maximum instance number in the UI form related to the -``WebApp`` application. All of them are subsequently added to the LB pool. -After the deployment, the LB virtual IP, by which an application is accessible, -is displayed. - -Configuring Network Access for VMs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, each VM instance deployed by ``io.murano.resources.Instance`` class -or its descendants joins an environment's default network. This network gets -created when the Environment is deployed for the first time, a subnet is -created in it and is uplinked to a router which is detected automatically based -on its name. - -This behavior may be overridden in two different ways. - -Using existing network as environment's default ------------------------------------------------ - -This option is available for users when they create a new environment in the -Dashboard. A dropdown control is displayed next to the input field prompting -for the name of environment. By default this control provides to create a new -network, but the user may opt to choose some already existing network to be the -default for the environment being created. If the network has more than one -subnet, the list will include all the available options with their CIDRs -shown. The selected network will be used as environment's default, so no new -network will be created. - -.. note:: - - Murano does not check the configuration or topology of the network selected - this way. It is up to the user to ensure that the network is uplinked to some - external network via a router - otherwise the murano engine will not be able - to communicate with the agents on the deployed VMs. If the Applications being - deployed require internet connectivity it is up to the user to ensure that - this net provides it, than DNS nameservers are set and accessible etc. - - -Modifying the App UI to prompt user for network ------------------------------------------------ - -The application package may be designed to ask user about the network they want -to use for the VMs deployed by this particular application. This allows to -override the default environment's network setting regardless of its value. - -To do this, application developer has to include a ``network`` field into the -Dynamic UI definition of the app. The value returned by this field is a tuple -of network_id and a subnet_id. This values may be passed as the -input properties for ``io.murano.resources.ExistingNeutronNetwork`` object -which may be in its turn passed to an instance of -``io.murano.resources.Instance`` as its network configuration. - -The UI definition may look like this: - -.. code-block:: yaml - - Templates: - customJoinNet: - - ?: - type: io.murano.resources.ExistingNeutronNetwork - internalNetworkName: $.instanceConfiguration.network[0] - internalSubnetworkName: $.instanceConfiguration.network[1] - Application: - ?: - type: com.example.someApplicationName - instance: - ?: - type: io.murano.resources.LinuxMuranoInstance - networks: - useEnvironmentNetwork: $.instanceConfiguration.network[0]=null - useFlatNetwork: false - customNetworks: switch($.instanceConfiguration.network[0], $=null=>list(), $!=null=>$customJoinNet) - Forms: - - instanceConfiguration: - fields: - - name: network - type: network - label: Network - description: Select a network to join. 'Auto' corresponds to a default environment's network. - required: false - murano_networks: translate - -For more details on the Dynamic UI its controls and templates please refer to -its :ref:`specification `. - - - diff --git a/doc/source/admin/config-wsgi.rst b/doc/source/admin/config-wsgi.rst deleted file mode 100644 index 92d2d36a9..000000000 --- a/doc/source/admin/config-wsgi.rst +++ /dev/null @@ -1,112 +0,0 @@ -Installing Murano API via WSGI -============================== - -This document is a guide to deploy murano using two WSGI mode uwsgi and -mod_wsgi of Apache. - -Please note that if you intend to use mode uwsgi, you should install -``mode_proxy_uwsgi`` module. For example on deb-base system: - -.. code-block:: console - - # sudo apt-get install libapache2-mod-proxy-uwsgi - # sudo a2enmod proxy - # sudo a2enmod proxy_uwsgi - -.. end - -WSGI Application ----------------- - -The function ``murano.httpd.init_application`` will setup a WSGI application -to run behind uwsgi and mod_wsgi - -Murano API behind uwsgi ------------------------ - -Create a ``murano-api-uwsgi`` file with content below: - -.. code-block:: ini - - [uwsgi] - chmod-socket = 666 - socket = /var/run/uwsgi/murano-wsgi-api.socket - lazy-apps = true - add-header = Connection: close - buffer-size = 65535 - hook-master-start = unix_signal:15 gracefully_kill_them_all - thunder-lock = true - plugins = python - enable-threads = true - worker-reload-mercy = 90 - exit-on-reload = false - die-on-term = true - master = true - processes = 2 - wsgi-file = /murano-wsgi-api - -.. end - -Start murano-api: - -.. code-block:: console - - # uwsgi --ini /etc/murano/murano-api-uwsgi.ini - -.. end - -Murano API behind mod_wsgi --------------------------- - -Create ``/etc/apache2/murano.conf`` with content below: - -.. code-block:: ini - - Listen 8082 - - - WSGIDaemonProcess murano-api processes=1 threads=10 user=%USER% display-name=%{GROUP} %VIRTUALENV% - WSGIProcessGroup murano-api - WSGIScriptAlias / %MURANO_BIN_DIR%/murano-wsgi-api - WSGIApplicationGroup %{GLOBAL} - WSGIPassAuthorization On - AllowEncodedSlashes On - = 2.4> - ErrorLogFormat "%{cu}t %M" - - ErrorLog /var/log/%APACHE_NAME%/murano_api.log - CustomLog /var/log/%APACHE_NAME%/murano_api_access.log combined - - - = 2.4> - Require all granted - - - Order allow,deny - Allow from all - - - - -.. end - -Then on deb-based systems copy or symlink the file to ``/etc/apache2/sites-available``. -For rpm-based systems the file will go in ``/etc/httpd/conf.d``. - -Enable the murano site. On deb-based systems: - -.. code-block:: console - - # a2ensite murano - # systemctl reload apache2.service - -.. end - -On rpm-based systems: - -.. code-block:: console - - # systemctl reload httpd.service - -.. end - diff --git a/doc/source/admin/configure_cloud_foundry_service_broker.rst b/doc/source/admin/configure_cloud_foundry_service_broker.rst deleted file mode 100644 index e2dc877f9..000000000 --- a/doc/source/admin/configure_cloud_foundry_service_broker.rst +++ /dev/null @@ -1,208 +0,0 @@ -.. _configure_service_broker: - -======================================= -Murano service broker for Cloud Foundry -======================================= - -Service broker overview ------------------------ - -Service broker is a new murano component which implements `Cloud Foundry -`_ Service Broker API. - -This lets users build 'hybrid' infrastructures that are services like databases, message -queues, key/value stores, and so on. This services can be uploaded and deployed with -murano and made available to Cloud Foundry apps on demand. The result is lowered cost, -shorter timetables, and quicker access to required tools — developers can 'self serve' -by building any required service, then make it instantly available in Cloud Foundry. - -Configure service broker ------------------------- - -Manual installation -~~~~~~~~~~~~~~~~~~~ - -If you use local murano installation, you can configure and run murano service -broker in a few simple steps: - -#. Change into the murano directory: - - .. code-block:: console - - cd ~/murano/murano - -#. Generate the murano service broker config file. - Murano service broker has a common config file for service broker API services. - Using tox, generate a sample configuration file: - - .. code-block:: console - - tox -e gencfconfig - -#. Copy the configuration file for further modifications: - - .. code-block:: console - - cd ~/murano/murano/etc/murano - ln -s murano-cfapi.conf.sample murano-cfapi.conf - -#. Edit ``murano-cfapi.conf``. Below is an example of the basic - settings you may need to configure. - - .. note:: - - The example below uses the SQLite database. Edit the **[database]** - section to use another database. - - .. code-block:: ini - - [DEFAULT] - debug = true - verbose = true - - ... - - [database] - backend = sqlalchemy - connection = sqlite:///murano_cfapi.sqlite - - ... - - [keystone_authtoken] - www_authenticate_uri = 'http://%OPENSTACK_HOST_IP%:5000/v3' - auth_host = '%OPENSTACK_HOST_IP%' - auth_port = 5000 - auth_protocol = http - admin_tenant_name = %OPENSTACK_ADMIN_TENANT% - admin_user = %OPENSTACK_ADMIN_USER% - admin_password = %OPENSTACK_ADMIN_PASSWORD% - - ... - - [cfapi] - tenant = %TENANT_NAME% - bind_host = %HOST_IP% - bind_port = 8083 - auth_url = 'http://%OPENSTACK_HOST_IP%:5000/v3' - - - .. note:: - - The ``bind_host`` IP should be in the same network as the - Cloud Foundry instance. - -#. Create database tables for murano service broker: - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-cfapi-db-manage \ - --config-file ./etc/murano/murano-cfapi.conf upgrade - -#. Launch the murano service broker API in a separate terminal: - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-cfapi --config-file ./etc/murano/murano-cfapi.conf - - .. note:: - - Run the command in a new terminal as the process will be running in - the terminal until you terminate it, therefore, blocking the current - terminal. - -Devstack installation -~~~~~~~~~~~~~~~~~~~~~ - -It is really easy to enable service broker in your devstack installation. -You need simply update your ``local.conf`` with the following: - - .. code-block:: ini - - [[local|localrc]] - enable_plugin murano https://opendev.org/openstack/murano - enable_service murano-cfapi - -How to use service broker -------------------------- - -After service broker is configured and started you have nothing to do with service -broker from murano side - it is an adapter which is used by Cloud Foundry PaaS. - -To access and use murano packages through Cloud Foundry, you need to perform following steps: - -#. Log in to Cloud Foundry instance via ssh. - - .. code-block:: console - - ssh -i @ - -#. Log in to Cloud Foundry itself. - - .. code-block:: console - - cf login -a https://api..xip.io -u -p - -#. Add murano service broker. - - .. code-block:: console - - cf create-service-broker http://:8083 - -#. Enable access to murano packages. - - .. code-block:: console - - cf enable-service-access - - .. warning:: - - By default, access to all services is prohibited. - - .. note:: - - You can use ``service-access`` command to see human-readable list of packages. - -#. Provision murano service through Cloud Foundry. - - .. code-block:: console - - cf create-service 'Apache HTTP Server' default MyApacheInstance -c apache.json - - .. code-block:: json - - { - "instance": { - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance" - }, - "keyname": "nstarodubtsev", - "assignFloatingIp": "True", - "name": "", - "availabilityZone": "nova", - "image": "1b9ff37e-dff3-4308-be08-9185705dad91" - }, - "enablePHP": "True" - } - -Known issues ------------- - -* `Hard to deploy complex apps - `_ - -Useful links ------------- - -Here is the list of the links for Cloud Foundry documentation which you might need: - -#. `Cloud Foundry development version launcher - `_ - -#. `How to manage Cloud Foundry service brokers - `_ - -#. `Cloud Foundry CLI docs - `_ diff --git a/doc/source/admin/deploy_murano.rst b/doc/source/admin/deploy_murano.rst deleted file mode 100644 index 2a86a8a98..000000000 --- a/doc/source/admin/deploy_murano.rst +++ /dev/null @@ -1,11 +0,0 @@ -================ -Deploying murano -================ - -.. toctree:: - :maxdepth: 2 - - deploy_murano/prerequisites - deploy_murano/devstack - deploy_murano/install_manually - deploy_murano/configure_ssl diff --git a/doc/source/admin/deploy_murano/configure_ssl.rst b/doc/source/admin/deploy_murano/configure_ssl.rst deleted file mode 100644 index 52746d02a..000000000 --- a/doc/source/admin/deploy_murano/configure_ssl.rst +++ /dev/null @@ -1,111 +0,0 @@ -============= -Configure SSL -============= - -Murano components can work with SSL. This section provides information on -how to set SSL properly. - -Configure SSL for Murano API -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To configure SSL for the Murano API service, modify the ``[ssl]`` section in ``/etc/murano/murano.conf``: - -.. code-block:: ini - - [ssl] - cert_file = - key_file = - ca_file = - -.. list-table:: - :widths: 10 25 - :header-rows: 1 - - * - Parameter - - Description - * - ``cert_file`` - - A path to the certificate file the server should use when binding to an SSL-wrapped socket. - * - ``key_file`` - - A path to the private key file the server should use when binding to an SSL-wrapped socket. - * - ``ca_file`` - - A path to the CA certificate file the server should use to validate client certificates provided during an SSL handshake. This parameter is ignored if the ``cert_file`` and ``key_file`` parameters are not set. - -Murano API starts using SSL automatically after you point to the HTTPS protocol -instead of HTTP during the registration of the Murano API service -in endpoints, modifying the ``publicurl`` argument to start with ``https://``. - -SSL for Murano API is implemented the same way as in any other OpenStack -component. See `ssl python module -`_ for details. - -Configure SSL for RabbitMQ -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -All murano components communicate with each other using RabbitMQ. -By default, all messages in RabbitMQ are not encrypted. You can encrypt -this interaction with SSL. Configure each RabbitMQ exchange separately. - -Murano API <-> RabbitMQ <-> Murano engine ------------------------------------------ - -Modify the ``[default]`` section in the ``/etc/murano/murano.conf`` file: - -#. Enable SSL for RabbitMQ: - - .. code-block:: ini - - # connect over SSL for RabbitMQ (boolean value) - rabbit_use_ssl = true - - -#. Set the ``kombu`` parameters. - - Specify the paths to the SSL key file and SSL CA certificate in a regular - ```` format without quotes or leave them empty to enable - self-signed certificates: - - .. code-block:: ini - - # SSL version to use (valid only if SSL enabled). valid values - # are TLSv1, SSLv23 and SSLv3. SSLv2 may be available on some - # distributions (string value) - kombu_ssl_version = - - # SSL key file (valid only if SSL enabled) (string value) - kombu_ssl_keyfile = - - # SSL cert file (valid only if SSL enabled) (string value) - kombu_ssl_certfile = - - # SSL certification authority file (valid only if SSL enabled) - # (string value) - kombu_ssl_ca_certs = - -Murano agent -> RabbitMQ ------------------------- - -To encrypt the communication between the murano agent and RabbitMQ, -set ``ssl = True`` in the ``[rabbitmq]`` section of -``/etc/murano/murano.conf``: - -.. code-block:: ini - - [rabbitmq] - ... - ssl = True - insecure = False - -If you want to configure the murano agent differently, you need to change -the `default template `_ located in the murano core library. -After you finish with the template modification, verify that you zip and -re-upload the murano core library. - -Configure SSL for the Dashboard -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you do not plan to use self-signed certificates, no additional -configurations are required. Just point your web browser to the URL -starting with ``https://``. - -Otherwise, set the ``MURANO_API_INSECURE`` parameter to ``True`` in -``/etc/openstack-dashboard/local_settings.py``. diff --git a/doc/source/admin/deploy_murano/devstack.rst b/doc/source/admin/deploy_murano/devstack.rst deleted file mode 100644 index e7f612657..000000000 --- a/doc/source/admin/deploy_murano/devstack.rst +++ /dev/null @@ -1,67 +0,0 @@ -============================== -Integrate murano with DevStack -============================== - -You can install murano with DevStack. The `murano/devstack`_ directory -in the murano repository contains the files necessary to integrate murano -with `DevStack`_. - -To install the development version of an OpenStack environment -with murano, proceed with the following steps: - -#. Download DevStack: - - .. code-block:: console - - git clone https://opendev.org/openstack/devstack - cd devstack - -#. Edit ``local.conf`` to enable murano DevStack plug-in: - - .. code-block:: console - - > cat local.conf - [[local|localrc]] - enable_plugin murano https://opendev.org/openstack/murano - -#. If you want to enable Murano Cloud Foundry Broker API service, add the - following line to ``local.conf``: - - .. code-block:: ini - - enable_service murano-cfapi - -#. If you want to use Glare Artifact Repository as a strorage for packages, - add the following line to ``local.conf``: - - .. code-block:: ini - - enable_service g-glare - - For more information on how to use Glare Artifact Repository, - see :ref:`glare_usage`. - -#. (Optional) To import murano packages when DevStack is up, define an ordered - list of FQDN packages in ``local.conf``. Verify that you list all package - dependencies. These packages will be imported from the ``murano-apps`` - git repository by default. For example: - - .. code-block:: ini - - MURANO_APPS=com.example.apache.Tomcat,com.example.Guacamole - - To configure the git repository that will be used as the source for - the imported packages, configure the ``MURANO_APPS_REPO`` and - ``MURANO_APPS_BRANCH`` variables. - -#. Run DevStack: - - .. code-block:: console - - ./stack.sh - -**Result:** Murano has installed with DevStack. - -.. Links -.. _DevStack: https://docs.openstack.org/devstack/latest/ -.. _murano/devstack: https://opendev.org/openstack/murano/src/branch/master/devstack diff --git a/doc/source/admin/deploy_murano/install_manually.rst b/doc/source/admin/deploy_murano/install_manually.rst deleted file mode 100644 index e9098996e..000000000 --- a/doc/source/admin/deploy_murano/install_manually.rst +++ /dev/null @@ -1,385 +0,0 @@ -.. _install_manually: - -======================= -Install murano manually -======================= - -Before you install Murano, verify that you completed the following tasks: - -#. Install software prerequisites depending on the operating system you use - as described in the System prerequisites section. - - .. TODO (OG): add ref to System prerequisites when it is ready - -#. Install tox: - - .. code-block:: console - - sudo pip install tox - -#. Install and configure a database. - - Murano can use various database types on back end. For development - purposes, use SQLite. For production installations, consider using - MySQL database. - - .. warning:: - - Murano supports PostgreSQL as well. Though, use it with caution - as it has not been thoroughly tested yet. - - Before you can use MySQL database, proceed with the following: - - #. Install MySQL: - - .. code-block:: console - - apt-get install mysql-server - - #. Create an empty database: - - Replace %MURANO_DB_PASSWORD% with the actual password. For example, - 'admin'. - - .. code-block:: console - - mysql -u root -p - - mysql> CREATE DATABASE murano; - mysql> GRANT ALL PRIVILEGES ON murano.* TO 'murano'@'localhost' \ - IDENTIFIED BY %MURANO_DB_PASSWORD%; - mysql> exit; - -Install the API service and engine -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. Create a folder to which all murano components will be stored: - - .. code-block:: console - - mkdir ~/murano - -#. Clone the murano git repository to the management server: - - .. code-block:: console - - cd ~/murano - git clone https://opendev.org/openstack/murano - -#. Create the configuration file. Murano has a common configuration - file for API and engine services. - - #. Generate a sample configuration file using tox: - - .. code-block:: console - - cd ~/murano/murano - tox -e genconfig - - #. Create a copy of ``murano.conf`` for further modifications: - - .. code-block:: console - - cd ~/murano/murano/etc/murano - cp murano.conf.sample murano.conf - -#. Edit the ``murano.conf`` file. An example below contains the basic - configuration. - - .. note:: - - The example uses MySQL database. If you want to use another - database type, edit the ``[database]`` section correspondingly. - - Replace items in "%" with the actual values. For example, replace - %RABBITMQ_SERVER_IP% with 127.0.0.1. So, the complete row with the - replaced value will be rabbit_host = 127.0.0.1 - - .. code-block:: ini - - [DEFAULT] - debug = true - verbose = true - rabbit_host = %RABBITMQ_SERVER_IP% - rabbit_userid = %RABBITMQ_USER% - rabbit_password = %RABBITMQ_PASSWORD% - rabbit_virtual_host = %RABBITMQ_SERVER_VIRTUAL_HOST% - - ... - - [database] - connection = mysql+pymysql://murano:%MURANO_DB_PASSWORD%@127.0.0.1/murano - - ... - - [keystone] - auth_url = 'http://%OPENSTACK_HOST_IP%:5000' - - ... - - [keystone_authtoken] - www_authenticate_uri = 'http://%OPENSTACK_HOST_IP%:5000' - auth_host = '%OPENSTACK_HOST_IP%' - auth_port = 5000 - auth_protocol = http - admin_tenant_name = %OPENSTACK_ADMIN_TENANT% - admin_user = %OPENSTACK_ADMIN_USER% - admin_password = %OPENSTACK_ADMIN_PASSWORD% - - ... - - [murano] - url = http://%YOUR_HOST_IP%:8082 - - [rabbitmq] - host = %RABBITMQ_SERVER_IP% - login = %RABBITMQ_USER% - password = %RABBITMQ_PASSWORD% - virtual_host = %RABBITMQ_SERVER_VIRTUAL_HOST% - - [networking] - default_dns = 8.8.8.8 # In case OpenStack neutron has no default - # DNS configured - - [oslo_messaging_notifications] - driver = messagingv2 - -#. Create a virtual environment and install murano prerequisites - using **tox**. The virtual environment will be created under - the ``tox`` directory. - - #. Install MySQL driver since it is not a part of the murano requirements: - - .. code-block:: console - - tox -e venv -- pip install PyMYSQL - - #. Create database tables for murano: - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-db-manage \ - --config-file ./etc/murano/murano.conf upgrade - - #. Launch the murano API in a separate terminal: - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-api --config-file ./etc/murano/murano.conf - - .. note:: - - Run the command in a new terminal as the process will be running in - the terminal until you terminate it, therefore, blocking the current - terminal. - - #. Leaving the API process running, return to the previous console and - import murano core library and other libraries from the `meta` - directory: - - .. code-block:: console - - cd ~/murano/murano/meta/ - for i in */; do pushd ./"$i"; zip -r ../../"${i%/}.zip" *; popd; done - cd .. - tox -e venv -- murano --os-username %OPENSTACK_ADMIN_USER% \ - --os-password %OPENSTACK_ADMIN_PASSWORD% \ - --os-auth-url http://%OPENSTACK_HOST_IP%:5000 \ - --os-project-name %OPENSTACK_ADMIN_TENANT% \ - --murano-url http://%MURANO_IP%:8082 \ - package-import --is-public *.zip - rm *.zip - - #. Launch the murano engine in a separate terminal: - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-engine --config-file ./etc/murano/murano.conf - - .. note:: - - Run the command in a new terminal as the process will be running in - the terminal until you terminate it, therefore, blocking the current - terminal. - -Register in keystone -~~~~~~~~~~~~~~~~~~~~ - -To make the murano API available to all OpenStack users, you need to register -the Application Catalog service within the Identity service. - -#. Add the ``application-catalog`` service to keystone: - - .. code-block:: console - - openstack service create --name murano --description \ - "Application Catalog for OpenStack" application-catalog - -#. Provide an endpoint for this service: - - .. code-block:: console - - openstack endpoint create --region RegionOne --publicurl 'http://%MURANO_IP%:8082/' \ - --adminurl 'http://%MURANO_IP%:8082/' --internalurl 'http://%MURANO_IP%:8082/' \ - %MURANO_SERVICE_ID% - - where ``MURANO-SERVICE-ID`` is the unique service number that can be found - in the :command:`openstack service create` output. - - .. note:: - - URLs (``--publicurl``, ``--internalurl``, and ``--adminurl`` values) - may differ depending on your environment. - -Install the murano dashboard -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section describes how to install and run the murano dashboard. - -#. Clone the repository with the murano dashboard: - - .. code-block:: console - - cd ~/murano - git clone https://opendev.org/openstack/murano-dashboard - -#. Clone the ``horizon`` repository: - - .. code-block:: console - - git clone https://opendev.org/openstack/horizon - -#. Create a virtual environment and install ``muranodashboard`` - as an editable module: - - .. code-block:: console - - cd horizon - tox -e venv -- pip install -e ../murano-dashboard - -#. Prepare local settings. - - .. code-block:: console - - cp openstack_dashboard/local/local_settings.py.example \ - openstack_dashboard/local/local_settings.py - - - For more information, check out the official - `horizon documentation `_. - -#. Enable and configure Murano dashboard in the OpenStack Dashboard: - - * For the Newton (and later) OpenStack installations, copy plug-in file - local settings files, and policy files: - - .. code-block:: console - - cp ../murano-dashboard/muranodashboard/local/enabled/*.py \ - openstack_dashboard/local/enabled/ - - cp ../murano-dashboard/muranodashboard/local/local_settings.d/*.py \ - openstack_dashboard/local/local_settings.d/ - - cp ../murano-dashboard/muranodashboard/conf/* openstack_dashboard/conf/ - - * For the OpenStack installations prior to the Newton release, run: - - .. code-block:: console - - cp ../murano-dashboard/muranodashboard/local/_50_murano.py \ - openstack_dashboard/local/enabled/ - - Customize local settings of your horizon installation, by editing the - ``openstack_dashboard/local/local_settings.py`` file: - - .. code-block:: python - - ... - ALLOWED_HOSTS = '*' - - # Provide your OpenStack Lab credentials - OPENSTACK_HOST = '%OPENSTACK_HOST_IP%' - - ... - - DEBUG_PROPAGATE_EXCEPTIONS = DEBUG - - Change the default session back end from browser cookies to database - to avoid issues with forms during the applications creation: - - .. code-block:: python - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'murano-dashboard.sqlite', - } - } - - SESSION_ENGINE = 'django.contrib.sessions.backends.db' - - -#. (Optional) If you do not plan to get the murano service from the keystone - application catalog, specify where the ``murano-api`` service is running: - - .. code-block:: python - - MURANO_API_URL = 'http://%MURANO_IP%:8082' - -#. (Optional) If you have set up the database as a session back end (this is - done by default with murano local_settings file starting with Newton), - perform database migration: - - .. code-block:: console - - tox -e venv -- python manage.py migrate --noinput - - Since a separate user is not required for development purpose, - you can reply ``no``. - -#. Run Django server at ``127.0.0.1:8000`` or provide a different ``IP`` - and ``PORT`` parameters: - - .. code-block:: console - - tox -e venv -- python manage.py runserver - - .. note:: - - The development server restarts automatically on every code change. - -**Result:** The murano dashboard is available at ``http://IP:PORT``. - -Import murano applications -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To fill the application catalog, you need to import applications to your -OpenStack environment. You can import applications using the murano dashboard, -as well as the command-line client. - -To import applications using CLI, complete the following tasks: - -#. Clone the murano apps repository: - - .. code-block:: console - - cd ~/murano - git clone https://opendev.org/openstack/murano-apps - -#. Import every package you need from this repository by running - the following command: - - .. code-block:: console - - cd ~/murano/murano - pushd ../murano-apps/Docker/Applications/%APP-NAME%/package - zip -r ~/murano/murano/app.zip * - popd - tox -e venv -- murano --murano-url http://%MURANO_IP%:8082 package-import app.zip - -**Result:** The applications are imported and available from the application -catalog. diff --git a/doc/source/admin/deploy_murano/prerequisites.rst b/doc/source/admin/deploy_murano/prerequisites.rst deleted file mode 100644 index 61d92fc1f..000000000 --- a/doc/source/admin/deploy_murano/prerequisites.rst +++ /dev/null @@ -1,178 +0,0 @@ -=================== -System requirements -=================== - -This section provides basic information about the murano environment system -requirements. Additionally, it contains a description of the performance -test scenario, which you may use to check if your hardware fits -the requirements. To do this, run the test and compare the results with -the baseline data provided. - -Software prerequisites -~~~~~~~~~~~~~~~~~~~~~~ - -Before you install murano, verify your system meets the following -prerequisites. - -**Supported operating systems:** - -* Ubuntu Server -* RHEL/CentOS -* Debian - -**System packages for Ubuntu:** - -* gcc -* python3-pip -* python3-dev -* libxml2-dev -* libxslt-dev -* libffi-dev -* libpq-dev -* python3-openssl -* mysql-client - -**System packages for CentOS:** - -* gcc -* python3-pip -* python3-devel -* libxml2-devel -* libxslt-devel -* libffi-devel -* postgresql-devel -* pyOpenSSL -* mysql - -Hardware requirements -~~~~~~~~~~~~~~~~~~~~~ - -We recommend that your system meets the following hardware requirements: - -+------------+--------------------------------+----------------------+ -| Criteria | Minimal | Recommended | -+============+================================+======================+ -| CPU | 4 core @ 2.4 GHz | 24 core @ 2.67 GHz | -+------------+--------------------------------+----------------------+ -| RAM | 8 GB | 24 GB or more | -+------------+--------------------------------+----------------------+ -| HDD | 2 x 500 GB (7200 rpm) | 4 x 500 GB (7200 rpm)| -+------------+--------------------------------+----------------------+ -| RAID | Software RAID-1 (use mdadm as | Hardware RAID-10 | -| | it improves the read | | -| | performance almost twice) | | -+------------+--------------------------------+----------------------+ - -Other possible storage configurations: - -* 1x SSD 500+ GB - -* 1x HDD (7200 rpm) 500+ GB and 1x SSD 250+ GB (install the system onto - the HDD and mount the SSD drive to the directory where the virtual - machines images are stored) - -* 1x HDD (15000 rpm) 500+ GB - -Testing the performance -~~~~~~~~~~~~~~~~~~~~~~~ - -We have measured the time required to boot 1 to 5 instances of the Windows -operating system simultaneously. You can use this data as the baseline -to check if your system is fast enough. - -.. note:: - - Use *sysprepped* images for this test to simulate an instance first boot. - -To reproduce the performance test, proceed with the following steps: - -#. Prepare a Windows 2012 Standard (with GUI) image in the ``QCOW2`` format. - This example uses the ``ws-2012-std.qcow2`` image. - -#. Verify that there are no KVM processes running on the host: - - .. code-block:: console - - ps aux | grep kvm - -#. Make 5 copies of the Windows image file: - - .. code-block:: console - - for i in $(seq 5); do \ - cp ws-2012-std.qcow2 ws-2012-std-$i.qcow2; done - -#. Create the ``start-vm.sh`` script in the directory with the ``.qcow2`` - files: - - .. code-block:: console - - #!/bin/bash - [ -z $1 ] || echo "VM count not provided!"; exit 1 - for i in $(seq $1); do - echo "Starting VM $i ..." - kvm -m 1024 -drive file=ws-2012-std-$i.qcow2,if=virtio -net user -net nic,model=virtio -nographic -usbdevice tablet -vnc :$i & done - -#. Start ONE instance using the command below (as root) and measure time - between the instance launch and the moment when the Server Manager window - displays. - - .. code-block:: console - - sudo ./start-vm.sh 1 - - To view the instance desktop, connect with VNC viewer to your host - to VNC screen :1 (port 5901). - -#. Turn off the instance. You may simply kill all KVM processes by running: - - .. code-block:: console - - sudo killall kvm - -#. Start FIVE instances with the command below (as root) and measure time - interval between ALL instances launch and the moment when the LAST - Server Manager window displays. - - .. code-block:: console - - sudo ./start-vm.sh 5 - - To view VM's desktops, connect with VNC viewer to your - host to VNC screens :1 thru :5 (ports 5901-5905). - -#. Turn off the instances. You may simply kill all KVM processes by running: - - .. code-block:: console - - sudo killall kvm - -Baseline data -------------- - -The table below provides the baseline data that was received in our test -murano environment. - -+--------------------------+--------------------------+---------------------+ -| | Boot ONE instance | Boot FIVE instances | -+==========================+==========================+=====================+ -| Avg. Time | 3m:40s | 8m | -+--------------------------+--------------------------+---------------------+ -| Max. Time | 5m | 20m | -+--------------------------+--------------------------+---------------------+ - -**Avg. Time** - Refers to the environment with the recommended hardware configuration - -**Max. Time** - Refers to the minimal hardware configuration - -Host optimizations ------------------- - -You can improve your default KVM installation performance with the following -optimizations up to 30%: - -* Change the default scheduler from **CFQ** to **Deadline** -* Use **ksm** -* Use **vhost-net** diff --git a/doc/source/admin/figures/add-interface.png b/doc/source/admin/figures/add-interface.png deleted file mode 100644 index df90deb5e..000000000 Binary files a/doc/source/admin/figures/add-interface.png and /dev/null differ diff --git a/doc/source/admin/figures/deploy-log.png b/doc/source/admin/figures/deploy-log.png deleted file mode 100644 index be3b2f8ad..000000000 Binary files a/doc/source/admin/figures/deploy-log.png and /dev/null differ diff --git a/doc/source/admin/figures/network-topology-1.png b/doc/source/admin/figures/network-topology-1.png deleted file mode 100644 index 6c8081883..000000000 Binary files a/doc/source/admin/figures/network-topology-1.png and /dev/null differ diff --git a/doc/source/admin/figures/network-topology-2.png b/doc/source/admin/figures/network-topology-2.png deleted file mode 100644 index f85e86450..000000000 Binary files a/doc/source/admin/figures/network-topology-2.png and /dev/null differ diff --git a/doc/source/admin/figures/new-inst.png b/doc/source/admin/figures/new-inst.png deleted file mode 100644 index 7d95fd672..000000000 Binary files a/doc/source/admin/figures/new-inst.png and /dev/null differ diff --git a/doc/source/admin/index.rst b/doc/source/admin/index.rst deleted file mode 100644 index e70d163f9..000000000 --- a/doc/source/admin/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. index:: Murano Administrator Guide - -.. _admin-guide: - -Deploying Murano -~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - deploy_murano - prepare_lab - murano_policies - manage_packages - manage_images - manage_categories - murano_repository - murano_agent - policy_enf - using_glare.rst - net_configuration - configure_cloud_foundry_service_broker - admin_troubleshooting - appdev-guide/developer_index - config-wsgi diff --git a/doc/source/admin/manage_categories.rst b/doc/source/admin/manage_categories.rst deleted file mode 100644 index ca80e7dbb..000000000 --- a/doc/source/admin/manage_categories.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _manage-categories: - -=================== -Managing categories -=================== diff --git a/doc/source/admin/manage_images.rst b/doc/source/admin/manage_images.rst deleted file mode 100644 index fea673b60..000000000 --- a/doc/source/admin/manage_images.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _manage-images: - -=============== -Managing images -=============== - -Build an image -~~~~~~~~~~~~~~ - -Manage images -~~~~~~~~~~~~~ diff --git a/doc/source/admin/manage_packages.rst b/doc/source/admin/manage_packages.rst deleted file mode 100644 index c0a056718..000000000 --- a/doc/source/admin/manage_packages.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. _manage-packages: - -================= -Managing packages -================= - -Managing packages on engine side -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To get access to the contents of murano packages, ``murano-engine`` queries -``murano-api``. However, it is also possible to specify a list of directories -that may contain packages locally. This option is useful to speed up -debugging and development of packages and/or to save bandwidth between the API -and the engine. If local directories are specified, they are examined before -querying the API. - -Local package directories -------------------------- - -To define a list of directories where the engine would look for package files, -set the ``load_packages_from`` option in the ``engine`` section -of the :file:`murano.conf` configuration file. This option can be set to a -comma-separated list of directory paths. Whenever an engine needs to access a -package, it would inspect these directories first, before accessing -``murano-api``. - -API package cache ------------------ - -If the package was not found in any of the ``load_packages_from`` directories, -or if none were specified, then ``murano-engine`` queries API for package -contents. -Whenever ``murano-engine`` downloads a package from API, it stores and unpacks -it locally. The engine uses the directory defined in the ``packages_cache`` -option in the ``engine`` section of the :file:`murano.conf` -configuration file. If it is not used, a temporary directory is created. - -The ``enable_packages_cache`` option in the same section defines whether the -packages would persist on disk or not. When set to ``False``, each package -downloaded from API is stored in a separate directory, that will be deleted -after the deployment (or action) is over. This means that every deployment -or action execution needs to download all the packages it requires, -regardless of any packages previously downloaded by the engine. When set to -``True`` (default), the engine shares downloaded packages between deployments -and action executions. This means that packages persist on disk and have to be -eventually deleted. Therefore, whenever the engine requires a package and that -package is not found locally, the engine downloads the package. Afterwards, it -checks all the previously cached packages with the same FQN and same version. -If the cached package is not required by any ongoing deployment, it gets -deleted. Otherwise, it stays on disk until a new version is downloaded. - -.. note:: - On UNIX-based operating systems, murano uses ``fcntl`` for IPC locks that - support both shared and exclusive locking. On Windows, ``msvcrt`` is used. - It does not support shared file locks. Therefore, enabling package cache - mechanism under Windows might result in performance decrease, since only - one process would be able to use one package at the same time. diff --git a/doc/source/admin/murano_agent.rst b/doc/source/admin/murano_agent.rst deleted file mode 100644 index 018d2e003..000000000 --- a/doc/source/admin/murano_agent.rst +++ /dev/null @@ -1,153 +0,0 @@ -.. _murano-agent: - -============ -Murano agent -============ - -Murano easily installs and configures necessary software on new virtual -machines. Murano agent is one of the main participants of these processes. - -Usually, it is enough to execute a single script to install a simple -application. A more complex installation requires a deep script result -analysis. For example, we have a cross-platform application. The first script -determines the operation system and the second one calls an appropriate -installation script. Note, that installation script may be written in different -languages (the shell for Linux and PowerShell for Windows). Murano agent can -easily handle this situation and even more complicated ones. - -So murano agent operates not with scripts, but with execution plans, which are -minimum units of the installation workflow. - -Murano-agent on a new VM -~~~~~~~~~~~~~~~~~~~~~~~~ - -Earlier most of the application deployments were possible only on images with -pre-installed murano agent. You can refer to -:ref:`corresponding documentation ` -on building an image with murano-agent. - -Currently murano-agent can be automatically installed by cloud-init. To deploy -an application on an image with pre-installed cloud-init you should mark the -image with Murano specific metadata. More information about preparing images -can be found :ref:`here `. This type of installation has some -limitations. The image has to have pre-installed python. Murano-agent is -installed from PyPi so the instance should have connectivity with the Internet. -Also it requires an installation of some python packages, e.g. python3-pip, -python3-dev, python3-setuptools, python3-virtualenv, which are also installed by -cloud-init. - -Interaction with murano-engine -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -First of all, communication between murano-agent and murano engine should be -established. The communication is performed through AMQP protocol. This type of -communication is preferable (for example, compared to SSH) because it is: - -* Durable - - * To establish the connection, there is no need to wait until the - instance is spawned. Murano-agent, on its turn, does not need - to wait for a murano-engine task. - - * Messages can be sent to RabbitMQ asynchronously. - - * The connection does not depend on network issues. And moreover, there is no - way to physically connect to the virtual machine if floating IP is not set. - - * It is possible to reload the instance and change network parameters during - the deployment. - -* Reliable - - If one instance of murano-engine fails in the middle of the deployment, - another one picks up the messages from the queue and continue the deployment. - -Right after application author calls the :command:`deploy` method of the class, inherited from -*io.murano.resources.Instance*, new murano-agent configuration file starts -forming in accordance with the values specified in the ``[rabbitmq]`` murano -configuration file section. A script that runs through cloud-init copies a -new file to the right place during the instance booting. - - -Execution plans and execution plan templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It was already mentioned that murano-agent recognizes execution plans. -These instructions contain scripts with all the required parameters -The application package author provides the execution plan templates together -with scripts code. During the deployment it is complemented with all required -parameters (including user-input). - -For more information on execution plan templates, refer to -:ref:`Execution plan template `. - -Take a look at the muranoPL code snippet. The``EtcdAddMember`` template expects -*name* and *ip* parameters. The first line shows that these parameters are -passed to the template, and the second one shows that the template is sent to -the agent: - -.. code-block:: console - - - $template: $resources.yaml('EtcdAddMember.template').bind(dict( - name => $.instance.name, - ip => $.getIp() - )) - - $clusterConfig: $._cluster.masterNode.instance.agent.call($template, $resources) - -Beside the simple agent call, there is a method that enables sending an already -prepared execution plan (not a template). The main difference between template -and full execution plan is in the ``files`` section. Prepared execution plan contains -file contents and name by which they are reachable. So it is not required to -provide the resources argument: - -.. code-block:: console - - ..instance.agent.callRaw($plan) - -Also, there are ``instance.agent.call($template, $resources)`` and -``..instance.agent.sendRaw($plan)`` methods which have the same meaning but -indicate the engine not to wait for the script execution result. The default -agent call response time (with the corresponding method call) is set in -murano configuration file and equals to one hour. Take a look at the ``engine`` -section: - -.. code-block:: console - - [engine] - # Time for waiting for a response from murano-agent during the - # deployment (integer value) - agent_timeout = 3600 - -.. note:: Murano-agent is able to run different types of scripts, - such as powershell, python, bash, chef, and puppets. Moreover, it has - a mechanism for extending supported formats and that is why murano - agent is called ``unified`` - -To use puppet a deployment workflow, configure an execution plan as follows: - -#. Set correct version of format: - - ``FormatVersion >=2.1.0``. Previous formats does not support puppet execution. - -#. Use corresponding type - - In the script section, script item should have ``Type: Puppet`` - -#. Provide entry-point class - - Use puppet syntax ``EntryPoint: mysql::server`` - - -.. note:: You can use scripts directly from git or svn repositories: - - .. code-block:: console - - Files: - - mysql: https://github.com/nanliu/puppet-staging.git - -A script output is available in the murano-agent log file. This file is located -on the spawned instance at :file:`/etc/murano/agent.conf` on a Linux-based -machine, or :file:`C:\\Murano\\Agent\\agent.conf` on a Windows-based machine. -You can also refer to murano-agent log if there is no connectivity with -murano-engine (check if RabbitMQ settings are updated) or to track -deployment execution. diff --git a/doc/source/admin/murano_policies.rst b/doc/source/admin/murano_policies.rst deleted file mode 100644 index 6f56cacdd..000000000 --- a/doc/source/admin/murano_policies.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. _murano_policies: - -=============== -Murano Policies -=============== - -Murano only uses 2 roles for policy enforcement. Murano allows access by -default and uses the admin role for any action that involves accessing -data across multiple projects in the cloud. - -.. glossary:: - - role:Member - User is non-admin to all APIs. - - role:admin - User is admin to all APIs. - -Sample File Generation ----------------------- - -To generate a sample policy.yaml file from the Murano defaults, run the -oslo policy generation script:: - - oslopolicy-sample-generator \ - --config-file etc/oslo-policy-generator/murano-policy-generator.conf \ - --output-file policy.yaml.sample - -or using tox:: - - tox -egenpolicy - -.. note:: - - In previous OpenStack releases the default policy format was JSON, but - now the `recommended format `_ - is YAML. -.. - -Merged File Generation ----------------------- - -This will output a policy file which includes all registered policy defaults -and all policies configured with a policy file. This file shows the effective -policy in use by the project:: - - oslopolicy-sample-generator \ - --config-file etc/oslo-policy-generator/murano-policy-generator.conf - -List Redundant Configurations ------------------------------ - -This will output a list of matches for policy rules that are defined in a -configuration file where the rule does not differ from a registered default -rule. These are rules that can be removed from the policy file with no change -in effective policy:: - - oslopolicy-list-redundant \ - --config-file etc/oslo-policy-generator/murano-policy-generator.conf - -Policy configuration --------------------- - -Like each service in OpenStack, Murano has its own role-based access policies -that determine who can access objects and under what circumstances. The default -implementation for these policies is defined in the service's source code -- -under :file:`murano.common.policies`. The default policy definitions can be -overridden using the :file:`policy.yaml` file. - -On each API call the corresponding policy check is performed. -:file:`policy.yaml` file can be changed without interrupting the API service. - -For detailed information on :file:`policy.yaml` syntax, please refer to the -`OpenStack official documentation `_ - -With this file you can set who may upload packages and perform other operations. - -So, changing ``"upload_package": "rule:default"`` to ``"rule:admin_api"`` -will forbid regular users from uploading packages. - -For reference: - -- ``"get_package"`` is checked whenever a user accesses a package - from the catalog. default: anyone -- ``"upload_package"`` is checked whenever a user uploads a package - to the catalog. default: anyone -- ``"modify_package"`` is checked whenever a user modifies a package - in the catalog. default: anyone -- ``"publicize_package"`` is checked whenever a user is trying to - make a murano package public (both when creating a new package or - modifying an existing one). default: admin users -- ``"manage_public_package"`` is checked whenever a user attempts to - modify parameters of a public package. default: admin users -- ``"delete_package"`` is checked whenever a user attempts to - delete a package from the catalog. default: anyone -- ``"download_package"`` is checked whenever a user attempts to - download a package from the catalog. default: anyone -- ``"list_environments_all_tenants"`` is checked whenever a request - to list environments of all tenants is made. default: admin users -- ``"execute_action"`` is checked whenever a user attempts to execute - an action on deployment environments. default: anyone - -.. note:: - - The package upload wizard in Murano dashboard consists of several steps: - The "upload_package" policy is enforced during the first step while - "modify_package" is enforced during the second step. Package parameters are - modified during package upload. So, please modify both policy definitions - together. Otherwise it will not be possible to browse package details on the - second step of the wizard. - -Default Murano Policies ------------------------ - -.. literalinclude:: ../_static/murano.policy.yaml.sample diff --git a/doc/source/admin/murano_repository.rst b/doc/source/admin/murano_repository.rst deleted file mode 100644 index d0367af58..000000000 --- a/doc/source/admin/murano_repository.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _murano-repository: - -================= -Murano repository -================= - -Use an existing repository -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Set up a custom repository -~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/admin/net_configuration.rst b/doc/source/admin/net_configuration.rst deleted file mode 100644 index d55c2c394..000000000 --- a/doc/source/admin/net_configuration.rst +++ /dev/null @@ -1,114 +0,0 @@ -===================== -Network configuration -===================== - -Murano may work in various networking environments and is capable of detecting -the current network configuration and choosing appropriate settings -automatically. However, some additional actions are required to support -advanced scenarios. - -Nova-network support -^^^^^^^^^^^^^^^^^^^^ - -Nova-network is the simplest networking solution, which has limited -capabilities but is available on any OpenStack deployment without the need to -deploy any additional components. - -When a new murano environment is created, murano checks if a dedicated -networking service, for example, neutron, exists in the current OpenStack -deployment. It relies on the Identity service catalog for that. If such a -service is not present, murano automatically falls back to nova-network. No -further configuration is needed in this case, all the VMs spawned by Murano -will be joining the same network. - -Neutron support -^^^^^^^^^^^^^^^ - -If neutron is installed, murano enables its advanced networking features that -give you the ability to avoid configuring networks for your application. - -By default, it creates an isolated network for each environment and joins -all VMs needed by your application to that network. To install and configure -the application in a newly spawned virtual machine, murano also requires a -router to be connected to the external network. - -Automatic neutron configuration -+++++++++++++++++++++++++++++++ - -To create the router automatically, provide the following parameters in the -configuration file: - -.. code-block:: ini - - [networking] - - external_network = %EXTERNAL_NETWORK_NAME% - router_name = %MURANO_ROUTER_NAME% - create_router = true - -To figure out the name of the external network, run -:command:`openstack network list --external`. - -During the first deployment, the required networks and router with a specified -name will be created and set up. - -Manual neutron configuration -++++++++++++++++++++++++++++ - -To configure neutron manually, follow the steps below. - -#. Create a public network. - - #. Log in to the OpenStack dashboard as an administrator. - - #. Verify the existence of external networks. For this, navigate to - :menuselection:`Project > Network > Network Topology`. - - #. Check the network type in network details. For this, navigate to - :menuselection:`Admin > Networks` and see the :guilabel:`Network name` - section. - Alternatively, run the :command:`openstack network list --external` - command using CLI. - - #. Create a new external network as described in the `OpenStack documentation `_. - - .. image:: figures/network-topology-1.png - :alt: Network Topology page - :width: 630 px - -#. Create a local network. - - #. Navigate to :menuselection:`Project > Network > Networks`. - #. Click :guilabel:`Create Network` and fill in the form. - - -#. Create a router. - - #. Navigate to :menuselection:`Project > Network > Routers`. - #. Click :guilabel:`Create Router`. - #. In the :guilabel:`Router Name` field, enter *murano-default-router*. - If you specify a name other than *murano-default-router*, change the - following settings in the configuration file: - - .. code-block:: ini - - [networking] - - router_name = %SPECIFIED_NAME% - create_router = false - - #. Click :guilabel:`Create router`. - #. Click the newly created router name. - #. In the :guilabel:`Interfaces` tab, click :guilabel:`Add Interface`. - #. Specify the subnet and IP address. - - .. image:: figures/add-interface.png - :alt: Add Interface dialog - :width: 630 px - - #. Verify the result in - :menuselection:`Project > Network > Network Topology`. - - .. image:: figures/network-topology-2.png - :alt: Network Topology page - :width: 630 px diff --git a/doc/source/admin/policy_enf.rst b/doc/source/admin/policy_enf.rst deleted file mode 100644 index 4ca030760..000000000 --- a/doc/source/admin/policy_enf.rst +++ /dev/null @@ -1,51 +0,0 @@ -.. _policy_enf: - -================================= -Policy enforcement using Congress -================================= - -Policies are defined and evaluated in the Congress_ project. -The policy language for Congress is Datalog. The congress policy consists -of the Datalog rules and facts. - -Examples of policies are as follows: - -* Minimum 2 GB of RAM for all VM instances. -* A certified version for all Apache server instances. -* Data placement policy: all database instances must be deployed at a given - geographic location enforcing some law restriction on data placement. - -These policies are evaluated over data in the form of tables (Congress data -structures). A deployed Murano environment must be decomposed to the Congress -data structures. The decomposed environment is sent to Congress for simulation. -Congress simulates whether the resulting state violates any defined -policy: deployment is aborted in case of policy violation. - -Murano uses two predefined policies in Congress: - -* ``murano_system`` contains rules and facts of policies defined by the cloud - administrator. -* ``murano`` contains only facts/records reflecting the resulting state after - the deployment of an environment. - -Records in the ``murano`` policy are queried by rules from -the ``murano_system`` policy. The Congress simulation does not create any -records in the ``murano`` policy, and only provides the feedback on whether -the resulting state violates the policy or not. - -As a part of the policy guided fulfillment, you need to enforce policies -on a murano environment deployment. If the policy enforcement fails, -the deployment fails as well. - -.. _Congress: https://wiki.openstack.org/wiki/Congress - -This section contains the following subsections: - -.. toctree:: - :maxdepth: 2 - - policy_enforcement/policy_enf_setup - policy_enforcement/policy_enf_rules - policy_enforcement/policy_enf_dev - policy_enforcement/policy_enf_modify - diff --git a/doc/source/admin/policy_enforcement/policy_enf_dev.rst b/doc/source/admin/policy_enforcement/policy_enf_dev.rst deleted file mode 100644 index 82fd8fc87..000000000 --- a/doc/source/admin/policy_enforcement/policy_enf_dev.rst +++ /dev/null @@ -1,197 +0,0 @@ -.. _policyenf_dev: - -Murano policy enforcement internals -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section describes internals of the murano policy enforcement -feature. - -Model decomposition -------------------- - -The data for the policy validation comes from the models of Murano -applications. These models are transformed to a set of rules that are -processed by Congress. - -There are several *tables* created in murano policy for different kinds -of rules that are as follows: - -* ``murano:objects(object_id, parent_id, type_name)`` -* ``murano:properties(object_id, property_name, property_value)`` -* ``murano:relationships(source, target, name)`` -* ``murano:connected(source, target)`` -* ``murano:parent_types(object_id, parent_type_name)`` -* ``murano:states(environment_id, state)`` - -**murano:objects(object_id, parent_id, type_name)** - - This rule is used for representation of all objects in Murano model, - such as environment, application, instance, and other. - - Value of the ``type`` property is used as the ``type_name`` parameter: - - .. code-block:: yaml - - name: wordpress-env - '?': {type: io.murano.Environment, id: 83bff5ac} - applications: - - '?': {id: e7a13d3c, type: com.example.databases.MySql} - - The model above transforms to the following rules: - - * ``murano:objects+("83bff5ac", "tenant_id", "io.murano.Environment")`` - * ``murano:objects+("83bff5ac", "e7a13d3c", "com.example.databases.MySql")`` - - .. note:: - - The owner of the environment is a project (tenant). - -**murano:properties(object_id, property_name, property_value)** - - Each object may have properties. In this example we have an application - with one property: - - .. code-block:: yaml - - applications: - - '?': {id: e7a13d3c, type: com.example.databases.MySql} - database: wordpress - - The model above transforms to the following rule: - - * ``murano:properties+("e7a13d3c", "database", "wordpress")`` - - Inner properties are also supported using dot notation: - - .. code-block:: yaml - - instance: - '?': {id: 825dc61d, type: io.murano.resources.LinuxMuranoInstance} - networks: - useFlatNetwork: false - - The model above transforms to the following rule: - - * ``murano:properties+("825dc61d", "networks.useFlatNetwork", "False")`` - - If a model contains list of values, it is represented as a set of multiple - rules: - - .. code-block:: yaml - - instances: - - '?': {id: be3c5155, type: io.murano.resources.LinuxMuranoInstance} - networks: - customNetworks: [10.0.1.0, 10.0.2.0] - - The model above transforms to the following rules: - - * ``murano:properties+("be3c5155", "networks.customNetworks", "10.0.1.0")`` - * ``murano:properties+("be3c5155", "networks.customNetworks", "10.0.2.0")`` - -**murano:relationships(source, target, name)** - - Murano application models may contain references to other applications. - In this example, the WordPress application references MySQL in - the ``database`` property: - - .. code-block:: yaml - - applications: - - '?': - id: 0aafd67e - type: com.example.databases.MySql - - '?': - id: 50fa68ff - type: com.example.WordPress - database: 0aafd67e - - The model above transforms to the following rule: - - * ``murano:relationships+("50fa68ff", "0aafd67e", "database")`` - - .. note:: - - For the ``database`` property we do not create - the ``murano:properties+`` rule. - - If we define an object within other object, they will have relationships - between them: - - .. code-block:: yaml - - applications: - - '?': - id: 0aafd67e - type: com.example.databases.MySql - instance: - '?': {id: ed8df2b0, type: io.murano.resources.LinuxMuranoInstance} - - The model above transforms to the following rule: - - * ``murano:relationships+("0aafd67e", "ed8df2b0", "instance")`` - - There are special relationships of ``services`` from the environment - to its applications: ``murano:relationships+("env_id", "app_id", - "services")`` - -**murano:connected(source, target)** - - This table stores both direct and indirect connections between instances. - It is derived from ``murano:relationships``: - - .. code-block:: yaml - - applications: - - '?': - id: 0aafd67e - type: com.example.databases.MySql - instance: - '?': {id: ed8df2b0, type: io.murano.resources.LinuxMuranoInstance} - - '?': - id: 50fa68ff - type: com.example.WordPress - database: 0aafd67e - - The model above transforms to the following rules: - - * ``murano:connected+("50fa68ff", "0aafd67e")`` # WordPress to MySql - * ``murano:connected+("50fa68ff", "ed8df2b0")`` # WordPress to LinuxMuranoInstance - * ``murano:connected+("0aafd67e", "ed8df2b0")`` # MySql to LinuxMuranoInstance - -**murano:parent_types(object_id, parent_name)** - - Each object in murano has a class type. These classes may inherit from one - or more parents. For example, ``LinuxMuranoInstance > LinuxInstance > - Instance``: - - .. code-block:: yaml - - instances: - - '?': {id: be3c5155, type: LinuxMuranoInstance} - - The model above transforms to the following rules: - - * ``murano:objects+("...", "be3c5155", "LinuxMuranoInstance")`` - * ``murano:parent_types+("be3c5155", "LinuxMuranoInstance")`` - * ``murano:parent_types+("be3c5155", "LinuxInstance")`` - * ``murano:parent_types+("be3c5155", "Instance")`` - - .. note:: - - The type of an object is also repeated in its parent types - (``LinuxMuranoInstance`` in the example) for easier handling of - user-created rules. - - .. note:: - - If a type inherits from more than one parent, and these parents inherit - from one common type, the ``parent_type`` rule is included only once in - the common type. - -**murano:states(environment_id, state)** - - Currently only one record for environment is created: - - * ``murano:states+("uugi324", "pending")`` - diff --git a/doc/source/admin/policy_enforcement/policy_enf_modify.rst b/doc/source/admin/policy_enforcement/policy_enf_modify.rst deleted file mode 100644 index d77073047..000000000 --- a/doc/source/admin/policy_enforcement/policy_enf_modify.rst +++ /dev/null @@ -1,101 +0,0 @@ -Using policy for the base modification of an environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Congress policies enables a user to define modification of an environment -prior to its deployment. This includes: - -* Adding components, for example, monitoring. -* Changing and setting properties, for example enforcing a given zone, - flavors, and others. -* Configuring relationships within an environment. - -Use cases examples: - -* Installation of the monitoring agent on each VM instance - by adding a component with the agent and creating relationship - between the agent and instance. - -* Enabling a certified version to all Apache server instances: - setting the version property to all Apache applications within - an environment to a particular version. - -These policies are evaluated over data in the form of tables that are Congress -data structures. A deployed murano environment must be decomposed to Congress -data structures. The further workflow is as follows: - -* The decomposed environment is sent to Congress for simulation. - -* Congress simulates whether the resulting state requires modification. - -* In case the modification of a deployed environment is required, - Congress returns a list of actions in the YAML format - to be performed on the environment prior to the deployment. - - For example: - - .. code-block:: yaml - - set-property: {object_id: c46770dec1db483ca2322914b842e50f, prop_name: keyname, value: production-key} - - The example above sets the ``keyname`` property to the ``production-key`` - value on the instance identified by ``object_id``. An administrator can use - it as an output of the Congress rules. - -* The action specification is parsed in murano. The given action class is - loaded, and the action instance is created. - -* The parsed parameters are supplied to the action ``__init__`` method. - -* The action is performed on a given environment (the ``modify`` method). - - -.. _base_mod_rules: - -Creating base modification rules --------------------------------- - -This example illustrates how to configure the rule enforcing all VM instances -to deploy with a secure key pair. This may be required in a production -environment. - -.. warning:: - - Before you create rules, configure your OpenStack environment as described - in :ref:`policyenf_setup`. - -**Procedure:** - -#. To create the ``predeploy_modify`` rule, run: - - .. code-block:: console - - congress policy rule create murano_system 'predeploy_modify(eid, obj_id, action):-murano:objects(obj_id, pid, type), murano_env_of_object(obj_id, eid), murano:properties(obj_id, "keyname", kn), concat("set-property: {object_id: ", obj_id, first_part), concat(first_part, ", prop_name: keyname, value: production-key}", action)' - - The command above contains the following information: - - .. code-block:: console - - predeploy_modify(eid, obj_id, action) :- - murano:objects(obj_id, pid, type), - murano:objects(eid, tid, "io.murano.Environment"), - murano:connected(eid, pid), - murano:properties(obj_id, "keyname", kn), - concat("set-property: {object_id: ", obj_id, first_part), - concat(first_part, ", prop_name: keyname, value: production-key}", action) - - Policy validation engine checks the ``predeploy_modify`` rule. - And the Congress engine evaluates the rules referenced inside this rule. - - .. note:: - - The ``production-key`` key pair must already exist, though you can use - any other existing key pair. - -#. Deploy the environment. - -Instances within the environment are deployed with the specified key pair. - -.. seealso:: - - * :ref:`policy_enf_rules` - diff --git a/doc/source/admin/policy_enforcement/policy_enf_rules.rst b/doc/source/admin/policy_enforcement/policy_enf_rules.rst deleted file mode 100644 index 491abb6c6..000000000 --- a/doc/source/admin/policy_enforcement/policy_enf_rules.rst +++ /dev/null @@ -1,84 +0,0 @@ -.. _policy_enf_rules: - -Creating policy enforcement rules -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This article illustrates how you can create policy enforcement rules. -For testing purposes, create rules that prohibit the creation -of instances with the flavor with over 2048 MB of RAM following -the procedure below. - -**Procedure:** - -#. Verify that you have configured your OpenStack environment as described - in :ref:`policyenf_setup`. - -#. To create the ``predeploy_errors`` rule, run: - - .. code-block:: console - - congress policy rule create murano_system "predeploy_errors(eid, obj_id, msg) :- murano:objects(obj_id, pid, type), murano:objects(eid, tid, \"io.murano.Environment\"), murano:connected(eid, pid), murano:properties(obj_id, \"flavor\", flavor_name), flavor_ram(flavor_name, ram), gt(ram, 2048), murano:properties(obj_id, \"name\", obj_name), concat(obj_name, \": instance flavor has RAM size over 2048MB\", msg)" - - The command above contains the following information: - - .. code-block:: console - - predeploy_errors(eid, obj_id, msg) :- - murano:objects(obj_id, pid, type), - murano:objects(eid, tid, "io.murano.Environment"), - murano:connected(eid, pid), - murano:properties(obj_id, "flavor", flavor_name), - flavor_ram(flavor_name, ram), - gt(ram, 2048), - murano:properties(obj_id, "name", obj_name), - concat(obj_name, ": instance flavor has RAM size over 2048MB", msg) - - Policy validation engine checks the ``predeploy_errors`` rule, and rules - referenced within this rule are evaluated by the Congress engine. - - In this example, we create the rule that references the ``flavor_ram`` - rule we create afterwards. It disables flavors with RAM more than - 2048 MB and constructs the message returned to the user - in the ``msg`` variable. - - In this example we use data from policy **murano** which is represented by - ``murano:properties``. There are stored rows with decomposition of model - representing murano application. We also use built-in functions of Congress: - - * ``gt`` stands for 'greater-than' - * ``concat`` joins two strings into one variable - -#. To create the ``flavor_ram`` rule, run: - - .. code-block:: console - - congress policy rule create murano_system "flavor_ram(flavor_name, ram) :- nova:flavors(id, flavor_name, cpus, ram)" - - This rule resolves parameters of flavor by flavor name and returns - the ``ram`` parameter. It uses the ``flavors`` rule from ``nova`` policy. - Data in this policy is filled by the ``nova`` datasource driver. - -#. Check the rule usage. - - #. Create an environment with a simple application: - - - Select an application from the murano applications. - - Create a ``m1.medium`` instance, which uses 4096 MB RAM. - - .. image:: ../figures/new-inst.png - :alt: Create new instance - :width: 100 % - - #. Deploy the environment. - -Deployment fails as the rule is violated: environment is in the ``Deploy -FAILURE`` status. Check the deployment logs for details: - -.. image:: ../figures/deploy-log.png - :alt: Deployment log - :width: 100 % - - -.. seealso:: - - * :ref:`base_mod_rules` diff --git a/doc/source/admin/policy_enforcement/policy_enf_setup.rst b/doc/source/admin/policy_enforcement/policy_enf_setup.rst deleted file mode 100644 index e07a41738..000000000 --- a/doc/source/admin/policy_enforcement/policy_enf_setup.rst +++ /dev/null @@ -1,111 +0,0 @@ -.. _policyenf_setup: - -Setting up policy enforcement -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Before you use the policy enforcement feature, configure Murano and Congress -properly. - -.. note:: - - This article does not cover Murano and Congress configuration options - useful for Murano application deployment, for example, DNS setup, - floating IPs, and so on. - -**To enable policy enforcement, complete the following tasks:** - -#. In Murano: - - * Enable the ``enable_model_policy_enforcer`` option - in the ``murano.conf`` file: - - .. code-block:: ini - - [engine] - # Enable model policy enforcer using Congress (boolean value) - enable_model_policy_enforcer = true - - * Restart murano-engine. - -#. Verify that Congress is installed and available in your OpenStack - environment. See the details in the `Congress official documentation - `_. - -#. `Install the congress command-line client - `_ - as any other OpenStack command-line client. - -#. For Congress, configure the following policies that policy enforcement uses - during the evaluation: - - * ``murano`` policy - - It is created by the Congress` murano datasource driver, which is a part - of Congress. Configure it for the OpenStack project (tenant) where you plan to - deploy your Murano application. Datasource driver retrieves deployed - Murano environments and populates Congress' murano policy tables. - See :ref:`policyenf_dev` for details. - - Remove the existing ``murano`` policy and create a new ``murano`` policy - configured for the ``demo`` project, by running: - - .. code-block:: console - - # remove default murano datasource configuration, because it is using 'admin' project. We need 'demo' project to be used. - openstack congress datasource delete murano - openstack congress datasource create murano murano --config username="$OS_USERNAME" --config tenant_name="demo" --config password="$OS_PASSWORD" --config auth_url="$OS_AUTH_URL" - - * ``murano_system`` policy - - It holds the user-defined rules for policy enforcement. Typically, - the rules use tables from other policies, for example, murano, nova, - keystone, and others. Policy enforcement expects the ``predeploy_errors`` - table here that is available on the ``predeploy_errors`` rules creation. - - Create the ``murano_system`` rule, by running: - - .. code-block:: console - - # create murano_system policy - openstack congress policy create murano_system - - # resolves objects within environment - openstack congress policy rule create murano_system 'murano_env_of_object(oid,eid):-murano:connected(eid,oid), murano:objects(eid,tid,"io.murano.Environment")' - - * ``murano_action`` policy with internal management rules. - - These rules are used internally in the policy enforcement request - and stored in a dedicated ``murano_action`` policy that is - created here. They are important in case an environment is redeployed. - - .. code-block:: console - - # create murano_action policy - openstack congress policy create murano_action --kind action - - # register action deleteEnv - openstack congress policy rule create murano_action 'action("deleteEnv")' - - # states - openstack congress policy rule create murano_action 'murano:states-(eid, st) :- deleteEnv(eid), murano:states( eid, st)' - - # parent_types - openstack congress policy rule create murano_action 'murano:parent_types-(tid, type) :- deleteEnv(eid), murano:connected(eid, tid),murano:parent_types(tid,type)' - openstack congress policy rule create murano_action 'murano:parent_types-(eid, type) :- deleteEnv(eid), murano:parent_types(eid,type)' - - # properties - openstack congress policy rule create murano_action 'murano:properties-(oid, pn, pv) :- deleteEnv(eid), murano:connected(eid, oid), murano:properties(oid, pn, pv)' - openstack congress policy rule create murano_action 'murano:properties-(eid, pn, pv) :- deleteEnv(eid), murano:properties(eid, pn, pv)' - - # objects - openstack congress policy rule create murano_action 'murano:objects-(oid, pid, ot) :- deleteEnv(eid), murano:connected(eid, oid), murano:objects(oid, pid, ot)' - openstack congress policy rule create murano_action 'murano:objects-(eid, tnid, ot) :- deleteEnv(eid), murano:objects(eid, tnid, ot)' - - # relationships - openstack congress policy rule create murano_action 'murano:relationships-(sid, tid, rt) :- deleteEnv(eid), murano:connected(eid, sid), murano:relationships( sid, tid, rt)' - openstack congress policy rule create murano_action 'murano:relationships-(eid, tid, rt) :- deleteEnv(eid), murano:relationships(eid, tid, rt)' - - # connected - openstack congress policy rule create murano_action 'murano:connected-(tid, tid2) :- deleteEnv(eid), murano:connected(eid, tid), murano:connected(tid,tid2)' - openstack congress policy rule create murano_action 'murano:connected-(eid, tid) :- deleteEnv(eid), murano:connected(eid,tid)' - diff --git a/doc/source/admin/prepare_lab.rst b/doc/source/admin/prepare_lab.rst deleted file mode 100644 index 8dca506ff..000000000 --- a/doc/source/admin/prepare_lab.rst +++ /dev/null @@ -1,204 +0,0 @@ -======================== -Prepare a lab for murano -======================== -This section provides basic information about lab's system requirements. -It also contains a description of a test which you may use to check if -your hardware fits the requirements. To do this, run the test and -compare the results with baseline data provided. - -.. _system_prerequisites: - -System prerequisites -~~~~~~~~~~~~~~~~~~~~ - -Supported operating systems ---------------------------- - -* Ubuntu Server 16.04 LTS or higher -* RHEL/CentOS 7.4 or higher - -**System packages are required for Murano** - -*Ubuntu* - -* gcc - -* python3-pip - -* python3-dev - -* libxml2-dev - -* libxslt-dev - -* libffi-dev - -* libpq-dev - -* python3-openssl - -* mysql-client - -Install all the requirements on Ubuntu by running:: - - sudo apt-get install gcc python3-pip python3-dev \ - libxml2-dev libxslt-dev libffi-dev \ - libpq-dev python3-openssl mysql-client - -*CentOS* - -* gcc - -* python3-pip - -* python3-devel - -* libxml2-devel - -* libxslt-devel - -* libffi-devel - -* postgresql-devel - -* pyOpenSSL - -* mysql - -Install all the requirements on CentOS by running:: - - sudo yum install gcc python3-pip python3-devel libxml2-devel \ - libxslt-devel libffi-devel postgresql-devel pyOpenSSL \ - mysql - -.. _lab_requirements: - -Lab requirements ----------------- - -+------------+--------------------------------+-----------------------+ -| Criteria | Minimal | Recommended | -+============+================================+=======================+ -| CPU | 4 core @ 2.4 GHz | 24 core @ 2.67 GHz | -+------------+--------------------------------+-----------------------+ -| RAM | 8 GB | 24 GB or more | -+------------+--------------------------------+-----------------------+ -| HDD | 2 x 500 GB (7200 rpm) | 4 x 500 GB (7200 rpm) | -+------------+--------------------------------+-----------------------+ -| RAID | Software RAID-1 (use mdadm as | Hardware RAID-10 | -| | it will improve read | | -| | performance almost two times) | | -+------------+--------------------------------+-----------------------+ - -`Table: Hardware requirements` - -There are a few possible storage configurations except the shown above. -All of them were tested and were working well. - -* 1x SSD 500+ GB - -* 1x HDD (7200 rpm) 500+ GB and 1x SSD 250+ GB (install the system onto - the HDD and mount the SSD drive to folder where VM images are) - -* 1x HDD (15000 rpm) 500+ GB - - -Test your lab host performance -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -We have measured time required to boot 1 to 5 instances of Windows -system simultaneously. You can use this data as the baseline to check if -your system is fast enough. - -You should use sysprepped images for this test, to simulate VM first -boot. - -Steps to reproduce test: - -#. Prepare Windows 2012 Standard (with GUI) image in QCOW2 format. Let's - assume that its name is ws-2012-std.qcow2 - -#. Ensure that there is NO KVM PROCESSES on the host. To do this, run - command: - - .. code-block:: console - - ps aux | grep kvm - -#. Make 5 copies of Windows image file: - - .. code-block:: console - - for i in $(seq 5); do \ - cp ws-2012-std.qcow2 ws-2012-std-$i.qcow2; done - -#. Create script start-vm.sh in the folder with .qcow2 files: - - .. code-block:: console - - #!/bin/bash - [ -z $1 ] || echo "VM count not provided!"; exit 1 - for i in $(seq $1); do - echo "Starting VM $i ..." - kvm -m 1024 -drive file=ws-2012-std-$i.qcow2,if=virtio -net user -net nic,model=virtio -nographic -usbdevice tablet -vnc :$i & done - -#. Start ONE instance with command below (as root) and measure time - between VM's launch and the moment when Server Manager window - appears. To view VM's desktop, connect with VNC viewer to your host - to VNC screen :1 (port 5901): - - .. code-block:: console - - sudo ./start-vm.sh 1 - -#. Turn VM off. You may simply kill all KVM processes by - - .. code-block:: console - - sudo killall kvm - -#. Start FIVE instances with command below (as root) and measure time - interval between ALL VM's launch and the moment when LAST Server Manager - window appears. To view VM's desktops, connect with VNC viewer to your - host to VNC screens :1 thru :5 (ports 5901-5905): - - .. code-block:: console - - sudo ./start-vm.sh 5 - -#. Turn VMs off. You may simply kill all KVM processes by - - .. code-block:: console - - sudo killall kvm - - -Baseline data -~~~~~~~~~~~~~ - -The table below provides baseline data which we've got in our -environment. - -+----------------+--------------------------+---------------------+ -| | Boot 1 instance | Boot 5 instances | -+================+==========================+=====================+ -| Avg. Time | 3m:40s | 8m | -+----------------+--------------------------+---------------------+ -| Max. Time | 5m | 20m | -+----------------+--------------------------+---------------------+ - -``Avg. Time`` refers to the lab with recommended hardware configuration, -while ``Max. Time`` refers to minimal hardware configuration. - - -Host optimizations -~~~~~~~~~~~~~~~~~~ - -Default KVM installation could be improved to provide better -performance. - -The following optimizations may improve host performance up to 30%: - -* change default scheduler from ``CFQ`` to ``Deadline`` -* use ``ksm`` -* use ``vhost-net`` diff --git a/doc/source/admin/using_glare.rst b/doc/source/admin/using_glare.rst deleted file mode 100644 index d7ea93179..000000000 --- a/doc/source/admin/using_glare.rst +++ /dev/null @@ -1,132 +0,0 @@ -.. _glare_usage: - -===================================== -Using Glare as a storage for packages -===================================== - -DevStack installation ---------------------- - -#. Enable Glare service in DevStack - - To enable the Glare service in DevStack, edit the ``local.conf`` file: - - .. code-block:: console - - $ cat local.conf - [[local|localrc]] - enable_service g-glare - -#. Run DevStack: - - .. code-block:: console - - $ ./stack.sh - - **Result** Glare service is installed with DevStack. - You can find logs in ``g-glare`` screen session. - -#. Install the ``muranoartifact`` plug-in from ``murano/contrib`` - - .. code-block:: console - - $ cd $DEST/murano/contrib/glance/ - $ sudo pip install -e . - -#. Restart ``Glare`` - -#. Set Glare as packages service in murano-engine. For this, - edit the ``[engine]`` section in the ``murano.conf`` file. - By default, ``murano.conf`` is located in the ``/etc/murano`` directory - - .. code-block:: ini - - [engine] - - packages_service = glare - -#. Restart ``murano-engine`` - - .. note:: You also can use ``glance`` as a value of the - ``packages_service`` option for the same behaviour - -#. Enable Glare in ``murano-dashboard``. For this, modify the following line - in the ``_50_murano.py`` file - - .. code-block:: python - - MURANO_USE_GLARE = True - - By default, the ``_50_murano.py`` file is located in - ``$HORIZON_DIR/openstack_dashboard/local/local_settings.d/``. - -#. Restart the ``apache2`` service. - Now ``murano-dashboard`` will retrieve packages from Glare. - -#. Log in to Dashboard and navigate to :menuselection:`Applications > Manage > Packages` - to view the empty list of packages. - Alternatively, use the :command:`murano` command. - -#. Use ``--murano-packages-service`` option to specify backend, - used by :command:`murano` command. Set it to ``glare`` for using ``Glare`` - - .. note:: You also can use ``glance`` as value - of ``--murano-packages-service`` option or environment variable - ``MURANO_PACKAGES_SERVICE`` for same behaviour - - + View list of packages: - - .. code-block:: console - - $ . {DEVSTACK_SOURCE_DIR}/openrc admin admin - $ murano --murano-packages-service=glare package-list - - +----+------+-----+--------+--------+-----------+------+---------+ - | ID | Name | FQN | Author | Active | Is Public | Type | Version | - +----+------+-----+--------+--------+-----------+------+---------+ - +----+------+-----+--------+--------+-----------+------+---------+ - - + Importing ``Core library`` - - .. code-block:: console - - $ cd $DEST/murano/meta/io.murano/ - $ zip io.murano.zip -r * - $ murano --murano-packages-service=glare package-import \ - --is-public /opt/stack/murano/meta/io.murano/io.murano.zip - - Importing package io.murano - +--------------------------------------+--------------+-----------+-----------+--------+-----------+---------+---------+ - | ID | Name | FQN | Author | Active | Is Public | Type | Version | - +--------------------------------------+--------------+-----------+-----------+--------+-----------+---------+---------+ - | 91a9c78f-f23a-4c82-aeda-14c8cbef096a | Core library | io.murano | murano.io | True | | Library | 0.0.0 | - +--------------------------------------+--------------+-----------+-----------+--------+-----------+---------+---------+ - -Set up Glare API entrypoint manually ------------------------------------- - -If you do not plan to get Glare service from keystone application catalog, -specify where g-glare service is running. - -#. Specify Glare URL in ``murano.conf``.It is http://0.0.0.0:9494 by default - and can be changed by setting `bind_host` and `bind_port` options in - the ``glance-glare.conf`` file. - - .. code-block:: ini - - [glare] - - url = http://: - -#. Specify Glare URL in the Dashboard settings file, ``_50_murano.py`` : - - .. code-block:: python - - GLARE_API_URL = 'http://:' - -#. Set the ``GLARE_URL`` environment variable for python-muranoclient. - Alternatively, use the ``--glare-url`` option in CLI. - - .. code-block:: console - - $ murano --murano-packages-service=glare --glare-url=http://0.0.0.0:9494 package-list diff --git a/doc/source/cli/index.rst b/doc/source/cli/index.rst deleted file mode 100644 index 02f244763..000000000 --- a/doc/source/cli/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -======================== -Murano CLI Documentation -======================== - -In this section you will find information on Murano's command line -interface. - -.. toctree:: - :maxdepth: 1 - - murano-status diff --git a/doc/source/cli/murano-status.rst b/doc/source/cli/murano-status.rst deleted file mode 100644 index 28c2f5ff6..000000000 --- a/doc/source/cli/murano-status.rst +++ /dev/null @@ -1,83 +0,0 @@ -============= -murano-status -============= - ----------------------------------------- -CLI interface for Murano status commands ----------------------------------------- - -Synopsis -======== - -:: - - murano-status [] - -Description -=========== - -:program:`murano-status` is a tool that provides routines for checking the -status of a Murano deployment. - -Options -======= - -The standard pattern for executing a :program:`murano-status` command is:: - - murano-status [] - -Run without arguments to see a list of available command categories:: - - murano-status - -Categories are: - -* ``upgrade`` - -Detailed descriptions are below: - -You can also run with a category argument such as ``upgrade`` to see a list of -all commands in that category:: - - murano-status upgrade - -These sections describe the available categories and arguments for -:program:`murano-status`. - -Upgrade -~~~~~~~ - -.. _murano-status-checks: - -``murano-status upgrade check`` - Performs a release-specific readiness check before restarting services with - new code. For example, missing or changed configuration options, - incompatible object states, or other conditions that could lead to - failures while upgrading. - - **Return Codes** - - .. list-table:: - :widths: 20 80 - :header-rows: 1 - - * - Return code - - Description - * - 0 - - All upgrade readiness checks passed successfully and there is nothing - to do. - * - 1 - - At least one check encountered an issue and requires further - investigation. This is considered a warning but the upgrade may be OK. - * - 2 - - There was an upgrade status check failure that needs to be - investigated. This should be considered something that stops an - upgrade. - * - 255 - - An unexpected error occurred. - - **History of Checks** - - **7.0.0 (Stein)** - - * Sample check to be filled in with checks as they are added in Stein. diff --git a/doc/source/conf.py b/doc/source/conf.py deleted file mode 100644 index 928cd6037..000000000 --- a/doc/source/conf.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (C) 2014 Mirantis Inc -# -# Licensed 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 os -import sys - -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../')) -sys.path.insert(0, os.path.abspath('../')) -sys.path.insert(0, os.path.abspath('./')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'oslo_config.sphinxconfiggen', - 'oslo_config.sphinxext', - 'oslo_policy.sphinxext', - 'oslo_policy.sphinxpolicygen', - 'sphinx.ext.viewcode', - 'sphinxcontrib.httpdomain',] - -if not on_rtd: - extensions.append('openstackdocstheme') - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/murano' -openstackdocs_pdf_link = True -openstackdocs_bug_project = 'murano' -openstackdocs_bug_tag = '' - -config_generator_config_file = '../../etc/oslo-config-generator/murano.conf' -sample_config_basename = '_static/murano' - -policy_generator_config_file = [ - ('../../etc/oslo-policy-generator/murano-policy-generator.conf', - '_static/murano'), -] - -# Set the default Pygments syntax -highlight_language = 'python' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['specification/murano-repository.rst', - 'specification/murano-api.rst', - 'murano_pl/builtin_functions.rst', - 'install/configure_network.rst', - 'articles/ad-ui.rst', - 'articles/telnet.rst'] - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -show_authors = False - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -if not on_rtd: - #TODO(efedorova): Change local theme to correspond with the theme on rtd - pass - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = 'Murano' -html_theme = 'openstackdocs' - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - 'index': ['sidebarlinks.html', 'localtoc.html', 'searchbox.html', 'sourcelink.html'], - '**': ['localtoc.html', 'relations.html', - 'searchbox.html', 'sourcelink.html'] -} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# -- Options for LaTeX output ------------------------------------------------- - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass -# [howto/manual]). -latex_documents = [ - ('index', 'doc-murano.tex', u'Murano Documentation', - u'OpenStack Foundation', 'manual'), -] -latex_domain_indices = False - -latex_elements = { - 'makeindex': '', - 'printindex': '', - 'preamble': r'\setcounter{tocdepth}{3}', - 'maxlistdepth': '10', -} - -# Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 -latex_use_xindy = False - -# Disable smartquotes, they don't work in latex -smartquotes_excludes = {'builders': ['latex']} diff --git a/doc/source/configuration/config-options.rst b/doc/source/configuration/config-options.rst deleted file mode 100644 index 8b8a4a278..000000000 --- a/doc/source/configuration/config-options.rst +++ /dev/null @@ -1,12 +0,0 @@ -========================================================= -Configuration options for the Application Catalog service -========================================================= - -The following options can be set in the ``/etc/murano/murano.conf`` config file. - -.. only:: html - - A :doc:`sample configuration file ` is also available. - -.. show-options:: - :config-file: etc/oslo-config-generator/murano.conf diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst deleted file mode 100644 index 9e19dc0bf..000000000 --- a/doc/source/configuration/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -=================== -Configuration Guide -=================== - -.. only:: html - - .. toctree:: - :maxdepth: 1 - - config-options - sample_config - sample_policy diff --git a/doc/source/configuration/sample_config.rst b/doc/source/configuration/sample_config.rst deleted file mode 100644 index 94d36f8a0..000000000 --- a/doc/source/configuration/sample_config.rst +++ /dev/null @@ -1,14 +0,0 @@ -=========================== -Murano Configuration Sample -=========================== - -The following is a sample murano configuration for adaptation and use. It is -auto-generated from murano when this documentation is built, so if you are -having issues with an option, please compare your version of murano with the -version of this documentation. - -.. only:: html - - The sample configuration can also be downloaded in `file form <../_static/murano.conf.sample>`_. - -.. literalinclude:: ../_static/murano.conf.sample diff --git a/doc/source/configuration/sample_policy.rst b/doc/source/configuration/sample_policy.rst deleted file mode 100644 index 67df03a15..000000000 --- a/doc/source/configuration/sample_policy.rst +++ /dev/null @@ -1,26 +0,0 @@ -==================== -Murano Sample Policy -==================== - -.. warning:: - - JSON formatted policy file is deprecated since Murano 11.0.0 (Wallaby). - This `oslopolicy-convert-json-to-yaml`__ tool will migrate your existing - JSON-formatted policy file to YAML in a backward-compatible way. - -.. __: https://docs.openstack.org/oslo.policy/latest/cli/oslopolicy-convert-json-to-yaml.html - -The following is a sample murano policy file that has been auto-generated -from default policy values in code. If you're using the default policies, then -the maintenance of this file is not necessary, and it should not be copied into -a deployment. Doing so will result in duplicate policy definitions. It is here -to help explain which policy operations protect specific murano APIs, but it -is not suggested to copy and paste into a deployment unless you're planning on -providing a different policy for an operation that is not the default. - -If you wish build a policy file, you can also use ``tox -e genpolicy`` to -generate it. - -The sample policy file can also be downloaded in `file form <../_static/murano.policy.yaml.sample>`_. - -.. literalinclude:: ../_static/murano.policy.yaml.sample diff --git a/doc/source/contributor/contributing.rst b/doc/source/contributor/contributing.rst deleted file mode 100644 index 8110f1020..000000000 --- a/doc/source/contributor/contributing.rst +++ /dev/null @@ -1,47 +0,0 @@ -============================ -So You Want to Contribute... -============================ -For general information on contributing to OpenStack, please check out the -`contributor guide `_ to get started. -It covers all the basics that are common to all OpenStack projects: the accounts -you need, the basics of interacting with our Gerrit review system, how we -communicate as a community, etc. -Below will cover the more project specific information you need to get started -with Murano. - -Communication -~~~~~~~~~~~~~ -* IRC channel #murano at OFTC -* Mailing list (prefix subjects with ``[murano]`` for faster responses) - http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-discuss - -Contacting the Core Team -~~~~~~~~~~~~~~~~~~~~~~~~ -Please refer the `murano Core Team -`_ contacts. - -New Feature Planning -~~~~~~~~~~~~~~~~~~~~ -murano features are tracked on `Launchpad `_. - -Task Tracking -~~~~~~~~~~~~~ -We track our tasks in `Launchpad `_. -If you're looking for some smaller, easier work item to pick up and get started -on, search for the 'low-hanging-fruit' tag. - -Reporting a Bug -~~~~~~~~~~~~~~~ -You found an issue and want to make sure we are aware of it? You can do so on -`Launchpad `_. - -Getting Your Patch Merged -~~~~~~~~~~~~~~~~~~~~~~~~~ -All changes proposed to the murano project require one or two +2 votes -from murano core reviewers before one of the core reviewers can approve -patch by giving ``Workflow +1`` vote. - -Project Team Lead Duties -~~~~~~~~~~~~~~~~~~~~~~~~ -All common PTL duties are enumerated in the `PTL guide -`_. diff --git a/doc/source/contributor/contributor_index.rst b/doc/source/contributor/contributor_index.rst deleted file mode 100644 index fe11ede3b..000000000 --- a/doc/source/contributor/contributor_index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. index:: Murano Contributor Guide - -.. _contributor-guide: - -Contributor Guide -~~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - how_to_contribute - dev_guidelines - plugins - dev_env - testing - doc_guidelines - stable_branches \ No newline at end of file diff --git a/doc/source/contributor/dev_env.rst b/doc/source/contributor/dev_env.rst deleted file mode 100644 index 8ac49d4e4..000000000 --- a/doc/source/contributor/dev_env.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _dev-env: - -======================= -Development environment -======================= diff --git a/doc/source/contributor/dev_guidelines.rst b/doc/source/contributor/dev_guidelines.rst deleted file mode 100644 index ea9d3fc0b..000000000 --- a/doc/source/contributor/dev_guidelines.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _dev-guidelines: - -====================== -Development guidelines -====================== - -Conventions -~~~~~~~~~~~ - -High-level overview of Murano components -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Coding guidelines -~~~~~~~~~~~~~~~~~ - -There are several significant rules for the Murano developer: - -* Follow PEP8 and OpenStack style guidelines. - -* Do not import functions. Only module imports are accepted. - -* Make commits as small as possible. It speeds up review of the change. - -* Six library usage rule: use it only when really necessary (for example if - existing code will not work in python 3 at all). - -* Mark application name in the 1st line of commit message for murano-apps - repository, i.e. [Apache] or [Kubernetes]. - -* Prefer code readability over performance unless the situations when - performance penalty can be proven to be big. - -* Write Py3-compatible code. If that's impossible leave comment. - -Rules for MuranoPL coding style: - -* Use camelCase for MuranoPL functions/namespaces/variables/properties, - PascalCase for class names. - -* Consider using ``$this`` instead of ``$`` where appropriate. - -Debug tips -~~~~~~~~~~ diff --git a/doc/source/contributor/doc_guidelines.rst b/doc/source/contributor/doc_guidelines.rst deleted file mode 100644 index 54f7715a8..000000000 --- a/doc/source/contributor/doc_guidelines.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _doc-guidelines: - -======================== -Documentation guidelines -======================== diff --git a/doc/source/contributor/how_to_contribute.rst b/doc/source/contributor/how_to_contribute.rst deleted file mode 100644 index 2c23415fe..000000000 --- a/doc/source/contributor/how_to_contribute.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _how_to_contribute: - -================= -How to contribute -================= - -.. TODO add a brief intro: - - Intended audience. - - How to start developing? - - How a new-comer can contribute? - - Communication channels - - Useful links for an OpenStack contributor - - consider the context of https://docs.openstack.org/sahara/latest/contributor/how-to-participate.html diff --git a/doc/source/contributor/plugins.rst b/doc/source/contributor/plugins.rst deleted file mode 100644 index 5c1deff04..000000000 --- a/doc/source/contributor/plugins.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _plugins: - -=============== -Murano plug-ins -=============== - -Murano plug-ins help to extend the capability of murano. -There are two types of murano plug-ins which serve different purposes: - -* Extend murano Core Library by implementing additional functionality. -* Add new package type classes. - -This section contains the following topics: - -.. toctree:: - :maxdepth: 2 - - plugins/murano_plugins - plugins/manage_plugins \ No newline at end of file diff --git a/doc/source/contributor/plugins/manage_plugins.rst b/doc/source/contributor/plugins/manage_plugins.rst deleted file mode 100644 index 550ea0884..000000000 --- a/doc/source/contributor/plugins/manage_plugins.rst +++ /dev/null @@ -1,105 +0,0 @@ -.. _manage_plugins: - -Creating a Murano plug-in -------------------------- - -Murano plug-in is a setuptools-compliant python package with ``setup.py`` and -all other necessary files. For more information about defining stevedore -plug-ins, see `stevedore documentation -`_. - -The structure of the demo application package -+++++++++++++++++++++++++++++++++++++++++++++ - -The package must meet the following requirements: - -* It must be a ZIP archive. -* The root folder of the archive must contain a ``manifest.yaml`` file. -* The manifest must be a valid YAML file representing key-value associative - array. -* The manifest should contain a *Format* key, that is, a format identifier. If - it is not present, "MuranoPL/1.0" is used. - -Murano uses the *Format* attribute of the manifest file to find an appropriate -plug-in for a particular package type. All interactions between the rest of -Murano and package file contents are done through the plug-in interface alone. - -Because Murano never directly accesses files inside the packages, it is -possible for plug-ins to dynamically generate MuranoPL classes on the fly. -Those classes will be served as adapters between Murano and third-party systems -responsible for deployment of particular package types. Thus, for Murano all -packages remain to be of MuranoPL type though some of them are "virtual". - -The format identifier has the following format: ``Name/Version``. -For example, ``Heat.HOT/1.0``. If name is not present, it is assumed to be -``MuranoPL`` (thus ``1.0`` becomes ``MuranoPL/1.0``). Version strings are in -SemVer three-component format (major.minor.patch). Missing version components -are assumed to be zero (thus 1.0 becomes 1.0.0). - -Installing a plug-in --------------------- - -To use a plug-in, install it on murano nodes in the same Python environment -with murano engine service. - -To install a plug-in: - -#. Execute the plug-in setup script. - - Alternatively, use a package deployment tool, such as pip: - - .. code-block:: console - - cd plugin_dir - pip install . - -#. Restart murano engine. After that, it will be possible to upload and deploy - the applications that use the capabilities that a plug-in provides. - -Plug-in versioning ------------------- - -Plug-ins located in Murano repository have the same version as Murano. -Therefore, to use a specific version of such plug-in, checkout to this version. -Then specify the version of plug-in classes in your application's manifest file -as usual: - - .. code-block:: yaml - - Require: - murano.plugins.example: 2.0.0 - -It should be standard SemVer format version string consisting of three parts: -Major.Minor.Patch. For more information about versioning, refer to -:ref:`versioning`. - -.. note:: - Enable Glare to use versioning. - -Organization ------------- - -Documentation -+++++++++++++ - -Documentation helps users understand what your plug-in does. For plug-ins -located in the Murano repository, create a ``README.rst`` file in the main -folder of the plug-in. The ``README.rst`` file may contain information about -the plug-in and an installation guide. - -Code -++++ - -The code of your plug-in may be located in the following repositories: - -* Murano repository. In this case, the plug-in should be located in the - ``murano/contrib/plugins`` folder. - -* A separate repository. In this case, create your own project. - -Bugs -++++ - -All bugs for specific plug-ins are reported in their projects. Bugs related -to plug-ins located in Murano repository should be reported in the `Murano -`_ project. diff --git a/doc/source/contributor/plugins/murano_plugins.rst b/doc/source/contributor/plugins/murano_plugins.rst deleted file mode 100644 index 36fca354e..000000000 --- a/doc/source/contributor/plugins/murano_plugins.rst +++ /dev/null @@ -1,243 +0,0 @@ -.. _muranopl_extensions: - -MuranoPL extension plug-ins -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano plug-ins allow extending MuranoPL with new classes. Therefore, using -such plug-ins applications with MuranoPL format, you access some additional -functionality defined in a plug-in. For example, the Magnum plug-in, which -allows murano to deploy applications such as Kubernetes using the capabilities -of the Magnum client. - -MuranoPL extension plug-ins can be used for the following purposes: - -* Providing interaction with external services. - - For example, you want to interact with the OpenStack Image service to get - information about images suitable for deployment. A plug-in may request image - data from glance during deployment, performing any necessary checks. - -* Enabling connections between murano applications and external hardware - - For example, you have an external load balancer located on a powerful - hardware and you want your applications launched in OpenStack to use that - load balancer. You can write a plug-in that interacts with the load balancer - API. Once done, add new apps to the pool of your load balancer or make any - other configurations from within your application definition. - -* Extending Core Library class functionality, which is responsible for creating - networks, interaction with murano-agent, and others - - For example, you want to create networks with special parameters for all of - your applications. You can just copy the class that is responsible for - network management from the Murano Core library, make the desired - modification, and load the new class as a plug-in. Both classes will be - available, and it is up to you to decide which way to create your networks. - -* Optimization of frequently used operations. Plug-in classes are written in - Python, therefore, the opportunity for improvement is significant. - - Murano provides a number of optimization opportunities depending on the - improvement needs. For example, classes in the Murano Core Library can be - rewritten in C and used from Python code to improve their performance in - particular use cases. - -.. _package_type_plugins: - -MuranoPL package type plug-ins -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The only package type natively supported by Murano is MuranoPL. However, it is -possible to extend Murano with support for other formats of application -definitions. TOSCA CSARs and HOT templates are the two examples of alternate -ways to define applications. - -Package types plug-ins are normal Python packages that can be distributed -through PyPI and installed using :command:`pip` or its alternatives. It is -important that the plug-in be installed to the same Python instance that is -used to run Murano API and Murano Engine. For multi-node Murano deployments, -plug-ins need to be installed on each node. - -To associate a plug-in with a particular package format, it needs to have a -special record in `[entry_points]` section of setup.cfg file: - -.. code-block:: ini - - io.murano.plugins.packages = - Name/Version = namespace:Class - -For example: - -.. code-block:: ini - - [entry_points] - io.murano.plugins.packages = - Cloudify.TOSCA/1.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage - - -This declaration maps particular pair of format-name/version to Python class -that implements Package API interface for the package type. It is possible -to specify several different format names or versions and map them to single -or different Python classes. For example, it is possible to specify - -.. code-block:: ini - - [entry_points] - io.murano.plugins.packages = - Cloudify.TOSCA/1.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage - Cloudify.TOSCA/1.1 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage - Cloudify.TOSCA/2.0 = murano_cloudify_plugin.cloudify_tosca_package:CloudifyToscaPackage_v2 - -.. note:: - - A single Python plug-in package may contain several Murano plug-ins - including of different types. For example, it is possible to combine - MuranoPL extension and package type plug-ins into a single package. - - -Tooling for package preparation -------------------------------- - -Some package formats may require additional tooling to prepare package ZIP -archive of desired structure. In such cases it is expected that those tools -will be provided by plug-in authors either as part of the same Python package -(by exposing additional shell entry points) or as a separate package or -distribution. - -The only two exceptions to this rule are native MuranoPL packages and HOT -packages that are built into Murano (there is no need to install additional -plug-ins for them). Tooling for those two formats is a part of -python-muranoclient. - - -Package API interface reference -------------------------------- - -Plug-ins expose API for the rest of Murano to interact with the package -by implementing `murano.packages.package.Package` interface. - - -Class initializer: - - `def __init__(self, format_name, runtime_version, source_directory, manifest):` - - - * **format_name**: name part of the format identifier (string) - * **runtime_version**: version part of the format identifier (instance of - semantic_version.Version) - * **source_directory**: path to the directory where package content was - extracted (string) - * **manifest**: contents of the manifest file (string->string dictionary) - - **Note**: implementations must call base class (`Package`) initializer - passing the first three of these arguments. - -Abstract properties that must be implemented by the plug-in: - - `def full_name(self):` - - * Fully qualified name of the package. Must be unique within package - scope of visibility (string) - - `def version(self):` - - * Package version (not to confuse with format version!). An instance of - `semantic_version.Version` - - `def classes(self):` - - * List (or tuple) of MuranoPL class names (FQNs) that package contains - - `def requirements(self):` - - * Dictionary of requirements (dependencies on other packages) in a form - of key-value mapping from required package FQN string to SemVer - version range specifier (instance of semantic_version.Spec or string - representation supported by Murano versioning scheme) - - `def package_type(self):` - - * Package type: "Application" or "Library" - - `def display_name(self):` - - * Human-readable name of the package as presented to the user (string) - - `def description(self):` - - * Package description (string or None) - - `def author(self):` - - * Package author (string or None) - - `def supplier(self):` - - * Package supplier (string or None) - - `def tags(self):` - - * List or tags for the package (list of strings) - - `def logo(self):` - - * Package (application) logo file content (str or None) - - `def supplier_logo(self):` - - * Package (application) supplier logo file content (str or None) - - `def ui(self):` - - * YAML-encoded string containing application's form definition (string or - None) - -Abstract methods that must be implemented by the plug-in: - - `def get_class(self, name):` - - * Returns string containing MuranoPL code (YAML-encoded string) for the - class whose fully qualified name is in "name" parameter (string) - - `def get_resource(self, name):` - - * Returns path for resource file whose name is in "name" parameter (string) - - -Properties that can be overridden in the plug-in: - - `def format_name(self):` - - * Canonical format name for the plug-in. Usually the same value that was - passed to class initializer - - - `def runtime_version(self):` - - * Format version. Usually the same value that was passed to class - initializer (semantic_version.Version) - - `def blob(self):` - - * Package file (.zip) content (str) - - -PackageBase class ------------------ - -Usually, there is no need to manually implement all the methods and properties -described. There is a `murano.packages.package.PackageBase` class that provides -typical implementation of most of required properties by obtaining -corresponding value from manifest file. - -When inheriting from PackageBase class, plug-in remains responsible for -implementation of: - -* `ui` property -* `classes` property -* `get_class` method - -This allows plug-in developers to concentrate on dynamic aspects of the package -type plug-in while keeping all static aspects (descriptions, logos and so on) -consistent across all package types (at least those who inherit from -`PackageBase`). \ No newline at end of file diff --git a/doc/source/contributor/stable_branches.rst b/doc/source/contributor/stable_branches.rst deleted file mode 100644 index 7577d5ee1..000000000 --- a/doc/source/contributor/stable_branches.rst +++ /dev/null @@ -1,63 +0,0 @@ -.. _stable_branches: - -============================== -Backporting to stable/branches -============================== - -Since murano is a big-tent OS project it largely follows the -`OpenStack stable branch guide `_ - -Upstream support phases -~~~~~~~~~~~~~~~~~~~~~~~ - -#. Phase I (first 6 months): All bugfixes (which meet the stable port criteria, - described in OS stable branch policy) are appropriate -#. Phase II (6-12 months): Only critical bugfixes and - security patches are acceptable -#. Phase III (more than 12 months): Only security - patches are acceptable - -In order to accept a change into $release it must first be accepted into all -releases back to master. - -There are two notable exceptions to the support phases rule: - -- murano-apps repository: - We recognise, that murano apps have different lifecycle than main murano - repository. Most of the time new apps are being written for already released - versions of murano, not for master. Having a rich collection of apps is one of - the goals of murano-apps repository, therefore we accept backports of apps and - app features to previous release branches. This is done on a case by case basis - and should be discussed with PTL and Murano core members on IRC or Mailing - List. However we believe, that submitting an app to stable branch only means - that author of the patch is not going to support the app. Therefore for the app - to get backported it still has to be first accepted to master and all - subsequent releases. - -- murano core library patches: Murano Core Library is an - app, that provides core functionality and classes for other murano apps. It - shares a lot of properties of regular murano apps and the rationale behind - allowing backports of MuranoPL code from master to stable branches is basically - the same: low regression risks during upgrades, high adoption impact. However - since core library is much more sensitive app, backports to it should be taken - more seriously and should be discussed on IRC and Mailing List and receive - PTL's approval. - -These two exceptions do not mean, that we're free to backport -any code from master to stable branches. Instead they show, that murano team -recognises the importance of these two areas of murano project and treats -exceptions to those slightly more liberally than to other parts of murano -project. - -Bug nomination process -~~~~~~~~~~~~~~~~~~~~~~ - -Whenever you file a bug, or see a bug, that you think -is eligible for backporting in stable branch nominate it for the corresponding -series. If bug reporter does not nominate the bug for eligible branch — this is -done by murano bug supervisor during triaging/confirmation process. In case it -is not clear whether the bug is eligible or not or if you do not have -permissions to nominate a bug for series you can set -`$release-backport-potential` tag (for example `liberty-backport-potential`). -Murano team is holding bi-weekly meetings on IRC (as part of regular community -meetings) to triage and nominate bugs for stable backports. diff --git a/doc/source/contributor/testing.rst b/doc/source/contributor/testing.rst deleted file mode 100644 index adc82211c..000000000 --- a/doc/source/contributor/testing.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _testing: - -======= -Testing -======= - -Testing guidelines -~~~~~~~~~~~~~~~~~~ - -Continuous Integration service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -UI testing -~~~~~~~~~~ - -Tempest tests -~~~~~~~~~~~~~ - -Automated testing machinery -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -CI design ---------- -CI jobs -------- diff --git a/doc/source/first-app/Before_the_start.rst b/doc/source/first-app/Before_the_start.rst deleted file mode 100644 index a4ea4a0fa..000000000 --- a/doc/source/first-app/Before_the_start.rst +++ /dev/null @@ -1,9 +0,0 @@ -================ -Before the start -================ - -What you need -------------- - -Deploy Murano -------------- diff --git a/doc/source/first-app/Debugging_and_troubleshooting_your_murano_app.rst b/doc/source/first-app/Debugging_and_troubleshooting_your_murano_app.rst deleted file mode 100644 index 5e995ece5..000000000 --- a/doc/source/first-app/Debugging_and_troubleshooting_your_murano_app.rst +++ /dev/null @@ -1,3 +0,0 @@ -============================================= -Debugging and troubleshooting your Murano app -============================================= diff --git a/doc/source/first-app/Develop_murano_app_for_plone.rst b/doc/source/first-app/Develop_murano_app_for_plone.rst deleted file mode 100644 index 8302e9b92..000000000 --- a/doc/source/first-app/Develop_murano_app_for_plone.rst +++ /dev/null @@ -1,48 +0,0 @@ -============================ -Develop Murano app for Plone -============================ - -Develop standalone Plone Murano app (single VM) ------------------------------------------------ - -Plone server requirements -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Define host VM requirements -........................... - -Host VM operatting system requirements -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Host VM hardware resources requirements -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Define preinstalled software and libraries requirements -....................................................... - -Define what the PloneServerApp should do -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create and debug sh-script that fully deploys the Plone server on a single VM -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create Murano package for your app -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Upload and deploy your Murano app to OpenStack cloud -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Develop cluster Plone Murano app (multi VM) -------------------------------------------- - -Develop basic server-client Murano app -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add load-balancing to the Plone cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add scalability to the Plone cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Add self-healing to the Plone cluster -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/first-app/Publish_your_murano_app_in_the_application_catalog.rst b/doc/source/first-app/Publish_your_murano_app_in_the_application_catalog.rst deleted file mode 100644 index 1d9baec14..000000000 --- a/doc/source/first-app/Publish_your_murano_app_in_the_application_catalog.rst +++ /dev/null @@ -1,15 +0,0 @@ -================================================== -Publish your Murano app in the application catalog -================================================== - -Join the OpenStack community -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Prepare testing environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Contribute your code to Murano-apps -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Contribute your code to App-catalog -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/first-app/README.rst b/doc/source/first-app/README.rst deleted file mode 100644 index 88ec6d6fb..000000000 --- a/doc/source/first-app/README.rst +++ /dev/null @@ -1,33 +0,0 @@ -This directory contains the "My first Murano App getting started guide" -tutorial. - -The tutorials work with an application that can be found in the -`openstack/murano-apps `_ -repository. - -Prerequisites -------------- - -To build the documentation, you must install the Graphviz package. - -/source -~~~~~~~ - -The :code:`/source` directory contains the tutorial documentation as -`reStructuredText `_ (RST). - -To build the documentation, you must install `Sphinx `_ and the -`OpenStack docs.openstack.org Sphinx theme (openstackdocstheme) `_. When -you invoke tox, these dependencies are automatically pulled in from the -top-level :code:`test-requirements.txt`. - -You must also install `Graphviz `_ on your build system. - -The document is build as part of the docs build, for example using:: - - tox -e docs - -/samples -~~~~~~~~ - -The code samples in this guide are located in this directory. diff --git a/doc/source/first-app/What_is_the_use_case.rst b/doc/source/first-app/What_is_the_use_case.rst deleted file mode 100644 index 78c9527ed..000000000 --- a/doc/source/first-app/What_is_the_use_case.rst +++ /dev/null @@ -1,3 +0,0 @@ -==================== -What is the use case -==================== diff --git a/doc/source/first-app/What_you_will_learn.rst b/doc/source/first-app/What_you_will_learn.rst deleted file mode 100644 index 38ef961a4..000000000 --- a/doc/source/first-app/What_you_will_learn.rst +++ /dev/null @@ -1,3 +0,0 @@ -=================== -What you will learn -=================== diff --git a/doc/source/first-app/Who_is_this_guide_for.rst b/doc/source/first-app/Who_is_this_guide_for.rst deleted file mode 100644 index a29a7602c..000000000 --- a/doc/source/first-app/Who_is_this_guide_for.rst +++ /dev/null @@ -1,3 +0,0 @@ -===================== -Who is this guide for -===================== diff --git a/doc/source/first-app/index.rst b/doc/source/first-app/index.rst deleted file mode 100644 index bcf505fb6..000000000 --- a/doc/source/first-app/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -========================================= -My first Murano App getting started guide -========================================= - -.. include:: README.rst - -Contents -~~~~~~~~ - -.. toctree:: - :maxdepth: 3 - - Who_is_this_guide_for - What_is_the_use_case - What_you_will_learn - Before_the_start - Develop_murano_app_for_plone - Debugging_and_troubleshooting_your_murano_app - Publish_your_murano_app_in_the_application_catalog diff --git a/doc/source/index.rst b/doc/source/index.rst deleted file mode 100644 index ea967bfc0..000000000 --- a/doc/source/index.rst +++ /dev/null @@ -1,129 +0,0 @@ -=============================== -Welcome to Murano Documentation -=============================== - -**Murano** is an open source OpenStack project that combines an application -catalog with versatile tooling to simplify and accelerate packaging and -deployment. It can be used with almost any application and service in -OpenStack. - -Murano project consists of several source code repositories: - -* `murano`_ -- the main repository. It contains code for Murano API server, - Murano engine and MuranoPL. -* `murano-agent`_ -- the agent that runs on guest VMs and executes the - deployment plan. -* `murano-dashboard`_ -- Murano UI implemented as a plugin for the OpenStack - Dashboard. -* `python-muranoclient`_ -- Client library and CLI client for Murano. - -.. note:: - `Administrator Documentation`, `Contributor Documentation`, and `Appendix` - are under development at the moment. - -.. Links - -.. _murano: https://opendev.org/openstack/murano/ -.. _murano-agent: https://opendev.org/openstack/murano-agent/ -.. _murano-dashboard: https://opendev.org/openstack/murano-dashboard/ -.. _python-muranoclient: https://opendev.org/openstack/python-muranoclient/ - - -Introduction to Murano -~~~~~~~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 1 - - reference/overview_index - - -Using Murano -~~~~~~~~~~~~ - -Learn how to use the Application Catalog directly from the Dashboard and -through the command-line interface (CLI), manage applications and environments. -The screenshots provided in this guide are of the Liberty release. - -.. toctree:: - :maxdepth: 1 - - user/quickstart/quickstart - user/user_index - -Installation -~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - install/index - -Configuration -~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - configuration/index - -CLI Guide -~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - cli/index - -Administrator Documentation -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Learn how to manage images, categories, and repositories using the Murano -client. - -.. toctree:: - :maxdepth: 1 - - admin/index - -First App Guide -~~~~~~~~~~~~~~~ - -A guide for developing your first Murano application. - -.. toctree:: - :maxdepth: 1 - - first-app/index - -Application Developer Documentation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Learn how to compose an application package and get it ready for uploading to -Murano. - -.. toctree:: - :maxdepth: 1 - - admin/appdev-guide/developer_index - admin/appdev-guide/faq - -Contributor Documentation -~~~~~~~~~~~~~~~~~~~~~~~~~ - -* If you are a new contributor to Murano please refer: :doc:`contributor/contributing` - -.. toctree:: - :maxdepth: 1 - - contributor/contributing - contributor/contributor_index - -Other Documentation -~~~~~~~~~~~~~~~~~~~ - -.. toctree:: - :maxdepth: 1 - - reference/appendix/appendix_index - reference/appendix/articles/articles_index diff --git a/doc/source/install/common_prerequisites.rst b/doc/source/install/common_prerequisites.rst deleted file mode 100644 index 76792354a..000000000 --- a/doc/source/install/common_prerequisites.rst +++ /dev/null @@ -1,98 +0,0 @@ -Prerequisites -------------- - -Before you install and configure the Application Catalog service, -you must create a database, service credentials, and API endpoints. - -#. To create the database, complete these steps: - - Murano can use various database types on the back end. For development - purposes, SQLite is enough in most cases. For production installations, you - should use MySQL or PostgreSQL databases. - - .. warning:: - - Although murano could use a PostgreSQL database on the back end, - it wasn't thoroughly tested and should be used with caution. - .. - - - * Use the database access client to connect to the database - server as the ``root`` user: - - .. code-block:: console - - $ mysql -u root -p - .. - - * Create the ``murano`` database: - - .. code-block:: mysql - - CREATE DATABASE murano; - .. - - * Grant proper access to the ``murano`` database: - - .. code-block:: mysql - - GRANT ALL PRIVILEGES ON murano.* TO 'murano'@'localhost' IDENTIFIED BY 'MURANO_DBPASS'; - .. - - Replace ``MURANO_DBPASS`` with a suitable password. - - * Exit the database access client. - - .. code-block:: mysql - - exit; - .. - -#. Source the ``admin`` credentials to gain access to - admin-only CLI commands: - - .. code-block:: console - - $ . admin-openrc - .. - -#. To create the service credentials, complete these steps: - - * Create the ``murano`` user: - - .. code-block:: console - - $ openstack user create --domain default --password-prompt murano - .. - - * Add the ``admin`` role to the ``murano`` user: - - .. code-block:: console - - $ openstack role add --project service --user murano admin - .. - - * Create the murano service entities: - - .. code-block:: console - - $ openstack service create --name murano --description "Application Catalog" application-catalog - .. - -#. Create the Application Catalog service API endpoints: - - .. code-block:: console - - $ openstack endpoint create --region RegionOne \ - application-catalog public http://:8082 - $ openstack endpoint create --region RegionOne \ - application-catalog internal http://:8082 - $ openstack endpoint create --region RegionOne \ - application-catalog admin http://:8082 - .. - - .. note:: - - URLs (publicurl, internalurl and adminurl) may be different - depending on your environment. - .. diff --git a/doc/source/install/enable-ssl.rst b/doc/source/install/enable-ssl.rst deleted file mode 100644 index caa01c9d9..000000000 --- a/doc/source/install/enable-ssl.rst +++ /dev/null @@ -1,149 +0,0 @@ -.. - Copyright 2014 Mirantis, Inc. - - Licensed 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. - -================= -SSL configuration -================= -Murano components are able to work with SSL. This section will help you -to configure proper settings for SSL configuration. - -HTTPS for Murano API -==================== - -SSL for the Murano API service can be configured in the *ssl* section in -``/etc/murano/murano.conf``. Just point to a valid SSL certificate. -See the example below: - -:: - - - [ssl] - cert_file = PATH - key_file = PATH - ca_file = PATH - -- *cert\_file* Path to the certificate file the server should use when binding to an SSL-wrapped socket. -- *key\_file* Path to the private key file the server should use when binding to an SSL-wrapped socket. -- *ca\_file* Path to the CA certificate file the server should use to validate client certificates provided during an SSL handshake. This is ignored if cert\_file and "key\_file" are not set. - -.. note:: - - The use of SSL is automatically started after pointing to an HTTPS protocol - instead of HTTP, during the registration of the Murano API service endpoints - (Change publicurl argument to start with \https://). -.. - - -SSL for Murano API is implemented like in any other OpenStack component. -This is because Murano uses the ssl python module; more information about -it can be found `here`_. - -.. _`here`: https://docs.python.org/2/library/ssl.html - -SSL for RabbitMQ -================ - -All Murano components communicate with each other via RabbitMQ. This -interaction can be encrypted with SSL. By default, all messages in Rabbit -MQ are not encrypted. Each RabbitMQ Exchange should be configured -separately. - -**Murano API <-> Rabbit MQ exchange <-> Murano Engine** - -Edit ssl parameters in default section of ``/etc/murano/murano.conf``. Set the -``rabbit_use_ssl`` option to *true* and configure the ssl kombu parameters. -Specify the path to the SSL keyfile and SSL CA certificate in a regular format: -/path/to/file without quotes or leave it empty to allow for self-signed -certificates. - -:: - - # connect over SSL for RabbitMQ (boolean value) - #rabbit_use_ssl=false - - # SSL version to use (valid only if SSL enabled). valid values - # are TLSv1, SSLv23 and SSLv3. SSLv2 may be available on some - # distributions (string value) - #kombu_ssl_version= - - # SSL key file (valid only if SSL enabled) (string value) - #kombu_ssl_keyfile= - - # SSL cert file (valid only if SSL enabled) (string value) - #kombu_ssl_certfile= - - # SSL certification authority file (valid only if SSL enabled) - # (string value) - #kombu_ssl_ca_certs= - - -**Murano Agent -> Rabbit MQ exchange** - -In the main murano configuration file, there is a section named *rabbitmq*, -which is responsible for setting up communication between Murano Agent and -Rabbit MQ. Just set the *ssl* parameter to True to enable ssl. - -:: - - [rabbitmq] - host = localhost - port = 5672 - login = guest - password = guest - virtual_host = / - ssl = True - -If you want to configure Murano Agent in a different way, change the default -template. It can be found in the Murano Core Library, located at -*https://opendev.org/openstack/murano/src/branch/master/meta/io.murano/Resources/Agent-v1.template*. -Take a look at the appSettings section: - -:: - - - - - - - - - - - - - - - - - - - -The desired parameter should be set directly to the value of the key that -you want to change. Quotes need to be kept. Thus you can change -"rabbitmq.ssl" and "rabbitmq.port" values to make Rabbit MQ work with -this exchange differently than the default Murano Engine way. - -.. note:: - - After modification, don't forget to zip and re-upload the core library. -.. - -SSL for Murano Dashboard -======================== - -If you are not going to use self-signed certificates, additional -configuration does not need to be done. Just prefix https in the URL. -Otherwise, set *MURANO_API_INSECURE = True* in Horizon's config file. You can -find it in ``/etc/openstack-dashboard/local_settings.py``. diff --git a/doc/source/install/from-source.rst b/doc/source/install/from-source.rst deleted file mode 100644 index df9b4f9fb..000000000 --- a/doc/source/install/from-source.rst +++ /dev/null @@ -1,277 +0,0 @@ -Install Murano from Source -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This section describes how to install and configure the Application Catalog -service for Ubuntu 16.04 (LTS) from source code. - -.. include:: common_prerequisites.rst - -Install the API service and Engine ----------------------------------- - -#. Create a folder which will hold all Murano components. - - .. code-block:: console - - mkdir ~/murano - .. - -#. Clone the murano git repository to the management server. - - .. code-block:: console - - cd ~/murano - git clone https://opendev.org/openstack/murano - .. - -#. Set up the murano config file - - Murano has a common config file for API and Engine services. - - First, generate a sample configuration file, using tox - - .. code-block:: console - - cd ~/murano/murano - tox -e genconfig - .. - - And make a copy of it for further modifications - - .. code-block:: console - - cd ~/murano/murano/etc/murano - ln -s murano.conf.sample murano.conf - .. - -#. Edit ``murano.conf`` with your favorite editor. Below is an example - which contains basic settings you likely need to configure. - - .. note:: - - The example below uses SQLite database. Edit **[database]** section - if you want to use any other database type. - .. - - .. code-block:: ini - - [DEFAULT] - debug = true - verbose = true - transport_url = rabbit://%RABBITMQ_USER%:%RABBITMQ_PASSWORD%@%RABBITMQ_SERVER_IP%:5672/ - - ... - - [database] - connection = mysql+pymysql://murano:MURANO_DBPASS@controller/murano - - ... - - [keystone] - auth_url = http://%OPENSTACK_KEYSTONE_ENDPOINT% - - ... - - [keystone_authtoken] - project_domain_name = Default - project_name = %OPENSTACK_ADMIN_PROJECT% - user_domain_name = Default - password = %OPENSTACK_ADMIN_PASSWORD% - username = %OPENSTACK_ADMIN_USER% - auth_url = http://%OPENSTACK_KEYSTONE_ENDPOINT% - auth_type = password - - ... - - [murano] - url = http://%YOUR_HOST_IP%:8082 - - [rabbitmq] - host = %RABBITMQ_SERVER_IP% - login = %RABBITMQ_USER% - password = %RABBITMQ_PASSWORD% - virtual_host = %RABBITMQ_SERVER_VIRTUAL_HOST% - - [networking] - default_dns = 8.8.8.8 # In case openstack neutron has no default - # DNS configured - .. - -#. Create a virtual environment and install Murano prerequisites. We will use - *tox* for that. The virtual environment will be created under *.tox* - directory. - - .. code-block:: console - - cd ~/murano/murano - tox - .. - -#. Create database tables for Murano. - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-db-manage \ - --config-file ./etc/murano/murano.conf upgrade - .. - -#. Open a new console and launch Murano API. A separate terminal is - required because the console will be locked by a running process. - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-api --config-file ./etc/murano/murano.conf - .. - -#. Import Core Murano Library. - - .. code-block:: console - - cd ~/murano/murano - pushd ./meta/io.murano - zip -r ../../io.murano.zip * - popd - tox -e venv -- murano --murano-url http://localhost:8082 \ - package-import --is-public io.murano.zip - .. - -#. Open a new console and launch Murano Engine. A separate terminal is - required because the console will be locked by a running process. - - .. code-block:: console - - cd ~/murano/murano - tox -e venv -- murano-engine --config-file ./etc/murano/murano.conf - .. - -Install Murano Dashboard -======================== - -Murano API & Engine services provide the core of Murano. However, your need a -control plane to use it. This section describes how to install and run Murano -Dashboard. - -#. Clone the murano dashboard repository. - - .. code-block:: console - - $ cd ~/murano - $ git clone https://opendev.org/openstack/murano-dashboard - .. - -#. Clone the ``horizon`` repository - - .. code-block:: console - - $ git clone https://opendev.org/openstack/horizon - .. - -#. Create a virtual environment and install ``muranodashboard`` as an editable - module: - - .. code-block:: console - - $ cd horizon - $ tox -e venv -- pip install -e ../murano-dashboard - .. - -#. Prepare local settings. - - .. code-block:: console - - $ cp openstack_dashboard/local/local_settings.py.example \ - openstack_dashboard/local/local_settings.py - .. - - For more information, check out the official - `horizon documentation `_. - -#. Enable and configure Murano dashboard in the OpenStack Dashboard: - - * For Newton (and later) OpenStack installations, copy the plugin file, - local settings files, and policy files. - - .. code-block:: console - - $ cp ../murano-dashboard/muranodashboard/local/enabled/*.py \ - openstack_dashboard/local/enabled/ - - $ cp ../murano-dashboard/muranodashboard/local/local_settings.d/*.py \ - openstack_dashboard/local/local_settings.d/ - - $ cp ../murano-dashboard/muranodashboard/conf/* openstack_dashboard/conf/ - .. - - * For the OpenStack installations prior to the Newton release, run: - - .. code-block:: console - - $ cp ../murano-dashboard/muranodashboard/local/_50_murano.py \ - openstack_dashboard/local/enabled/ - .. - - Customize local settings of your horizon installation, by editing the - :file:`openstack_dashboard/local/local_settings.py` file: - - .. code-block:: python - - ... - ALLOWED_HOSTS = '*' - - # Provide OpenStack Lab credentials - OPENSTACK_HOST = '%OPENSTACK_HOST_IP%' - - ... - - DEBUG_PROPAGATE_EXCEPTIONS = DEBUG - .. - - Change the default session back end-from using browser cookies to using a - database instead to avoid issues with forms during the creation of - applications: - - .. code-block:: python - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'murano-dashboard.sqlite', - } - } - - SESSION_ENGINE = 'django.contrib.sessions.backends.db' - .. - -#. (Optional) If you do not plan to get the murano service from the keystone - application catalog, specify where the murano-api service is running: - - .. code-block:: python - - MURANO_API_URL = 'http://%MURANO_IP%:8082' - .. - -#. (Optional) If you have set up the database as a session back-end (this is - done by default with the murano local_settings file starting with Newton), - perform database migration: - - .. code-block:: console - - $ tox -e venv -- python manage.py migrate --noinput - .. - -#. Run the Django server at 127.0.0.1:8000 or provide different IP and PORT - parameters: - - .. code-block:: console - - $ tox -e venv -- python manage.py runserver - .. - -.. note:: - - The development server restarts automatically following every code change. -.. - -**Result:** The murano dashboard is available at http://IP:PORT. diff --git a/doc/source/install/get_started.rst b/doc/source/install/get_started.rst deleted file mode 100644 index 3f735fcb6..000000000 --- a/doc/source/install/get_started.rst +++ /dev/null @@ -1,23 +0,0 @@ -==================================== -Application Catalog service overview -==================================== -The Application Catalog service consists of the following components: - -``murano`` command-line client - A CLI that communicates with the ``murano-api`` to publish various - cloud-ready applications on new virtual machines. - -``murano-api`` service - An OpenStack-native REST API that processes API requests by sending - them to the ``murano-engine`` service via AMQP. - -``murano-agent`` service - The agent that runs on guest VMs and executes the deployment plan, - a combination of execution plan templates and scripts. - -``murano-engine`` service - The workflow component of Murano, responsible for the deployment of an - environment. - -``murano-dashboard`` service - Murano UI implemented as a plugin for the OpenStack Dashboard. diff --git a/doc/source/install/import-murano-apps.rst b/doc/source/install/import-murano-apps.rst deleted file mode 100644 index 4c38a3d57..000000000 --- a/doc/source/install/import-murano-apps.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. - Copyright 2014 Mirantis, Inc. - - Licensed 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. - -Applications need to be imported to fill the catalog. -This can be done via the dashboard or via CLI: - -1. Clone the murano apps repository. - - .. code-block:: console - - cd ~/murano - git clone https://opendev.org/openstack/murano-apps - .. - -2. Import every package you need from this repository, using the command - below. - - .. code-block:: console - - cd ~/murano/murano - pushd ../murano-apps/Docker/Applications/%APP-NAME%/package - zip -r ~/murano/murano/app.zip * - popd - tox -e venv -- murano --murano-url http://:8082 package-import app.zip diff --git a/doc/source/install/index.rst b/doc/source/install/index.rst deleted file mode 100644 index a6fb6de24..000000000 --- a/doc/source/install/index.rst +++ /dev/null @@ -1,21 +0,0 @@ -=========================== -Application Catalog service -=========================== - -.. toctree:: - :maxdepth: 2 - - get_started.rst - install.rst - verify.rst - next-steps.rst - -The Murano Project introduces an application catalog to OpenStack, enabling -application developers and cloud administrators to publish various cloud-ready -applications in a browsable categorized catalog. Cloud users -- including -inexperienced ones -- can then use the catalog to compose reliable application -environments with the push of a button. - -This chapter assumes a working setup of OpenStack following the -`OpenStack Installation Tutorial -`_. diff --git a/doc/source/install/install-api.rst b/doc/source/install/install-api.rst deleted file mode 100644 index 097b3c1d0..000000000 --- a/doc/source/install/install-api.rst +++ /dev/null @@ -1,106 +0,0 @@ -.. - Copyright 2014 Mirantis, Inc. - - Licensed 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. - -Install Murano API -~~~~~~~~~~~~~~~~~~ - -This section describes how to install and configure the Application Catalog -service for Ubuntu 16.04 (LTS). - -.. include:: common_prerequisites.rst - -Install and configure components --------------------------------- - -#. Install the packages: - - .. code-block:: console - - # apt-get update - - # apt-get install murano-engine murano-api - -#. Edit ``murano.conf`` with your favorite editor. Below is an example - which contains basic settings you likely need to configure. - - .. note:: - - The example below uses SQLite database. Edit **[database]** section - if you want to use any other database type. - .. - - .. code-block:: ini - - [DEFAULT] - debug = true - verbose = true - transport_url = rabbit://%RABBITMQ_USER%:%RABBITMQ_PASSWORD%@%RABBITMQ_SERVER_IP%:5672/ - - ... - - [database] - connection = mysql+pymysql://murano:MURANO_DBPASS@controller/murano - - ... - - [keystone] - auth_url = http://%OPENSTACK_KEYSTONE_ENDPOINT% - - ... - - [keystone_authtoken] - project_domain_name = Default - project_name = %OPENSTACK_ADMIN_PROJECT% - user_domain_name = Default - password = %OPENSTACK_ADMIN_PASSWORD% - username = %OPENSTACK_ADMIN_USER% - auth_url = http://%OPENSTACK_KEYSTONE_ENDPOINT% - auth_type = password - - ... - - [murano] - url = http://%YOUR_HOST_IP%:8082 - - [rabbitmq] - host = %RABBITMQ_SERVER_IP% - login = %RABBITMQ_USER% - password = %RABBITMQ_PASSWORD% - virtual_host = %RABBITMQ_SERVER_VIRTUAL_HOST% - - [networking] - default_dns = 8.8.8.8 # In case openstack neutron has no default - # DNS configured - .. - -#. Populate the Murano database: - - .. code-block:: console - - # su -s /bin/sh -c "murano-db-manage upgrade" murano - - .. note:: - - Ignore any deprecation messages in this output. - -Finalize installation ---------------------- - -#. Restart the Application Catalog services: - - .. code-block:: console - - # service murano-api restart - # service murano-engine restart diff --git a/doc/source/install/install-dashboard.rst b/doc/source/install/install-dashboard.rst deleted file mode 100644 index 03905747c..000000000 --- a/doc/source/install/install-dashboard.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. - Copyright 2014 Mirantis, Inc. - - Licensed 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. - -Install Murano Dashboard -======================== - -Murano API & Engine services provide the core of Murano. However, your need a -control plane to use it. This section describes how to install and run Murano -Dashboard. - -#. Install OpenStack Dashboard, the steps please reference from - `OpenStack Dashboard Install Guide `__. - -#. Install the packages: - - .. code-block:: console - - # apt install python-murano-dashboard - -#. Edit the ``/etc/openstack-dashboard/local_settings.py`` - file to customize local settings of your envi - - .. code-block:: python - - ... - OPENSTACK_HOST = '%OPENSTACK_HOST_IP%' - OPENSTACK_KEYSTONE_DEFAULT_ROLE = '%OPENSTACK_ROLE%' - ... - - .. - - Change the default session back end-from using browser cookies to using a - database instead to avoid issues with forms during the creation of - applications: - - .. code-block:: python - - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'murano-dashboard.sqlite', - } - } - - SESSION_ENGINE = 'django.contrib.sessions.backends.db' - .. - -#. (Optional) If you do not plan to get the murano service from the keystone - application catalog, specify where the murano-api service is running: - - .. code-block:: python - - MURANO_API_URL = 'http://%MURANO_IP%:8082' - .. - -Finalize installation ---------------------- - -#. Restart the Apache service: - - .. code-block:: console - - # service apache2 restart diff --git a/doc/source/install/install-network-config.rst b/doc/source/install/install-network-config.rst deleted file mode 100644 index 1b629403a..000000000 --- a/doc/source/install/install-network-config.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. - Copyright 2014 Mirantis, Inc. - - Licensed 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. - -===================== -Network Configuration -===================== -Murano may work in various networking environments and is capable of detecting -the current network configuration and choosing the appropriate settings -automatically. However, some additional actions are required to support -advanced scenarios. - -Nova network support -==================== -Nova Network is the simplest networking solution, which has limited -capabilities but is available on any OpenStack deployment without the need to -deploy any additional components. For more information about Nova Network, see -``__. - -When a new Murano Environment is created, Murano checks if a dedicated -networking service (i.e. Neutron) exists in the current OpenStack deployment. -It relies on Keystone's service catalog for that. If such a service is not -present, Murano automatically falls back to Nova Network. No further -configuration is needed in this case; all the VMs spawned by Murano will join -the same network. - -Neutron support -=============== -If Neutron is installed, Murano enables its advanced networking features that -give you the ability to not care about configuring networks for your -application. - -By default, Murano will create an isolated network for each environment and -attach all VMs needed by your application to that network. To install and -configure applications in just-spawned virtual machines, Murano also requires -a router connected to the external network. - -Automatic Neutron network configuration -======================================= -To create a router automatically, provide the following parameters in the -config file: - -.. code-block:: ini - - [networking] - - external_network = %EXTERNAL_NETWORK_NAME% - router_name = %MURANO_ROUTER_NAME% - create_router = true -.. diff --git a/doc/source/install/install.rst b/doc/source/install/install.rst deleted file mode 100644 index f6ab49972..000000000 --- a/doc/source/install/install.rst +++ /dev/null @@ -1,32 +0,0 @@ -.. _install: - -Install and configure -~~~~~~~~~~~~~~~~~~~~~ - -This section describes how to install and configure the -Application Catalog service, code-named murano, on the controller node. - -This section assumes that you already have a working OpenStack environment with -at least the following components installed: Identity service, Image service, -Compute service, Networking service, Block Storage service and Orchestration -service. See `OpenStack Install Guides `__. - -Note that installation and configuration vary by distribution. Currently, -this installation guide is tailored toward Ubuntu environments, but can easily -be adapted to work with other types of distros. - -.. note:: - - Fedora support wasn't thoroughly tested. We do not guarantee that murano - will work on Fedora. -.. - -.. toctree:: - :maxdepth: 2 - - install-api.rst - install-dashboard.rst - from-source.rst - install-network-config.rst - enable-ssl.rst diff --git a/doc/source/install/next-steps.rst b/doc/source/install/next-steps.rst deleted file mode 100644 index d04b2acc8..000000000 --- a/doc/source/install/next-steps.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _next-steps: - -Next steps -~~~~~~~~~~ - -Your OpenStack environment now includes the Murano service. - -Import Murano Applications --------------------------- -.. include:: import-murano-apps.rst - -Additional Resources --------------------- - -#. To add additional services, see - ``__. - -#. If you would like to add glare as the storage service for packages, see: - ``__. diff --git a/doc/source/install/verify.rst b/doc/source/install/verify.rst deleted file mode 100644 index 24af77f43..000000000 --- a/doc/source/install/verify.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. _verify: - -Verify operation -~~~~~~~~~~~~~~~~ - -Verify operation of the Application Catalog service. - -.. note:: - - Perform these commands on the controller node. - -#. Source the ``admin`` project credentials to gain access to - admin-only CLI commands: - - .. code-block:: console - - $ . admin-openrc - -#. List service components to verify successful launch and registration - of each process: - - .. code-block:: console - - $ openstack service list | grep application-catalog - | 7b12ef5edef848fc9200c271f71b1307 | murano | application-catalog | \ No newline at end of file diff --git a/doc/source/reference/appendix/appendix_index.rst b/doc/source/reference/appendix/appendix_index.rst deleted file mode 100644 index 13b430cc6..000000000 --- a/doc/source/reference/appendix/appendix_index.rst +++ /dev/null @@ -1,12 +0,0 @@ -Appendix -~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - murano_concepts - tutorials - rest_api_spec - cli_ref - glossary - articles/articles_index diff --git a/doc/source/reference/appendix/articles/articles_index.rst b/doc/source/reference/appendix/articles/articles_index.rst deleted file mode 100644 index 36d90bec2..000000000 --- a/doc/source/reference/appendix/articles/articles_index.rst +++ /dev/null @@ -1,41 +0,0 @@ -Miscellaneous -~~~~~~~~~~~~~ - -**Background Concepts for Murano** - -.. toctree:: - :maxdepth: 1 - - workflow - - -**Tutorials** - -.. toctree:: - :maxdepth: 1 - - image_builders/index - test_docs - - -**Guidelines** - -.. toctree:: - :maxdepth: 1 - - guidelines - -**Gerrit review dashboard** - -.. toctree:: - :maxdepth: 1 - - murano_gerrit_dashboard - - -**API specification** - -.. toctree:: - :maxdepth: 1 - - specification/index diff --git a/doc/source/reference/appendix/articles/guidelines.rst b/doc/source/reference/appendix/articles/guidelines.rst deleted file mode 100644 index 1e497b3e7..000000000 --- a/doc/source/reference/appendix/articles/guidelines.rst +++ /dev/null @@ -1,83 +0,0 @@ -====================== -Development Guidelines -====================== - -Coding Guidelines ------------------ - -For all the code in Murano we have a rule - it should pass `PEP 8`_. - -To check your code against PEP 8 run: - -:: - - tox -e pep8 - - -.. seealso:: - - * https://pep8.readthedocs.org/en/latest/ - * https://flake8.readthedocs.org - * https://docs.openstack.org/hacking/latest/ - -Blueprints and Specs --------------------- - -Murano team uses the `murano-specs`_ repository for its blueprint and -specification (specs) review process. See `Launchpad`_ to propose or -see the status of a current blueprint. - -Testing Guidelines ------------------- - -Murano has a suite of tests that are run on all submitted code, -and it is recommended that developers execute the tests themselves to -catch regressions early. Developers are also expected to keep the -test suite up-to-date with any submitted code changes. - -Unit tests are located at ``murano/tests``. - -Murano's suite of unit tests can be executed in an isolated environment -with `Tox`_. To execute the unit tests run the following from the root of -Murano repo on Python 3.x: - -:: - - tox -e py3.x - - -Documentation Guidelines ------------------------- - -Murano dev-docs are written using Sphinx / RST and located in the main repo -in ``doc`` directory. - -The documentation in docstrings should follow the `PEP 257`_ conventions -(as mentioned in the `PEP 8`_ guidelines). - -More specifically: - -1. Triple quotes should be used for all docstrings. -2. If the docstring is simple and fits on one line, then just use - one line. -3. For docstrings that take multiple lines, there should be a newline - after the opening quotes, and before the closing quotes. -4. `Sphinx`_ is used to build documentation, so use the restructured text - markup to designate parameters, return values, etc. Documentation on - the sphinx specific markup can be found here: - - - -Run the following command to build docs locally. - -:: - - tox -e docs - - -.. _PEP 8: http://www.python.org/dev/peps/pep-0008/ -.. _PEP 257: http://www.python.org/dev/peps/pep-0257/ -.. _Tox: http://tox.testrun.org/ -.. _Sphinx: http://sphinx.pocoo.org/markup/index.html -.. _murano-specs: http://opendev.org/openstack/murano-specs -.. _Launchpad: http://blueprints.launchpad.net/murano diff --git a/doc/source/reference/appendix/articles/image_builders/index.rst b/doc/source/reference/appendix/articles/image_builders/index.rst deleted file mode 100644 index faa1d50fd..000000000 --- a/doc/source/reference/appendix/articles/image_builders/index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _building_images: - -===================== -Building Murano Image -===================== - -.. toctree:: - :maxdepth: 2 - - windows - linux - upload diff --git a/doc/source/reference/appendix/articles/image_builders/linux.rst b/doc/source/reference/appendix/articles/image_builders/linux.rst deleted file mode 100644 index d4d0e9923..000000000 --- a/doc/source/reference/appendix/articles/image_builders/linux.rst +++ /dev/null @@ -1,64 +0,0 @@ -=========== -Linux Image -=========== - -At the moment the best way to build a Linux image with the murano agent is -to use disk image builder. - - -.. note:: - - Disk image builder requires sudo rights - - -The process is quite simple. Let's assume that you use a directory ~/git -for cloning git repositories: - -.. code-block:: console - - export GITDIR=~/git - mkdir -p $GITDIR - - -Clone the components required to build an image to that directory: - -.. code-block:: console - - cd $GITDIR - git clone https://opendev.org/openstack/murano - git clone https://opendev.org/openstack/murano-agent - - -Install diskimage-builder - -.. code-block:: console - - sudo pip install diskimage-builder - - -Install additional packages required by disk image builder: - -.. code-block:: console - - sudo apt-get install qemu-utils curl python3-tox - - -Export paths where additional dib elements are located: - -.. code-block:: console - - export ELEMENTS_PATH=$GITDIR/murano/contrib/elements:$GITDIR/murano-agent/contrib/elements - - -Build Ubuntu-based image with the murano agent: - -.. code-block:: console - - disk-image-create vm ubuntu murano-agent -o murano-agent.qcow2 - - -If you need a Fedora based image, replace 'ubuntu' to 'fedora' in the last command. - -It'll take a while (up to 30 minutes if your hard drive and internet connection are slow). - -When you are done upload the murano-agent.qcow2 image to glance and play :) diff --git a/doc/source/reference/appendix/articles/image_builders/upload.rst b/doc/source/reference/appendix/articles/image_builders/upload.rst deleted file mode 100644 index d1029bdcb..000000000 --- a/doc/source/reference/appendix/articles/image_builders/upload.rst +++ /dev/null @@ -1,90 +0,0 @@ -.. _upload_images: - -======================== -Upload image into glance -======================== - -To deploy applications with murano, virtual machine images should be uploaded into glance in a special way - *murano_image_info* property should be set. - -1. Use the OpenStack client image create command to import your disk image to glance: - -.. code-block:: console - - openstack image create --public \ - > --disk-format qcow2 --container-format bare \ - > --file --property -.. - -Replace the command line arguments to openstack image create with the appropriate values for your environment and disk image: - -* Replace **** with the local path to the image file to upload. E.g. **ws-2012-std.qcow2**. - -* Replace **** with the following property string - -* Replace **** with the name that users will refer to the disk image by. E.g. **ws-2012-std** - -.. code-block:: text - - murano_image_info='{"title": "Windows 2012 Standard Edition", "type": "windows.2012"}' -.. - -where: - -* **title** - user-friendly description of the image -* **type** - murano image type, see :ref:`murano_image_types` - -2. To update metadata of the existing image run the command: - -.. code-block:: console - - openstack image set --property -.. - -* Replace **** with murano_image_info property, e.g. - -* Replace **** with image id from the previous command output. - -.. code-block:: text - - murano_image_info='{"title": "Windows 2012 Standard Edition", "type": "windows.2012"}' -.. - -.. warning:: - - The value of the **--property** argument (named **murano_image_info**) is a JSON string. - Only double quotes are valid in JSON, so please type the string exactly as in the example above. -.. - -.. note:: - - Existing images could be marked in a simple way in the horizon UI with the murano dashboard installed. - Navigate to *Applications -> Manage -> Images -> Mark Image* and fill up a form: - - * **Image** - ws-2012-std - * **Title** - My Prepared Image - * **Type** - Windows Server 2012 -.. - -After these steps desired image can be chosen in application creation wizard. - - -.. _murano_image_types: - -Murano image types ------------------- - -.. list-table:: - :header-rows: 1 - - * - Type Name - - Description - - * - windows.2012 - - Windows Server 2012 - - * - linux - - Generic Linux images, Ubuntu / Debian, RedHat / Centos, etc - - * - cirros.demo - - Murano demo image, based on CirrOS -.. diff --git a/doc/source/reference/appendix/articles/image_builders/windows.rst b/doc/source/reference/appendix/articles/image_builders/windows.rst deleted file mode 100644 index 1ed749d70..000000000 --- a/doc/source/reference/appendix/articles/image_builders/windows.rst +++ /dev/null @@ -1,172 +0,0 @@ -MS Windows image builder for OpenStack Murano -============================================= - -Introduction ------------- - -This repository contains MS Windows templates, powershell scripts and bash scripted logic used to create qcow2 images -for QEMU/KVM based virtual machines used in OpenStack. - -MS Windows Versions -------------------- - -Supported by builder versions with en_US localization: - -* Windows 2012 R2 -* Windows 2012 R2 Core -* Windows 2008 R2 -* Windows 2008 R2 Core - -Getting Started ---------------- - -Trial versions of Windows 2008 R2 / 2012 R2 used by default. You could use these images for 180 days without activation. -You could download evaluation versions from official Microsoft website: - -* `[Windows 2012 R2 - download] `_ -* `[Windows 2008 R2 - download] `_ - -System requirements -~~~~~~~~~~~~~~~~~~~ - -* Debian based Linux distribution, like Ubuntu, Mint and so on. -* Packages required: - ``qemu-kvm virt-manager virt-goodies virtinst bridge-utils libvirt-bin - uuid-runtime samba samba-common cifs-utils`` -* User should be able to run sudo without password prompt! - - .. code-block:: console - - sudo echo "${USER} ALL = NOPASSWD: ALL" > /etc/sudoers.d/${USER} - sudo chmod 440 /etc/sudoers.d/${USER} - -* Free disk space > 50G on partition where script will spawn virtual machines because of ``40G`` required by virtual - machine HDD image. -* Internet connectivity. -* Samba shared resource. - -Configuring builder -~~~~~~~~~~~~~~~~~~~ - -Configuration parameters to tweak: - -``[default]`` - -* ``workdir`` - place where script would prepare all software required by build scenarios. By `default` is not set, - i.e. script directory would used as root of working space. -* ``vmsworkdir`` - must contain valid path, this parameter tells script where it should spawn virtual machines. -* ``runparallel`` - *true* of *false*, **false** set by default. This parameter describes how to start virtual machines, - one by one or in launch them in background. - -``[samba]`` - -* ``mode`` - *local* or *remote*. In local mode script would try to install and configure Samba server locally. If set - to remote, you should also provide information about connection. -* ``host`` - in local mode - is 192.168.122.1, otherwise set proper ip address. -* ``user`` - set to **guest** by default in case of guest rw access. -* ``domain`` - Samba server user domain, if not set `host` value used. -* ``password`` - Samba server user password. -* ``image-builder-share`` - Samba server remote directory. - -MS Windows install preparation: - -``[win2k12r2]`` or ``[win2k8r2]`` - shortcuts for 2012 R2 and 2008 R2. - -* ``enabled`` - *true* of *false*, include or exclude release processing by script. -* ``editions`` - standard, core or both(space used as delimiter). -* ``iso`` - local path to iso file - -By default ``[win2k8r2]`` - disabled, if you need you can enable this release in *config.ini* file. - -Run ---- - -Preparation -~~~~~~~~~~~ - -Run ``chmod +x *.sh`` in builder directory to make script files executable. - -Command line parameters: -~~~~~~~~~~~~~~~~~~~~~~~~ - -``runme.sh`` - the main script - -* ``--help`` - shows usage -* ``--forceinstall-dependencies`` - Runs dependencies install. -* ``--check-smb`` - Run checks or configuration of Samba server. -* ``--download-requirements`` - Download all required and configures software except MS Windows ISO. -* ``--show-configured`` - Shows configured and available to use MS Windows releases. -* ``--run`` - normal run - -Experimental options: -^^^^^^^^^^^^^^^^^^^^^ - -* ``--config-file`` - Set configuration file location instead of default. - -Use cases ---------- - -All examples below describes changes in ``config.ini`` file - -1. I want to build one image for specific version and edition. For example: version - **2012 R2** and edition - - **standard**. Steps to reach the goal: - - * Disable ``[win2k8r2]`` from script processing. - - .. code-block:: ini - - [win2k8r2] - enabled=false - - - Update ``[win2k12r2]`` with desired edition(**standard**). - - .. code-block:: ini - - [win2k12r2] - enabled=true - editions=standard - - * Execute ``runme.sh --run`` - -2. I want to build two images for specific version with all supported by script editions. For example: **2012 R2** and - editions - **standard** and **core**. Steps to reach the goal: - - * Disable `[win2k8r2]` from script processing. - - .. code-block:: ini - - [win2k8r2] - enabled=false - - * Update ``[win2k12r2]`` with desired editions(**standard** and **core**). - - .. code-block:: ini - - [win2k12r2] - enabled=true - editions=standard core - - - * Execute ``runme.sh --run`` - -3. I want to build two images for all supported by script versions with specific editions. For example: versions - - **2012 R2** and **2008 R2** and edition - **core**. Steps to reach the goal: - - * Update ``[win2k8r2]`` with desired edition(**core**). - - .. code-block:: ini - - [win2k8r2] - enabled=true - editions=core - - * Update ``[win2k12r2]`` with desired edition(**core**). - - .. code-block:: ini - - [win2k12r2] - enabled=true - editions=core - - * Execute ``runme.sh --run`` - diff --git a/doc/source/reference/appendix/articles/multi_region.rst b/doc/source/reference/appendix/articles/multi_region.rst deleted file mode 100644 index da091c04b..000000000 --- a/doc/source/reference/appendix/articles/multi_region.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. _multi-region: - -============================= -Support for OpenStack regions -============================= -Murano supports multi-region deployment. If OpenStack setup has several regions -it is possible to choose the region to deploy an application. - -There is the new option in the murano configuration file: - -* `home_region` - default region name used to get services - endpoints. The region where murano-api resides. - -Now murano has two possible ways to deploy apps in different regions: - -1. Deploy an application in the current murano region. -2. Associate environments with regions. - -Deploy an app in the current region -=================================== -Each region has a copy of murano services and its own RabbitMQ for api to -engine communication. In this case application will be deployed to the same -region that murano run in. - -.. seealso:: - - :ref:`multi_region` - -Associate environments with regions -=================================== -Murano services are in one region but environments can be associated with -different regions. There are two new properties in the class -`io.murano.Environment`: - -* `regionConfigs` - a dict with RabbitMQ settings for each region. The - structure of the agentRabbitMq part of the dict is identical to [rabbitmq] - section in the `murano.conf` file. For example: - - .. code-block:: yaml - - regionConfigs: - RegionOne: - agentRabbitMq: - host: 192.1.1.1 - login: admin - password: admin - - User can store such configs as YAML or JSON files. These config files must - be stored in a special folder that is configured in [engine] section of - `murano.conf` file under `class_configs` key and must be named using - %FQ class name%.json or %FQ class name%.yaml pattern. - -* `region` - region name to deploy an app. It can be passed when creating - environment via CLI: - - .. code-block:: console - - murano environment-create environment_name --region RegionOne - - If it is not specified a value from `home_region` option of `murano.conf` - file will be used. diff --git a/doc/source/reference/appendix/articles/murano_gerrit_dashboard.rst b/doc/source/reference/appendix/articles/murano_gerrit_dashboard.rst deleted file mode 100644 index b23f0e097..000000000 --- a/doc/source/reference/appendix/articles/murano_gerrit_dashboard.rst +++ /dev/null @@ -1,57 +0,0 @@ -Murano Gerrit Dashboard -======================= - -Description ------------ -If you would like to contribute to murano by reviewing patches to -murano-related projects — you can use this gerrit dashboard, or create your own -using -`Gerrit Dash Creator `__ - -URL ---- - -:: - - https://review.opendev.org/#/dashboard/?foreach=%28project%3A%5E.%2A%2F.%2Amurano.%2A+OR+project%3Aopenstack%2Fyaql%29+NOT+label%3AWorkflow%3C%3D%2D1+NOT+label%3ACode%2DReview%3C%3D%2D2+status%3Aopen&title=Murano&My+Patches=owner%3Aself&You+are+a+reviewer%2C+but+haven%27t+voted+in+the+current+revision=NOT+label%3ACode%2DReview%3C%3D2%2Cself+reviewer%3Aself+NOT+owner%3Aself&Need+Feedback=NOT+label%3ACode%2DReview%3C%3D2+NOT+label%3AVerified%3C%3D%2D1+NOT+owner%3Aself&Passed+Jenkins%2C+No+Negative+Feedback=label%3ACode%2DReview%3E%3D1+NOT+label%3ACode%2DReview%3C%3D%2D1+AND+NOT+label%3AVerified%3C%3D%2D1+NOT+owner%3Aself+NOT+reviewer%3Aself+limit%3A50&Maybe+Review%3F=NOT+owner%3Aself+NOT+reviewer%3Aself+limit%3A25&My+%2B1s=label%3ACode%2DReview%3D1%2Cself+limit%3A25&Need+final+%2B2=label%3ACode%2DReview%3E%3D2+NOT+label%3ACode%2DReview%3C%3D%2D1+NOT+label%3AVerified%3C%3D%2D1+NOT+label%3ACode%2DReview%3C%3D2%2Cself+NOT+owner%3Aself+limit%3A25&My+%2B2s=label%3ACode%2DReview%3D2%2Cself+limit%3A25 - -`View this dashboard `__ - - -Configuration -------------- - -:: - - - [dashboard] - title = Murano - description = Murano Review Inbox - foreach = (project:^.*/.*murano.* OR project:openstack/yaql) NOT label:Workflow<=-1 NOT label:Code-Review<=-2 status:open - - [section "My Patches"] - query = owner:self - - [section "You are a reviewer, but haven't voted in the current revision"] - query = NOT label:Code-Review<=2,self reviewer:self NOT owner:self - - [section "Need Feedback"] - query = NOT label:Code-Review<=2 NOT label:Verified<=-1 NOT owner:self - - [section "Passed Jenkins, No Negative Feedback"] - query = label:Code-Review>=1 NOT label:Code-Review<=-1 AND NOT label:Verified<=-1 NOT owner:self NOT reviewer:self limit:50 - - [section "Maybe Review?"] - query = NOT owner:self NOT reviewer:self limit:25 - - [section "My +1s"] - query = label:Code-Review=1,self limit:25 - - [section "Need final +2"] - query = label:Code-Review>=2 NOT label:Code-Review<=-1 NOT label:Verified<=-1 NOT label:Code-Review<=2,self NOT owner:self limit:25 - - [section "My +2s"] - query = label:Code-Review=2,self limit:25 - - - diff --git a/doc/source/reference/appendix/articles/specification/index.rst b/doc/source/reference/appendix/articles/specification/index.rst deleted file mode 100644 index 48bb53e49..000000000 --- a/doc/source/reference/appendix/articles/specification/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -=========================== -Murano API v1 specification -=========================== - -.. toctree:: - :maxdepth: 1 - - overview - murano-api - murano-repository - murano-env-temp \ No newline at end of file diff --git a/doc/source/reference/appendix/articles/specification/murano-api.rst b/doc/source/reference/appendix/articles/specification/murano-api.rst deleted file mode 100644 index 80d0bc9a4..000000000 --- a/doc/source/reference/appendix/articles/specification/murano-api.rst +++ /dev/null @@ -1,1316 +0,0 @@ -========== -Murano API -========== - -Glossary -======== - -.. _glossary-environment: - -* **Environment** - - The environment is a set of applications managed by a single project (tenant). They could be related logically with each other or not. - Applications within a single environment may comprise of complex configuration while applications in different environments are always - independent from one another. Each environment is associated with a single OpenStack project. - -.. _glossary-sessions: - -* **Session** - - Since murano environments are available for local modification for different users and from different locations, it's needed to store local modifications somewhere. - Sessions were created to provide this opportunity. After a user adds an application to the environment - a new session is created. - After a user sends an environment to deploy, a session with a set of applications changes status to *deploying* and all other open sessions for that environment become invalid. - One session could be deployed only once. - -* **Object Model** - - Applications are defined in MuranoPL object model, which is defined as a JSON object. - The murano API doesn't know anything about it. - -* **Package** - - A .zip archive, containing instructions for an application deployment. - -* **Environment-Template** - The environment template is the specification of a set of applications managed by a single project, which are - related to each other. The environment template is stored in an environment template catalog, and it can be - managed by the user (creation, deletion, updating). Finally, it can be deployed on OpenStack by translating - into an environment. - - -Environment API -=============== - -+----------------------+------------+-------------------------------------------+ -| Attribute | Type | Description | -+======================+============+===========================================+ -| id | string | Unique ID | -+----------------------+------------+-------------------------------------------+ -| name | string | User-friendly name | -+----------------------+------------+-------------------------------------------+ -| created | datetime | Creation date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| updated | datetime | Modification date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| tenant_id | string | OpenStack project ID | -+----------------------+------------+-------------------------------------------+ -| version | int | Current version | -+----------------------+------------+-------------------------------------------+ -| networking | string | Network settings | -+----------------------+------------+-------------------------------------------+ -| acquired_by | string | Id of a session that acquired this | -| | | environment (for example is deploying it) | -+----------------------+------------+-------------------------------------------+ -| status | string | Deployment status: ready, pending, | -| | | deploying | -+----------------------+------------+-------------------------------------------+ - -**Common response codes** - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Operation completed successfully | -+----------------+-----------------------------------------------------------+ -| 403 | User is not authorized to perform the operation | -+----------------+-----------------------------------------------------------+ - -List environments ------------------ - -*Request* - - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| GET | /environments | Get a list of existing | -| | | Environments | -+----------+----------------------------------+----------------------------------+ - - -*Parameters:* - -* `all_tenants` - boolean, indicates whether environments from all projects are listed. - *True* environments from all projects are listed. Admin user required. - *False* environments only from current project are listed (default like option unspecified). - -* `tenant` - indicates environments from specified tenant are listed. Admin user required. - -*Response* - - -This call returns a list of environments. Only the basic properties are -returned. - -:: - - { - "environments": [ - { - "status": "ready", - "updated": "2014-05-14T13:02:54", - "networking": {}, - "name": "test1", - "created": "2014-05-14T13:02:46", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "id": "2fa5ab704749444bbeafe7991b412c33" - }, - { - "status": "ready", - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "id": "744e44812da84e858946f5d817de4f72" - } - ] - } - -Create environment ------------------- - -+----------------------+------------+--------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+========================================================+ -| name | string | Environment name; at least one non-white space symbol | -+----------------------+------------+--------------------------------------------------------+ - -*Request* - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| POST | /environments | Create new Environment | -+----------+----------------------------------+----------------------------------+ - -* **Content-Type** - application/json - -* **Example** - {"name": "env_name"} - -*Response* - -:: - - { - "id": "ce373a477f211e187a55404a662f968", - "name": "env_name", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a", - "version": 0 - } - - -Update environment ------------------- - -+----------------------+------------+--------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+========================================================+ -| name | string | Environment name; at least one non-white space symbol | -+----------------------+------------+--------------------------------------------------------+ - -*Request* - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| PUT | /environments/ | Update an existing Environment | -+----------+----------------------------------+----------------------------------+ - -* **Content-Type** - application/json - -* **Example** - {"name": "env_name_changed"} - -*Response* - -**Content-Type** - application/json - -:: - - { - "id": "ce373a477f211e187a55404a662f968", - "name": "env_name_changed", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:45:54Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a", - "version": 0 - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Edited environment | -+----------------+-----------------------------------------------------------+ -| 400 | Environment name must contain at least one non-white space| -| | symbol | -+----------------+-----------------------------------------------------------+ -| 403 | User is not authorized to access environment | -+----------------+-----------------------------------------------------------+ -| 404 | Environment not found | -+----------------+-----------------------------------------------------------+ -| 409 | Environment with specified name already exists | -+----------------+-----------------------------------------------------------+ - -Get environment details ------------------------ - -*Request* - -Return information about the environment itself and about applications, including this environment. - -+----------+----------------------------------+-----------------------------------+----------------------------------+ -| Method | URI | Header | Description | -+==========+==================================+===================================+==================================+ -| GET | /environments/{id} | X-Configuration-Session (optional)| Response detailed information | -| | | | about Environment including | -| | | | child entities | -+----------+----------------------------------+-----------------------------------+----------------------------------+ - -*Response* - -**Content-Type** - application/json - -:: - - { - "status": "ready", - "updated": "2014-05-14T13:12:26", - "networking": {}, - "name": "quick-env-2", - "created": "2014-05-14T13:09:55", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 1, - "services": [ - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "name": "exgchhv6nbika2", - "ipAddresses": [ - "10.0.0.200" - ], - "?": { - "type": "io.murano.resources.Instance", - "id": "14cce9d9-aaa1-4f09-84a9-c4bb859edaff" - } - }, - "name": "rewt4w56", - "?": { - "status": "ready", - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "446373ef-03b5-4925-b095-6c56568fa518" - } - } - ], - "id": "20d4a012628e4073b48490a336a8acbf" - } - -Delete environment ------------------- - -*Request* - - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| DELETE | /environments/{id}?abandon | Remove specified Environment. | -+----------+----------------------------------+----------------------------------+ - - -*Parameters:* - -* `abandon` - boolean, indicates how to delete environment. *False* is used if - all resources used by environment must be destroyed; *True* is used when just - database must be cleaned - - -*Response* - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Environment deleted successfully | -+----------------+-----------------------------------------------------------+ -| 403 | User is not allowed to delete this resource | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified environment doesn`t exist | -+----------------+-----------------------------------------------------------+ - - -Environment configuration API -============================= - -Multiple :ref:`sessions ` could be opened for one environment -simultaneously, but only one session going to be deployed. First session that -starts deploying is going to be deployed; other ones become invalid and could -not be deployed at all. -User could not open new session for environment that in -*deploying* state (that's why we call it "almost lock free" model). - -+----------------------+------------+-------------------------------------------+ -| Attribute | Type | Description | -+======================+============+===========================================+ -| id | string | Session unique ID | -+----------------------+------------+-------------------------------------------+ -| environment\_id | string | Environment that going to be modified | -| | | during this session | -+----------------------+------------+-------------------------------------------+ -| created | datetime | Creation date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| updated | datetime | Modification date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| user\_id | string | Session owner ID | -+----------------------+------------+-------------------------------------------+ -| version | int | Environment version for which | -| | | configuration session is opened | -+----------------------+------------+-------------------------------------------+ -| state | string | Session state. Could be: open, deploying, | -| | | deployed | -+----------------------+------------+-------------------------------------------+ - -Configure environment / open session ------------------------------------- - -During this call a new working session is created with its ID returned in response body. -Notice that the session ID should be added to request headers with name ``X-Configuration-Session`` -in subsequent requests when necessary. - -*Request* - - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| POST | /environments//configure | Creating new configuration | -| | | session | -+----------+----------------------------------+----------------------------------+ - -*Response* - -**Content-Type** - application/json - -:: - - { - "id": "257bef44a9d848daa5b2563779714820", - "updated": datetime.datetime(2014, 5, 14, 14, 17, 58, 949358), - "environment_id": "744e44812da84e858946f5d817de4f72", - "ser_id": "4e91d06270c54290b9dbdf859356d3b3", - "created": datetime.datetime(2014, 5, 14, 14, 17, 58, 949305), - "state": "open", - "version": 0L - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Session created successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 403 | Could not open session for environment, environment has | -| | deploying status | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified environment doesn`t exist | -+----------------+-----------------------------------------------------------+ - - -Deploy session --------------- - -With this request all local changes made within the environment start to deploy on OpenStack. - -*Request* - -+----------+---------------------------------+--------------------------------+ -| Method | URI | Description | -+==========+=================================+================================+ -| POST | /environments//sessions/| Deploy changes made in session | -| | /deploy | with specified session_id | -+----------+---------------------------------+--------------------------------+ - -*Response* - - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Session status changes to *deploying* | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 403 | Session is already deployed or deployment is in progress | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified session or environment doesn`t exist | -+----------------+-----------------------------------------------------------+ - -Get session details -------------------- - -*Request* - -+----------+---------------------------------+---------------------------+ -| Method | URI | Description | -+==========+=================================+===========================+ -| GET | /environments//sessions/| Get details about session | -| | | with specified session_id | -+----------+---------------------------------+---------------------------+ - -*Response* - - -:: - - { - "id": "4aecdc2178b9430cbbb8db44fb7ac384", - "environment_id": "4dc8a2e8986fa8fa5bf24dc8a2e8986fa8", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:54Z", - "user_id": "d7b501094caf4daab08469663a9e1a2b", - "version": 0, - "state": "deploying" - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Session details information received | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 403 | Session is invalid | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified session or environment doesn`t exist | -+----------------+-----------------------------------------------------------+ - -Delete session --------------- - -*Request* - -+----------+---------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+=================================+==================================+ -| DELETE | /environments//sessions/| Delete session with specified | -| | | session_id | -+----------+---------------------------------+----------------------------------+ - -*Response* - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Session is deleted successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 403 | Session is in deploying state and could not be deleted | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified session or environment doesn`t exist | -+----------------+-----------------------------------------------------------+ - -Environment model API -===================== - -Get environment model ---------------------- - -+----------+-------------------------------------+------------------------+--------------------------+ -| Method | URI | Header | Description | -+==========+=====================================+========================+==========================+ -| GET | /environments//model/ | X-Configuration-Session| Get an Environment model | -| | | (optional) | | -+----------+-------------------------------------+------------------------+--------------------------+ - -Specifying allows to get a specific section of the model, for example -`defaultNetworks`, `region` or `?` or any of the subsections. - -*Response* - -**Content-Type** - application/json - -.. code-block:: javascript - - { - "defaultNetworks": { - "environment": { - "internalNetworkName": "net_two", - "?": { - "type": "io.murano.resources.ExistingNeutronNetwork", - "id": "594e94fcfe4c48ef8f9b55edb3b9f177" - } - }, - "flat": null - }, - "region": "RegionTwo", - "name": "new_env", - "regions": { - "": { - "defaultNetworks": { - "environment": { - "autoUplink": true, - "name": "new_env-network", - "externalRouterId": null, - "dnsNameservers": [], - "autogenerateSubnet": true, - "subnetCidr": null, - "openstackId": null, - "?": { - "dependencies": { - "onDestruction": [{ - "subscriber": "c80e33dd67a44f489b2f04818b72f404", - "handler": null - }] - }, - "type": "io.murano.resources.NeutronNetwork/0.0.0@io.murano", - "id": "e145b50623c04a68956e3e656a0568d3", - "name": null - }, - "regionName": "RegionOne" - }, - "flat": null - }, - "name": "RegionOne", - "?": { - "type": "io.murano.CloudRegion/0.0.0@io.murano", - "id": "c80e33dd67a44f489b2f04818b72f404", - "name": null - } - }, - "RegionOne": "c80e33dd67a44f489b2f04818b72f404", - "RegionTwo": { - "defaultNetworks": { - "environment": { - "autoUplink": true, - "name": "new_env-network", - "externalRouterId": "e449bdd5-228c-4747-a925-18cda80fbd6b", - "dnsNameservers": ["8.8.8.8"], - "autogenerateSubnet": true, - "subnetCidr": "10.0.198.0/24", - "openstackId": "00a695c1-60ff-42ec-acb9-b916165413da", - "?": { - "dependencies": { - "onDestruction": [{ - "subscriber": "f8cb28d147914850978edb35eca156e1", - "handler": null - }] - }, - "type": "io.murano.resources.NeutronNetwork/0.0.0@io.murano", - "id": "72d2c13c600247c98e09e2e3c1cd9d70", - "name": null - }, - "regionName": "RegionTwo" - }, - "flat": null - }, - "name": "RegionTwo", - "?": { - "type": "io.murano.CloudRegion/0.0.0@io.murano", - "id": "f8cb28d147914850978edb35eca156e1", - "name": null - } - } - }, - services: [] - "?": { - "type": "io.murano.Environment/0.0.0@io.murano", - "_actions": { - "f7f22c174070455c9cafc59391402bdc_deploy": { - "enabled": true, - "name": "deploy", - "title": "deploy" - } - }, - "id": "f7f22c174070455c9cafc59391402bdc", - "name": null - } - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Environment model received successfully | -+----------------+-----------------------------------------------------------+ -| 403 | User is not authorized to access environment | -+----------------+-----------------------------------------------------------+ -| 404 | Environment is not found or specified section of the | -| | model does not exist | -+----------------+-----------------------------------------------------------+ - -Update environment model ------------------------- - -*Request* - -+----------+--------------------------------+------------------------+-----------------------------+ -| Method | URI | Header | Description | -+==========+================================+========================+=============================+ -| PATCH | /environments//model/ | X-Configuration-Session| Update an Environment model | -+----------+--------------------------------+------------------------+-----------------------------+ - -* **Content-Type** - application/env-model-json-patch - - Allowed operations for paths: - - * "" (model root): no operations - * "defaultNetworks": "replace" - * "defaultNetworks/environment": "replace" - * "defaultNetworks/environment/?/id": no operations - * "defaultNetworks/flat": "replace" - * "name": "replace" - * "region": "replace" - * "?/type": "replace" - * "?/id": no operations - - For other paths any operation (add, replace or remove) is allowed. - -* **Example of request body with JSON-patch** - -.. code-block:: javascript - - [{ - "op": "replace", - "path": "/defaultNetworks/flat", - "value": true - }] - -*Response* - -**Content-Type** - application/json - -See *GET* request response. - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Environment is edited successfully | -+----------------+-----------------------------------------------------------+ -| 400 | Body format is invalid | -+----------------+-----------------------------------------------------------+ -| 403 | User is not authorized to access environment or specified | -| | operation is forbidden for the given property | -+----------------+-----------------------------------------------------------+ -| 404 | Environment is not found or specified section of the | -| | model does not exist | -+----------------+-----------------------------------------------------------+ - -Environment deployments API -=========================== - -Environment deployment API allows to track changes of environment status, deployment events and errors. -It also allows to browse deployment history. - -List Deployments ----------------- - -Returns information about all deployments of the specified environment. - -*Request* - -+----------+------------------------------------+--------------------------------------+ -| Method | URI | Description | -+==========+====================================+======================================+ -| GET | /environments//deployments | Get list of environment deployments | -+----------+------------------------------------+--------------------------------------+ -| GET | /deployments | Get list of deployments for all | -| | | environments in user's project | -+----------+------------------------------------+--------------------------------------+ - -*Response* - -**Content-Type** - application/json - -:: - - { - "deployments": [ - { - "updated": "2014-05-15T07:24:21", - "environment_id": "744e44812da84e858946f5d817de4f72", - "description": { - "services": [ - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "?": { - "type": "io.murano.resources.Instance", - "id": "ef729199-c71e-4a4c-a314-0340e279add8" - }, - "name": "xkaduhv7qeg4m7" - }, - "name": "teslnet1", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "6e437be2-b5bc-4263-8814-6fd57d6ddbd5" - } - } - ], - "defaultNetworks": { - "environment": { - "name": "test2-network", - "?": { - "type": "io.murano.lib.networks.neutron.NewNetwork", - "id": "b6a1d515434047d5b4678a803646d556" - } - }, - "flat": null - }, - "name": "test2", - "?": { - "type": "io.murano.Environment", - "id": "744e44812da84e858946f5d817de4f72" - } - }, - "created": "2014-05-15T07:24:21", - "started": "2014-05-15T07:24:21", - "finished": null, - "state": "running", - "id": "327c81e0e34a4c93ad9b9052ef42b752" - } - ] - } - - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Deployments information received successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this environment | -+----------------+-----------------------------------------------------------+ - -Application management API -========================== - -All applications should be created within an environment and all environment modifications are held within the session. -Local changes apply only after successful deployment of an environment session. - -Get application details ------------------------ - -Using GET requests to applications endpoint user works with list containing all -applications for specified environment. A user can request a whole list, -specific application, or specific attribute of a specific application using tree -traversing. To request a specific application, the user should add to endpoint part -an application id, e.g.: */environments//services/*. For -selection of specific attribute on application, simply appending part with -attribute name will work. For example to request application name, user -should use next endpoint: */environments//services//name* - -*Request* - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| GET | /environments//services/ | X-Configuration-Session (optional) | -+----------------+-----------------------------------------------------------+------------------------------------+ - -**Parameters:** - -* `env_id` - environment ID, required -* `app_id` - application ID, optional - -*Response* - -**Content-Type** - application/json - -:: - - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "?": { - "type": "io.murano.resources.Instance", - "id": "060715ff-7908-4982-904b-3b2077ff55ef" - }, - "name": "hbhmyhv6qihln3" - }, - "name": "dfg34", - "?": { - "status": "pending", - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "6e7b8ad5-888d-4c5a-a498-076d092a7eff" - } - } - -Create new application ----------------------- - -Create a new application and add it to the murano environment. -Result JSON is calculated in Murano dashboard, which is based on `UI definition `_. - -*Request* - -**Content-Type** - application/json - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| POST | /environments//services | X-Configuration-Session | -+----------------+-----------------------------------------------------------+------------------------------------+ - -:: - - { - "instance": { - "flavor": "m1.medium", - "image": "clod-fedora-v3", - "?": { - "type": "io.murano.resources.Instance", - "id": "bce8308e-5938-408b-a27a-0d3f0a2c52eb" - }, - "name": "nhekhv6r7mhd4" - }, - "name": "sdf34sadf", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "190c8705-5784-4782-83d7-0ab55a1449aa" - } - } - - -*Response* - -Created application returned - - -**Content-Type** - application/json - -:: - - { - "instance": { - "flavor": "m1.medium", - "image": "cloud-fedora-v3", - "?": { - "type": "io.murano.resources.Instance", - "id": "bce8308e-5938-408b-a27a-0d3f0a2c52eb" - }, - "name": "nhekhv6r7mhd4" - }, - "name": "sdf34sadf", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "Telnet" - }, - "type": "io.murano.apps.linux.Telnet", - "id": "190c8705-5784-4782-83d7-0ab55a1449a1" - } - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Application was created successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to perform this action | -+----------------+-----------------------------------------------------------+ -| 403 | Policy prevents this user from performing this action | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Environment doesn't exist | -+----------------+-----------------------------------------------------------+ -| 400 | Required header or body are not provided | -+----------------+-----------------------------------------------------------+ - -Update applications -------------------- - -Applications list for environment can be updated. - -*Request* - -**Content-Type** - application/json - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| PUT | /environments//services | X-Configuration-Session | -+----------------+-----------------------------------------------------------+------------------------------------+ - -:: - - [{ - "instance": { - "availabilityZone": "nova", - "name": "apache-instance", - "assignFloatingIp": true, - "keyname": "", - "flavor": "m1.small", - "image": "146d5523-7b2d-4abc-b0d0-2041f920ce26", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "25185cb6f29b415fa2e438309827a797" - } - }, - "name": "ApacheHttpServer", - "enablePHP": true, - "?": { - "type": "com.example.apache.ApacheHttpServer", - "id": "6e66106d7dcb4748a5c570150a3df80f" - } - }] - - -*Response* - -Updated applications list returned - - -**Content-Type** - application/json - -:: - - [{ - "instance": { - "availabilityZone": "nova", - "name": "apache-instance", - "assignFloatingIp": true, - "keyname": "", - "flavor": "m1.small", - "image": "146d5523-7b2d-4abc-b0d0-2041f920ce26", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "25185cb6f29b415fa2e438309827a797" - } - }, - "name": "ApacheHttpServer", - "enablePHP": true, - "?": { - "type": "com.example.apache.ApacheHttpServer", - "id": "6e66106d7dcb4748a5c570150a3df80f" - } - }] - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Services are updated successfully | -+----------------+-----------------------------------------------------------+ -| 400 | Required header is not provided | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized | -+----------------+-----------------------------------------------------------+ -| 403 | Session is in deploying state and could not be updated | -| | or user is not allowed to update services | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified environment and/or session do not | -| | exist | -+----------------+-----------------------------------------------------------+ - -Delete application from environment ------------------------------------ - -Delete one or all applications from the environment - -*Request* - -+----------------+-----------------------------------------------------------+-----------------------------------+ -| Method | URI | Header | -+================+===========================================================+===================================+ -| DELETE | /environments//services/ | X-Configuration-Session(optional) | -+----------------+-----------------------------------------------------------+-----------------------------------+ - -**Parameters:** - -* `env_id` - environment ID, required -* `app_id` - application ID, optional - -Statistic API -============= - -Statistic API intends to provide billing feature - -Instance environment statistics -------------------------------- - -*Request* - -Get information about all deployed instances in the specified environment - -+----------------+--------------------------------------------------------------+ -| Method | URI | -+================+==============================================================+ -| GET | /environments//instance-statistics/raw/ | -+----------------+--------------------------------------------------------------+ - -**Parameters:** - -* `env_id` - environment ID, required -* `instance_id` - ID of the instance for which need to provide statistic information, optional - -*Response* - - -+----------------------+------------+-----------------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+=================================================================+ -| type | int | Code of the statistic object; 200 - instance, 100 - application | -+----------------------+------------+-----------------------------------------------------------------+ -| type_name | string | Class name of the statistic object | -+----------------------+------------+-----------------------------------------------------------------+ -| instance_id | string | Id of deployed instance | -+----------------------+------------+-----------------------------------------------------------------+ -| active | bool | Instance status | -+----------------------+------------+-----------------------------------------------------------------+ -| type_title | string | User-friendly name for browsing statistic in UI | -+----------------------+------------+-----------------------------------------------------------------+ -| duration | int | Seconds of instance uptime | -+----------------------+------------+-----------------------------------------------------------------+ - -**Content-Type** - application/json - -:: - - [ - { - "type": 200, - "type_name": "io.murano.resources.Instance", - "instance_id": "ef729199-c71e-4a4c-a314-0340e279add8", - "active": true, - "type_title": null, - "duration": 1053, - } - ] - -*Request* - -+----------------+--------------------------------------------------------------+ -| Method | URI | -+================+==============================================================+ -| GET | /environments//instance-statistics/aggregated | -+----------------+--------------------------------------------------------------+ - -*Response* - -+----------------------+------------+-----------------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+=================================================================+ -| type | int | Code of the statistic object; 200 - instance, 100 - application | -+----------------------+------------+-----------------------------------------------------------------+ -| duration | int | Amount uptime of specified type objects | -+----------------------+------------+-----------------------------------------------------------------+ -| count | int | Quantity of specified type objects | -+----------------------+------------+-----------------------------------------------------------------+ - -**Content-Type** - application/json - - :: - - [ - { - "duration": 720, - "count": 2, - "type": 200 - } - ] - -General Request Statistics --------------------------- - -*Request* - -+----------------+---------------+ -| Method | URI | -+================+===============+ -| GET | /stats | -+----------------+---------------+ - -*Response* - -+----------------------+------------+-----------------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+=================================================================+ -| requests_per_tenant | int | Number of incoming requests for user project | -+----------------------+------------+-----------------------------------------------------------------+ -| errors_per_second | int | Class name of the statistic object | -+----------------------+------------+-----------------------------------------------------------------+ -| errors_count | int | Class name of the statistic object | -+----------------------+------------+-----------------------------------------------------------------+ -| requests_per_second | float | Average number of incoming request received in one second | -+----------------------+------------+-----------------------------------------------------------------+ -| requests_count | int | Number of all requests sent to the server | -+----------------------+------------+-----------------------------------------------------------------+ -| cpu_percent | bool | Current cpu usage | -+----------------------+------------+-----------------------------------------------------------------+ -| cpu_count | int | Available cpu power is ``cpu_count * 100%`` | -+----------------------+------------+-----------------------------------------------------------------+ -| host | string | Server host-name | -+----------------------+------------+-----------------------------------------------------------------+ -| average_response_time| float | Average time response waiting, seconds | -+----------------------+------------+-----------------------------------------------------------------+ - -**Content-Type** - application/json - -:: - - [ - { - "updated": "2014-05-15T08:26:17", - "requests_per_tenant": "{\"726ed856965f43cc8e565bc991fa76c3\": 313}", - "created": "2014-04-29T13:23:59", - "cpu_count": 2, - "errors_per_second": 0, - "requests_per_second": 0.0266528, - "cpu_percent": 21.7, - "host": "fervent-VirtualBox", - "error_count": 0, - "request_count": 320, - "id": 1, - "average_response_time": 0.55942 - } - ] - - -Actions API -=========== - -Murano actions are simple MuranoPL methods, that can be called on deployed applications. -Application contains a list with available actions. Actions may return a result. - -Execute an action ------------------ - -Generate task with executing specified action. Input parameters may be provided. - -*Request* - -**Content-Type** - application/json - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| POST | /environments//actions/ | | -+----------------+-----------------------------------------------------------+------------------------------------+ - -**Parameters:** - -* `env_id` - environment ID, required -* `actions_id` - action ID to execute, required - -:: - - "{: value}" - - or - - "{}" in case action has no properties - -*Response* - -Task ID that executes specified action is returned - -**Content-Type** - application/json - -:: - - { - "task_id": "620e883070ad40a3af566d465aa156ef" - } - -GET action result ------------------ - -Request result value after action execution finish. Not all actions have return values. - - -*Request* - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| GET | /environments//actions/ | | -+----------------+-----------------------------------------------------------+------------------------------------+ - -**Parameters:** - -* `env_id` - environment ID, required -* `task_id` - task ID, generated on desired action execution - -*Response* - -Json, describing action result is returned. Result type and value are provided. - -**Content-Type** - application/json - -:: - - { - "isException": false, - "result": ["item1", "item2"] - } - - -Static Actions API -================== - -Static actions are MuranoPL methods that can be called on a MuranoPL class -without deploying actual applications and usually return a result. - -Execute a static action ------------------------ - -Invoke public static method of the specified MuranoPL class. -Input parameters may be provided if method requires them. - -*Request* - -**Content-Type** - application/json - -+----------------+-----------------------------------------------------------+------------------------------------+ -| Method | URI | Header | -+================+===========================================================+====================================+ -| POST | /actions | | -+----------------+-----------------------------------------------------------+------------------------------------+ - -:: - - { - "className": "my.class.fqn", - "methodName": "myMethod", - "packageName": "optional.package.fqn", - "classVersion": "1.2.3", - "parameters": { - "arg1": "value1", - "arg2": "value2" - } - } - -+-----------------+------------+-----------------------------------------------------------------------------+ -| Attribute | Type | Description | -+=================+============+=============================================================================+ -| className | string | Fully qualified name of MuranoPL class with static method | -+-----------------+------------+-----------------------------------------------------------------------------+ -| methodName | string | Name of the method to invoke | -+-----------------+------------+-----------------------------------------------------------------------------+ -| packageName | string | Fully qualified name of a package with the MuranoPL class (optional) | -+-----------------+------------+-----------------------------------------------------------------------------+ -| classVersion | string | Class version specification, "=0" by default | -+-----------------+------------+-----------------------------------------------------------------------------+ -| parameters | object | Key-value pairs of method parameter names and their values, "{}" by default | -+-----------------+------------+-----------------------------------------------------------------------------+ - -*Response* - -JSON-serialized result of the static method execution. - -HTTP codes: - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Action was executed successfully | -+----------------+-----------------------------------------------------------+ -| 400 | Bad request. The format of the body is invalid, method | -| | doesn't match provided arguments, mandatory arguments are | -| | not provided | -+----------------+-----------------------------------------------------------+ -| 403 | User is not allowed to execute the action | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified class, package or method doesn't | -| | exist or method is not exposed | -+----------------+-----------------------------------------------------------+ -| 503 | Unhandled exception in the action | -+----------------+-----------------------------------------------------------+ diff --git a/doc/source/reference/appendix/articles/specification/murano-env-temp.rst b/doc/source/reference/appendix/articles/specification/murano-env-temp.rst deleted file mode 100644 index efc33bccd..000000000 --- a/doc/source/reference/appendix/articles/specification/murano-env-temp.rst +++ /dev/null @@ -1,591 +0,0 @@ -Environment template API -======================== - -Manage environment template definitions in murano. It is possible to create, update, delete, and deploy into OpenStack by translating -it into an environment. In addition, applications can be added to or deleted from the environment template. - -**Environment Template Properties** - -+----------------------+------------+-------------------------------------------+ -| Attribute | Type | Description | -+======================+============+===========================================+ -| id | string | Unique ID | -+----------------------+------------+-------------------------------------------+ -| name | string | User-friendly name | -+----------------------+------------+-------------------------------------------+ -| created | datetime | Creation date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| updated | datetime | Modification date and time in ISO format | -+----------------------+------------+-------------------------------------------+ -| tenant_id | string | OpenStack project | -+----------------------+------------+-------------------------------------------+ -| version | int | Current version | -+----------------------+------------+-------------------------------------------+ -| networking | string | Network settings | -+----------------------+------------+-------------------------------------------+ -| description | string | The environment template specification | -+----------------------+------------+-------------------------------------------+ - -**Common response codes** - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Operation completed successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to perform the operation | -+----------------+-----------------------------------------------------------+ - -Methods for Environment Template API - -List Environments Templates ---------------------------- - -*Request* - -+----------+----------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+==================================+==================================+ -| GET | /templates | Get a list of existing | -| | | environment templates | -+----------+----------------------------------+----------------------------------+ - -*Parameters:* - -* `is_public` - boolean, indicates whether public environment templates are listed or not. - *True* public environments templates from all projects are listed. - *False* private environments templates from current project are listed - *empty* all project templates plus public templates from all projects are listed - -*Response* - -This call returns a list of environment templates. Only the basic properties are -returned. - -:: - - { - "templates": [ - { - "updated": "2014-05-14T13:02:54", - "networking": {}, - "name": "test1", - "created": "2014-05-14T13:02:46", - "tenant_id": "726ed856965f43cc8e565bc991fa76c3", - "version": 0, - "is_public": false, - "id": "2fa5ab704749444bbeafe7991b412c33" - }, - { - "updated": "2014-05-14T13:02:55", - "networking": {}, - "name": "test2", - "created": "2014-05-14T13:02:51", - "tenant_id": "123452452345346345634563456345346", - "version": 0, - "is_public": true, - "id": "744e44812da84e858946f5d817de4f72" - } - ] - } - - -Create environment template ---------------------------- - -+----------------------+------------+---------------------------------------------------------+ -| Attribute | Type | Description | -+======================+============+=========================================================+ -| name | string | Environment template name; only alphanumeric characters | -| | and '-' | | -+----------------------+------------+---------------------------------------------------------+ - -*Request* - -+----------+--------------------------------+--------------------------------------+ -| Method | URI | Description | -+==========+================================+======================================+ -| POST | /templates | Create a new environment template | -+----------+--------------------------------+--------------------------------------+ - -*Content-Type* - application/json - -*Example* - {"name": "env_temp_name"} - -*Response* - -:: - - { - "id": "ce373a477f211e187a55404a662f968", - "name": "env_temp_name", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:44Z", - "tenant_id": "0849006f7ce94961b3aab4e46d6f229a", - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | Operation completed successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to perform the operation | -+----------------+-----------------------------------------------------------+ -| 409 | The environment template already exists | -+----------------+-----------------------------------------------------------+ - - -Get environment templates details ---------------------------------- - -*Request* - -Return information about environment template itself and about applications, including to this -environment template. - -+----------+--------------------------------+-------------------------------------------------+ -| Method | URI | Description | -+==========+================================+=================================================+ -| GET | /templates/{env-temp-id} | Obtains the environment template information | -+----------+--------------------------------+-------------------------------------------------+ - -* `env-temp-id` - environment template ID, required - -*Response* - -*Content-Type* - application/json - -:: - - { - "updated": "2015-01-26T09:12:51", - "networking": - { - }, - "name": "template_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "id": "aa9033ca7ce245fca10e38e1c8c4bbf7", - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Get environment template details successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ - -Delete environment template ---------------------------- - -*Request* - -+----------+-----------------------------------+-----------------------------------+ -| Method | URI | Description | -+==========+===================================+===================================+ -| DELETE | /templates/ | Delete the template id | -+----------+-----------------------------------+-----------------------------------+ - - -*Parameters:* - -* `env-temp_id` - environment template ID, required - -*Response* - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Environment Template deleted successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ - -Adding application to environment template ------------------------------------------- - -*Request* - -+----------+------------------------------------+----------------------------------+ -| Method | URI | Description | -+==========+====================================+==================================+ -| POST | /templates/{env-temp-id}/services | Create a new application | -+----------+------------------------------------+----------------------------------+ - -*Parameters:* - -* `env-temp-id` - The environment-template id, required -* payload - the service description - -*Content-Type* - application/json - -*Example* - -:: - - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - -*Response* - -:: - - - { - "instance": - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": - { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Application added successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ - -Delete application from an environment template ------------------------------------------------ - -*Request* - -+----------+---------------------------------------------+--------------------------------------+ -| Method | URI | Description | -+==========+=============================================+======================================+ -| DELETE | /templates/{env-temp-id}/services/{app-id} | Delete application with Specified id | -+----------+---------------------------------------------+--------------------------------------+ - -*Parameters:* - -* `env-temp-id` - The environment template ID, required -* `app-id` - The application ID, required - -*Content-Type* - application/json - -*Response* - - -:: - - { - "updated": "2015-01-26T09:12:51", - "services": [], - "name": "template_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "id": "aa9033ca7ce245fca10e38e1c8c4bbf7", - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Application deleted successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The application does not exist | -+----------------+-----------------------------------------------------------+ - -Get applications information from an environment template ---------------------------------------------------------- - -*Request* - -+----------+-------------------------------------+-----------------------------------+ -| Method | URI | Description | -+==========+=====================================+===================================+ -| GET | /templates/{env-temp-id}/services | It obtains the service description| -+----------+-------------------------------------+-----------------------------------+ - -*Parameters:* - -* `env-temp-id` - The environment template ID, required - -*Content-Type* - application/json - -*Response* - -:: - - [ - { - "instance": - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": - { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "tomcat", - "?": - { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - }, - { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "password": "XXX", - "name": "mysql", - "?": - { - "type": "io.murano.apps.database.MySQL", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Application information received successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ - -Update applications information from an environment template ------------------------------------------------------------- - -*Request* - -+----------+-----------------------------------------------+-----------------------------------+ -| Method | URI | Description | -+==========+===============================================+===================================+ -| PUT | /templates/{env-temp-id}/services/{service-id}| It updates the service description| -+----------+-----------------------------------------------+-----------------------------------+ - -*Parameters:* - -* `env-temp-id` - The environment template ID, required -* `service-id` - The service ID to be updated -* payload - the service description - -*Content-Type* - application/json - -*Example* - -:: - - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - -*Response* - -:: - - - { - "instance": - { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": - { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - }, - "port": "8080" - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Environment Template updated successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ - -Create an environment from an environment template --------------------------------------------------- - -*Request* - -+----------+--------------------------------------------+--------------------------------------+ -| Method | URI | Description | -+==========+============================================+======================================+ -| POST | /templates/{env-temp-id}/create-environment| Create an environment | -+----------+--------------------------------------------+--------------------------------------+ - - -*Parameters:* - -* `env-temp-id` - The environment template ID, required - -*Payload:* - -* 'environment name': The environment name to be created. - -*Content-Type* - application/json - -*Example* - -:: - - { - "name": "environment_name" - } - -*Response* - -:: - - { - "environment_id": "aa90fadfafca10e38e1c8c4bbf7", - "name": "environment_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "session_id": "adf4dadfaa9033ca7ce245fca10e38e1c8c4bbf7", - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Environment created from template successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ -| 409 | The environment already exists | -+----------------+-----------------------------------------------------------+ - - -**POST /templates/{env-temp-id}/clone** - -*Request* - -+----------+--------------------------------+-------------------------------------------------+ -| Method | URI | Description | -+==========+================================+=================================================+ -| POST | /templates/{env-temp-id}/clone | It clones a public template from one project | -| | | to another | -+----------+--------------------------------+-------------------------------------------------+ - -*Parameters:* - -* `env-temp-id` - environment template ID, required - -*Example Payload* -:: - - { - 'name': 'cloned_env_template_name' - } - -*Content-Type* - application/json - -*Response* - -:: - - { - "updated": "2015-01-26T09:12:51", - "name": "cloned_env_template_name", - "created": "2015-01-26T09:12:51", - "tenant_id": "00000000000000000000000000000001", - "version": 0, - "is_public": False, - "id": "aa9033ca7ce245fca10e38e1c8c4bbf7", - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Environment Template cloned successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 403 | User has no access to these resources | -+----------------+-----------------------------------------------------------+ -| 404 | The environment template does not exist | -+----------------+-----------------------------------------------------------+ -| 409 | Conflict. The environment template name already exists | -+----------------+-----------------------------------------------------------+ - diff --git a/doc/source/reference/appendix/articles/specification/murano-repository.rst b/doc/source/reference/appendix/articles/specification/murano-repository.rst deleted file mode 100644 index 22481b3a6..000000000 --- a/doc/source/reference/appendix/articles/specification/murano-repository.rst +++ /dev/null @@ -1,606 +0,0 @@ -Application catalog API -======================= - -Manage application definitions in the Application Catalog. -You can browse, edit and upload new application packages (.zip.package archive with all data that required for a service deployment). - -Packages -======== - -Methods for application package management - -**Package Properties** - -- ``id``: guid of a package (``fully_qualified_name`` can also be used for some API functions) -- ``fully_qualified_name``: fully qualified domain name - domain name that specifies exact application location -- ``name``: user-friendly name -- ``type``: package type, "library" or "application" -- ``description``: text information about application -- ``author``: name of application author -- ``tags``: list of short names, connected with the package, which allows to search applications easily -- ``categories``: list of application categories -- ``class_definition``: list of class names used by a package -- ``is_public``: determines whether the package is shared for other projects -- ``enabled``: determines whether the package is browsed in the Application Catalog -- ``owner_id``: id of a project that owns the package - -.. note:: - - It is possible to use ``in`` operator for properties ``id``, ``category`` and ``tag``. - For example to get packages with ``id1, id2, id3`` use ``id=in:id1,id2,id3``. - -List packages -------------- - -`/v1/catalog/packages?{marker}{limit}{order_by}{type}{category}{fqn}{owned}{id}{catalog}{class_name}{name} [GET]` - -This is the compound request to list and search through application catalog. -If there are no search parameters all packages that is_public, enabled and belong to the user's project will be listed. -Default order is by 'created' field. -For an admin role all packages are available. - -**Parameters** - -+---------------------+--------+---------------------------------------------+ -| Attribute | Type | Description | -+=====================+========+=============================================+ -| ``catalog`` | bool | If false (default) - search packages, that | -| | | current user can edit (own for non-admin, | -| | | all for admin) | -| | | If true - search packages, that current user| -| | | can deploy (i.e. his own + public) | -+---------------------+--------+---------------------------------------------+ -| ``marker`` | string | A package identifier marker may be | -| | | specified. When present only packages which | -| | | occur after the identifier ID will be listed| -+---------------------+--------+---------------------------------------------+ -| ``limit`` | string | When present the maximum number of results | -| | | returned will not exceed the specified | -| | | value. The typical pattern of limit and | -| | | marker is to make an initial limited request| -| | | and then to use the ID of the last package | -| | | from the response as the marker parameter in| -| | | a subsequent limited request. | -+---------------------+--------+---------------------------------------------+ -| ``order_by`` | string | Allows to sort packages by: `fqn`, `name`, | -| | | `created`. Created is default value. | -+---------------------+--------+---------------------------------------------+ -| ``type`` | string | Allows to point a type of package: | -| | | `application`, `library` | -+---------------------+--------+---------------------------------------------+ -| ``category`` | string | Allows to point a categories for a search | -+---------------------+--------+---------------------------------------------+ -| ``fqn`` | string | Allows to point a fully qualified package | -| | | name for a search | -+---------------------+--------+---------------------------------------------+ -| ``owned`` | bool | Search only from packages owned by current | -| | | project | -+---------------------+--------+---------------------------------------------+ -| ``id`` | string | Allows to point an id for a search | -+---------------------+--------+---------------------------------------------+ -| ``include_disabled``| bool | Include disabled packages in a the result | -+---------------------+--------+---------------------------------------------+ -| ``search`` | string | Gives opportunity to search specified data | -| | | by all the package parameters and order | -| | | packages | -+---------------------+--------+---------------------------------------------+ -| ``class_name`` | string | Search only for packages, that use specified| -| | | class | -+---------------------+--------+---------------------------------------------+ -| ``name`` | string | Allows to point a package name for a search | -+---------------------+--------+---------------------------------------------+ - -**Response 200 (application/json)** - -:: - - {"packages": [ - { - "id": "fed57567c9fa42c192dcbe0566f8ea33", - "fully_qualified_name" : "com.example.murano.services.linux.telnet", - "is_public": false, - "name": "Telnet", - "type": "linux", - "description": "Installs Telnet service", - "author": "OpenStack, Inc.", - "created": "2014-04-02T14:31:55", - "enabled": true, - "tags": ["linux", "telnet"], - "categories": ["Utility"], - "owner_id": "fed57567c9fa42c192dcbe0566f8ea40" - }, - { - "id": "fed57567c9fa42c192dcbe0566f8ea31", - "fully_qualified_name": "com.example.murano.services.windows.WebServer", - "is_public": true, - "name": "Internet Information Services", - "type": "windows", - "description": "The Internet Information Service sets up an IIS server and joins it into an existing domain", - "author": "OpenStack, Inc.", - "created": "2014-04-02T14:31:55", - "enabled": true, - "tags": ["windows", "web"], - "categories": ["Web"], - "owner_id": "fed57567c9fa42c192dcbe0566f8ea40" - }] - } - - - -Upload a new package[POST] --------------------------- - -`/v1/catalog/packages` - -See the example of multipart/form-data request, It should contain two parts - text (JSON string) and file object - -**Request (multipart/form-data)** - -.. code-block:: none - - Content-type: multipart/form-data, boundary=AaB03x - Content-Length: $requestlen - - --AaB03x - content-disposition: form-data; name="submit-name" - - --AaB03x - Content-Disposition: form-data; name="JsonString" - Content-Type: application/json - - {"categories":["web"] , "tags": ["windows"], "is_public": false, "enabled": false} - `categories` - array, required - `tags` - array, optional - `name` - string, optional - `description` - string, optional - `is_public` - bool, optional - `enabled` - bool, optional - - --AaB03x - content-disposition: file; name="file"; filename="test.tar" - Content-Type: targz - Content-Transfer-Encoding: binary - - $binarydata - --AaB03x-- - - -**Response 200 (application/json)** - -.. code-block:: json - - { - "updated": "2014-04-03T13:00:13", - "description": "A domain service hosted in Windows environment by using Active Directory Role", - "tags": ["windows"], - "is_public": true, - "id": "8f4f09bd6bcb47fb968afd29aacc0dc9", - "categories": ["test1"], - "name": "Active Directory", - "author": "Mirantis, Inc", - "created": "2014-04-03T13:00:13", - "enabled": true, - "class_definition": [ - "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "com.mirantis.murano.windows.activeDirectory.SecondaryController", - "com.mirantis.murano.windows.activeDirectory.Controller", - "com.mirantis.murano.windows.activeDirectory.PrimaryController" - ], - "fully_qualified_name": "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "type": "Application", - "owner_id": "fed57567c9fa42c192dcbe0566f8ea40" - } - -Get package details -------------------- - -`/v1/catalog/packages/{id} [GET]` - -Display details for a package. - -**Parameters** - -``id`` (required) Hexadecimal `id` (or fully qualified name) of the package - -**Response 200 (application/json)** - -:: - - { - "updated": "2014-04-03T13:00:13", - "description": "A domain service hosted in Windows environment by using Active Directory Role", - "tags": ["windows"], - "is_public": true, - "id": "8f4f09bd6bcb47fb968afd29aacc0dc9", - "categories": ["test1"], - "name": "Active Directory", - "author": "Mirantis, Inc", - "created": "2014-04-03T13:00:13", - "enabled": true, - "class_definition": [ - "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "com.mirantis.murano.windows.activeDirectory.SecondaryController", - "com.mirantis.murano.windows.activeDirectory.Controller", - "com.mirantis.murano.windows.activeDirectory.PrimaryController" - ], - "fully_qualified_name": "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "type": "Application", - "owner_id": "fed57567c9fa42c192dcbe0566f8ea40" - } - -**Response 403** - -* In attempt to get a non-public package by a user whose project is not an owner of this package. - -**Response 404** - -* In case the specified package id doesn't exist. - -Update a package -================ - -`/v1/catalog/packages/{id} [PATCH]` - -Allows to edit mutable fields (categories, tags, name, description, is_public, enabled). -See the full specification `here `_. - -**Parameters** - -``id`` (required) Hexadecimal `id` (or fully qualified name) of the package - -**Content type** - -application/murano-packages-json-patch - -Allowed operations: - -:: - - [ - { "op": "add", "path": "/tags", "value": [ "foo", "bar" ] }, - { "op": "add", "path": "/categories", "value": [ "foo", "bar" ] }, - { "op": "remove", "path": "/tags", ["foo"] }, - { "op": "remove", "path": "/categories", ["foo"] }, - { "op": "replace", "path": "/tags", "value": [] }, - { "op": "replace", "path": "/categories", "value": ["bar"] }, - { "op": "replace", "path": "/is_public", "value": true }, - { "op": "replace", "path": "/enabled", "value": true }, - { "op": "replace", "path": "/description", "value":"New description" }, - { "op": "replace", "path": "/name", "value": "New name" } - ] - -**Request 200 (application/murano-packages-json-patch)** - -:: - - [ - { "op": "add", "path": "/tags", "value": [ "windows", "directory"] }, - { "op": "add", "path": "/categories", "value": [ "Directory" ] } - ] - -**Response 200 (application/json)** - -:: - - { - "updated": "2014-04-03T13:00:13", - "description": "A domain service hosted in Windows environment by using Active Directory Role", - "tags": ["windows", "directory"], - "is_public": true, - "id": "8f4f09bd6bcb47fb968afd29aacc0dc9", - "categories": ["test1"], - "name": "Active Directory", - "author": "Mirantis, Inc", - "created": "2014-04-03T13:00:13", - "enabled": true, - "class_definition": [ - "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "com.mirantis.murano.windows.activeDirectory.SecondaryController", - "com.mirantis.murano.windows.activeDirectory.Controller", - "com.mirantis.murano.windows.activeDirectory.PrimaryController" - ], - "fully_qualified_name": "com.mirantis.murano.windows.activeDirectory.ActiveDirectory", - "type": "Application", - "owner_id": "fed57567c9fa42c192dcbe0566f8ea40" - } - -**Response 403** - -* An attempt to update immutable fields -* An attempt to perform operation that is not allowed on the specified path -* An attempt to update non-public package by user whose project is not an owner of this package - -**Response 404** - -* An attempt to update package that doesn't exist - - -Delete application definition from the catalog ----------------------------------------------- - -`/v1/catalog/packages/{id} [DELETE]` - -**Parameters** - -* ``id`` (required) Hexadecimal `id` (or fully qualified name) of the package to delete - -**Response 404** - -* An attempt to delete package that doesn't exist - - -Get application package ------------------------ - -`/v1/catalog/packages/{id}/download [GET]` - -Get application definition package - -**Parameters** - -* ``id`` (required) Hexadecimal `id` (or fully qualified name) of the package - -**Response 200 (application/octet-stream)** - -The sequence of bytes representing package content - -**Response 404** - -Specified package id doesn't exist - - -Get UI definition ------------------ - -`/v1/catalog/packages/{id}/ui [GET]` - -Retrieve UI definition for an application which described in a package with provided id - -**Parameters** - -* ``id`` (required) Hexadecimal `id` (or fully qualified name) of the package - -**Response 200 (application/octet-stream)** - -The sequence of bytes representing UI definition - -**Response 404** - -Specified package id doesn't exist - -**Response 403** - -Specified package is not public and not owned by user project, performing the request - -**Response 404** - -* Specified package id doesn't exist - - -Get logo --------- - -Retrieve application logo which described in a package with provided id - -`/v1/catalog/packages/{id}/logo [GET]` - -**Parameters** - -``id`` (required) Hexadecimal `id` (or fully qualified name) of the package - -**Response 200 (application/octet-stream)** - -The sequence of bytes representing application logo - -**Response 403** - -Specified package is not public and not owned by user project, -performing the request - -**Response 404** - -Specified package is not public and not owned by user project, -performing the request - -Categories -========== - -Provides category management. Categories are used in the Application Catalog -to group application for easy browsing and search. - -List categories ---------------- - -* `/v1/catalog/packages/categories [GET]` - - !DEPRECATED (Plan to remove in L release) Retrieve list of all available application categories - - **Response 200 (application/json)** - - A list, containing category names - - *Content-Type* - application/json - - :: - - { - "categories": ["Web service", "Directory", "Database", "Storage"] - } - - -* `/v1/catalog/categories [GET]` - - +----------+------------------------------+---------------------------------+ - | Method | URI | Description | - +==========+==============================+=================================+ - | GET | /catalog/categories | Get list of existing categories | - +----------+------------------------------+---------------------------------+ - - Retrieve list of all available application categories - - **Response 200 (application/json)** - - A list, containing detailed information about each category - - *Content-Type* - application/json - - :: - - {"categories": [ - { - "id": "0420045dce7445fabae7e5e61fff9e2f", - "updated": "2014-12-26T13:57:04", - "name": "Web", - "created": "2014-12-26T13:57:04", - "package_count": 1 - }, - { - "id": "3dd486b1e26f40ac8f35416b63f52042", - "updated": "2014-12-26T13:57:04", - "name": "Databases", - "created": "2014-12-26T13:57:04", - "package_count": 0 - }] - } - - - -Get category details --------------------- - -`/catalog/categories/ [GET]` - - Return detailed information for a provided category - -*Request* - -+----------+-----------------------------------+-----------------------------+ -| Method | URI | Description | -+==========+===================================+=============================+ -| GET | /catalog/categories/ | Get category detail | -+----------+-----------------------------------+-----------------------------+ - -*Parameters* - -* ``category_id`` - required, category ID, required - -*Response* - - *Content-Type* - application/json - -:: - - { - "id": "b308f7fa8a2f4a5eb419970c827f4466", - "updated": "2015-01-28T17:00:19", - "packages": [ - { - "fully_qualified_name": "io.murano.apps.ZabbixServer", - "id": "4dfb566e69e6445fbd4aea5099fe95e9", - "name": "Zabbix Server" - } - ], - "name": "Web", - "created": "2015-01-28T17:00:19", - "package_count": 1 - } - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Category deleted successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified category doesn`t exist | -+----------------+-----------------------------------------------------------+ - -Add new category ----------------- - -`/catalog/categories [POST]` - - Add new category to the Application Catalog - -*Parameters* - -+----------------------+------------+----------------------------------------+ -| Attribute | Type | Description | -+======================+============+========================================+ -| name | string | Environment name; only alphanumeric | -| | | characters and '-' | -+----------------------+------------+----------------------------------------+ - -*Request* - -+----------+----------------------------------+------------------------------+ -| Method | URI | Description | -+==========+==================================+==============================+ -| POST | /catalog/categories | Create new category | -+----------+----------------------------------+------------------------------+ - - *Content-Type* - application/json - - *Example* - {"name": "category_name"} - -*Response* - -:: - - { - "id": "ce373a477f211e187a55404a662f968", - "name": "category_name", - "created": "2013-11-30T03:23:42Z", - "updated": "2013-11-30T03:23:44Z", - "package_count": 0 - } - - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Category created successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 409 | Conflict. Category with specified name already exist | -+----------------+-----------------------------------------------------------+ - - -Delete category ---------------- - -`/catalog/categories [DELETE]` - -*Request* - -+----------+-----------------------------------+-----------------------------+ -| Method | URI | Description | -+==========+===================================+=============================+ -| DELETE | /catalog/categories/ | Delete category with | -| | | specified ID | -+----------+-----------------------------------+-----------------------------+ - -*Parameters:* - -* ``category_id`` - required, category ID, required - -*Response* - -+----------------+-----------------------------------------------------------+ -| Code | Description | -+================+===========================================================+ -| 200 | OK. Category deleted successfully | -+----------------+-----------------------------------------------------------+ -| 401 | User is not authorized to access this session | -+----------------+-----------------------------------------------------------+ -| 404 | Not found. Specified category doesn`t exist | -+----------------+-----------------------------------------------------------+ -| 403 | Forbidden. Category with specified name is assigned to | -| | the package, presented in the catalog | -+----------------+-----------------------------------------------------------+ diff --git a/doc/source/reference/appendix/articles/specification/overview.rst b/doc/source/reference/appendix/articles/specification/overview.rst deleted file mode 100644 index 527774de0..000000000 --- a/doc/source/reference/appendix/articles/specification/overview.rst +++ /dev/null @@ -1,41 +0,0 @@ -General information -=================== - -* **Introduction** - - The murano service API is a programmatic interface used for interaction with - murano. Other interaction mechanisms like the murano dashboard or the murano CLI - should use the API as an underlying protocol for interaction. - -* **Allowed HTTPs requests** - - * *POST* : To create a resource - * *GET* : Get a resource or list of resources - * *DELETE* : To delete resource - * *PATCH* : To update a resource - -* **Description Of Usual Server Responses** - - * 200 ``OK`` - the request was successful. - * 201 ``Created`` - the request was successful and a resource was created. - * 204 ``No Content`` - the request was successful but there is no representation to return (i.e. the response is empty). - * 400 ``Bad Request`` - the request could not be understood or required parameters were missing. - * 401 ``Unauthorized`` - authentication failed or user didn't have permissions for requested operation. - * 403 ``Forbidden`` - access denied. - * 404 ``Not Found`` - resource was not found - * 405 ``Method Not Allowed`` - requested method is not supported for resource. - * 406 ``Not Acceptable`` - the requested resource is only capable of generating content not acceptable - according to the Accept headers sent in the request. - * 409 ``Conflict`` - requested method resulted in a conflict with the current state of the resource. - -* **Response of POSTs and PUTs** - - All POST and PUT requests by convention should return the created object - (in the case of POST, with a generated ID) as if it was requested by - GET. - -* **Authentication** - - All requests include a keystone authentication token header - (X-Auth-Token). Clients must authenticate with keystone before - interacting with the murano service. \ No newline at end of file diff --git a/doc/source/reference/appendix/articles/telnet_example.rst b/doc/source/reference/appendix/articles/telnet_example.rst deleted file mode 100644 index 22a8dedf2..000000000 --- a/doc/source/reference/appendix/articles/telnet_example.rst +++ /dev/null @@ -1,48 +0,0 @@ -:orphan: - -.. _telnet_example: - -Telnet Example --------------- - -.. code-block:: yaml - - Namespaces: - =: io.murano.apps.linux - std: io.murano - res: io.murano.resources - - - Name: Telnet - - # Inheritance from io.murano.Application class - # (located at Murano Core library) indicates, - # that this is a complete application - # and that 'deploy' method has to be defined. - Extends: std:Application - - Properties: - name: - Contract: $.string().notNull() - - instance: - Contract: $.class(res:Instance).notNull() - - - Methods: - deploy: - Body: - # Determine the environment to which the application belongs. - # This message will be stored in deployment logs and available in UI - - $this.find(std:Environment).reporter.report($this, 'Creating VM for Telnet Instance.') - # Deploy VM - - $.instance.deploy() - - $this.find(std:Environment).reporter.report($this, 'Instance is created. Setup Telnet service.') - # Create instance of murano resource class. Agent will use it to find - # corresponding execution plan by the file name - - $resources: new('io.murano.system.Resources') - # Deploy Telnet - - $template: $resources.yaml('DeployTelnet.template') - # Send prepared execution plan to Murano agent - - $.instance.agent.call($template, $resources) - - $this.find(std:Environment).reporter.report($this, 'Telnet service setup is done.') diff --git a/doc/source/reference/appendix/articles/test_docs.rst b/doc/source/reference/appendix/articles/test_docs.rst deleted file mode 100644 index b06d1d34e..000000000 --- a/doc/source/reference/appendix/articles/test_docs.rst +++ /dev/null @@ -1,237 +0,0 @@ -.. _test_docs: - -================================== -Murano automated tests description -================================== - -This page describes automated tests for a Murano project: - -* where tests are located -* how they are run -* how to execute tests on a local machine -* how to find the root of problems with FAILed tests - -Murano continuous integration service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano project has separate CI server, which runs tests for all commits and -verifies that new code does not break anything. - -Murano CI uses OpenStack QA cloud for testing infrastructure. - -Murano CI url: https://murano-ci.mirantis.com/jenkins/ Anyone can login -to that server, using launchpad credentials. - -There you can find each job for each repository: one for **murano** and -another one for **murano-dashboard**. - -* ``gate-murano-dashboard-ubuntu\*`` verifies each commit to - the murano-dashboard repository -* ``gate-murano-ubuntu\*`` verifies each commit to the murano repository - -Other jobs allow one to build and test Murano documentation and to perform other -useful work to support the Murano CI infrastructure. -All jobs are run following a fresh installation of the operating system and all components -are installed on each run. - -UI tests -~~~~~~~~ - -The Murano project has a web user interface and all possible user scenarios -should be tested. -All UI tests are located at -``https://opendev.org/openstack/murano-dashboard/src/branch/master/muranodashboard/tests/functional``. - -Automated tests for the Murano web UI are written in Python using the special -Selenium library. This library is used to automate web browser interactions -with Python. See official `Selenium documentation `_ -for details. - -Prerequisites: --------------- - -* Install the Python module called nose using either the - :command:`easy_install nose` or :command:`pip install nose` command. - This will install the nose libraries, as well as the ``nosetests`` script, - which you can use to automatically discover and run tests. -* Install external Python libraries, which are required for the Murano web UI - tests: ``testtools`` and ``selenium``. -* Verify that you have one of the following web browsers installed: - - * Mozilla Firefox 46.0 - - .. note:: - - If you do not have Firefox package out of the box, - install and remove it. Otherwise, you will need to install - dependent libraries manually. To downgrade Firefox: - - .. code-block:: console - - apt-get remove firefox - wget https://ftp.mozilla.org/pub/firefox/releases/46.0/linux-x86_64/en-US/firefox-46.0.tar.bz2 - tar -xjf firefox-46.0.tar.bz2 - rm -rf /opt/firefox - mv firefox /opt/firefox46 - ln -s /opt/firefox46/firefox /usr/bin/firefox - - * Google Chrome - -* To run the tests on a remote server, configure the remote X server. - Use VNC Software to see the test results in real-time. - - #. Specify the display environment variable: - - .. code-block:: console - - $DISPLAY=: - - #. Configure remote X server and VNC Software by typing: - - .. code-block:: console - - apt-get install xvfb xfonts-100dpi xfonts-75dpi xfonts-cyrillic xorg dbus-x11 - "Xvfb -fp "/usr/share/fonts/X11/misc/" :$DISPLAY -screen 0 "1280x1024x16" &" - apt-get install --yes x11vnc - x11vnc -bg -forever -nopw -display :$DISPLAY -ncache 10 - sudo iptables -I INPUT 1 -p tcp --dport 5900 -j ACCEPT - -Download and run tests ----------------------- - -To download and run the tests: - -#. Verify that all additional components has been installed. - -#. Clone the ``murano-dashboard`` git repository: - - .. code-block:: console - - git clone https://opendev.org/openstack/murano-dashboard - -#. Change the default settings: - - #. Specify the Murano Repository URL variable for Horizon local settings - in ``murano_dashboard/muranodashboard/local/local_settings.d/_50_murano.py``: - - .. code-block:: console - - MURANO_REPO_URL = 'http://localhost:8099' - - #. Copy ``muranodashboard/tests/functional/config/config.conf.sample`` to - ``config.conf``. - - #. Set appropriate URLs and credentials for your OpenStack lab. - Only Administrator user credentials are appropriate. - - .. code-block:: console - - [murano] - - horizon_url = http://localhost/dashboard - murano_url = http://localhost:8082 - user = *** - password = *** - tenant = *** - keystone_url = http://localhost:5000/v3 - -All tests are kept in ``sanity_check.py`` and divided into 10 test suites: - -* TestSuiteSmoke - verification of Murano panels; checks that they can be open - without errors. -* TestSuiteEnvironment - verification of all operations with environment are - finished successfully. -* TestSuiteImage - verification of operations with images. -* TestSuiteFields - verification of custom fields validators. -* TestSuitePackages - verification of operations with Murano packages. -* TestSuiteApplications - verification of Application Catalog page and of - application creation process. -* TestSuiteAppsPagination - verification of apps pagination in case of many - applications installed. -* TestSuiteRepository - verification of importing packages and bundles. -* TestSuitePackageCategory - verification of main operations with categories. -* TestSuiteCategoriesPagination - verification of categories pagination - in case of many categories created. -* TestSuiteMultipleEnvironments - verification of ability to apply action - to multiple environments. - -To run the tests follow these instructions: - -* To run all tests: - -.. code-block:: console - - nosetests sanity_check.py - -* To run a single suite: - -.. code-block:: console - - nosetests sanity_check.py: - -* To run a single test: - -.. code-block:: console - - nosetests sanity_check.py:. - - -In case of successful execution, you should see something like this: - -.. code-block:: console - - ......................... - Ran 34 tests in 1.440s - OK - -In case of failure, the folder with screenshots of the last operation of -tests that finished with errors would be created. -It is located in ``muranodashboard/tests/functional`` folder. - -There are also a number of command line options that can be used to control -the test execution and generated outputs. For more details about ``nosetests``, -type: - -.. code-block:: console - - nosetests -h - - -Tempest tests -~~~~~~~~~~~~~ - -All Murano services have tempest-based automated tests, which verify -API interfaces and deployment scenarios. -Tempest tests for Murano are located at ``https://opendev.org/openstack/murano/src/branch/master/murano/tests/functional``. - -The following Python files contain basic test suites for different Murano components. - -API tests ---------- - -Murano API tests are run on the devstack gate located at -``https://opendev.org/openstack/murano-tempest-plugin/src/branch/master/murano_tempest_tests/tests/api``. - -* ``test_murano_envs.py`` contains test suite with actions on murano - environments (create, delete, get, and others). -* ``test_murano_sessions.py`` contains test suite with actions on murano - sessions (create, delete, get, and others). -* ``test_murano_services.py`` contains test suite with actions on murano - services (create, delete, get, and others). -* ``test_murano_repository.py`` contains test suite with actions on murano - package repository. - -Engine tests ------------- - -Murano Engine Tests are run on murano-ci at ``https://opendev.org/openstack/murano-tempest-plugin/src/branch/master/murano_tempest_tests/tests/functional``: - -* ``base.py`` contains base test class and tests with actions on deploy - Murano services such as Telnet and Apache. - -Command-line interface tests ----------------------------- - -Murano CLI tests are currently in the middle of creation. The current scope -is read-only operations on a cloud that are hard to test through unit tests. -All tests have description and execution steps in their docstrings. diff --git a/doc/source/reference/appendix/articles/workflow.rst b/doc/source/reference/appendix/articles/workflow.rst deleted file mode 100644 index 3ba569160..000000000 --- a/doc/source/reference/appendix/articles/workflow.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. _murano-workflow: - -=============== -Murano workflow -=============== -What happens when a component is being created in an environment? This document -will use the Telnet package referenced elsewhere as an example. It assumes the -package has been previously uploaded to Murano. - - -Step 1. Begin deployment -========================= -The API sends a message that instructs murano-engine, the workflow component of -Murano, to deploy an environment. The message consists of a JSON document -containing the class types required to create the environment, as well as any -parameters the user selected prior to deployment. Examples are: - -* An :ref:`Environment` object (io.murano.Environment) with a *name* -* An object (or objects) referring to networks that need to be created - or that already exist -* A list of Applications (e.g. io.murano.apps.linux.Telnet). Each Application - will contain, or will reference, anything it requires. The Telnet example, - has a property called *instance* whose contract states it must be of type - io.murano.resources.Instance. In turn the Instance has properties it requires - (like a name, a flavor, a keypair name). - -Each object in this *model* has an ID so that the state of each can be tracked. - -The classes that are required are determined by the application's manifest. In -the :ref:`Telnet example ` only one class is explicitly -required; the telnet application definition. - -The :ref:`Telnet class definition ` refers to several other -classes. It extends :ref:`Application` and it requires an :ref:`Instance`. -It also refers to the :ref:`Environment` in which it will be contained, -sends reports through the environment's :ref:`status-reporter` -and adds security group rules to the :ref:`security-group-manager`. - - -Step 2. Load definitions -========================= -The engine makes a series of requests to the API to download packages it -needs. These requests pass the class names the environment will require, and -during this stage the engine will validate that all the required classes exist -and are accessible, and will begin creating them. All Classes whose *workflow* -sections contain an *initialize* fragment are then initialized. A typical initialization -order would be (defined by the ordering in the *model* sent to the murano-engine): - -* :ref:`Network` -* :ref:`Instance` -* :ref:`Object` -* :ref:`Environment` - - -Step 3. Deploy resources -========================== -The workflow defined in Environment.deploy is now executed. The first step -typically is to initialize the messaging component that will pay attention -to murano-agent (see later). The next stage is to deploy each application the -environment knows about in turn, by running deploy() for each application. -This happens concurrently for all the applications belonging to an instance. - -In the :ref:`Telnet example ` (under *Workflow*), the workflow -dictates sending a status message (via the environment's *reporter*, and -configuring some security group rules. It is at this stage that the engine -first contacts Heat to request information about any pre-existing resources -(and there will be none for a fresh deploy) before updating the new Heat -template with the security group information. - -Next it instructs the engine to deploy the *instance* it relies on. A large -part of the interaction with Heat is carried out at this stage; the first -thing an Instance does is add itself to the environment's network. Since the -network doesn't yet exist, murano-engine runs the neutron network workflow -which pushes template fragments to Heat. These fragments can define: -* Networks -* Subnets -* Router interfaces - -Once this is done the Instance itself constructs a Heat template fragment and -again pushes it to Heat. The Instance will include a *userdata* script that -is run when the instance has started up, and which will configure and run -murano-agent. - - -Step 4. Software configuration via murano-agent -================================================ -If the workflow includes murano-agent components (and the telnet example does), -typically the application workflow will execute them as the next step. - -In the telnet example, the workflow instructs the engine to load -*DeployTelnet.yaml* as YAML, and pass it to the murano-agent running on the -configured instance. This causes the agent to execute the *EntryPoint* defined -in the agent script (which in this case deploys some packages and sets some -iptables rules). - - -Step 5. Done -============= -After execution is finished, the engine sends a last message indicating that -fact; the API receives it and marks the environment as deployed. diff --git a/doc/source/reference/appendix/cli_ref.rst b/doc/source/reference/appendix/cli_ref.rst deleted file mode 100644 index b5cc7622b..000000000 --- a/doc/source/reference/appendix/cli_ref.rst +++ /dev/null @@ -1,624 +0,0 @@ -.. _cli-ref: - -========================== -Murano command-line client -========================== - -The ``murano`` client is the command-line -interface (CLI) for the Application catalog API and its extensions. - -For help on a specific ``murano`` command, enter: - -.. code-block:: console - - murano help COMMAND - - murano usage - usage: murano \[--version] \[-d] \[-v] \[-k] \[--os-cacert ] - \[--cert-file CERT_FILE] \[--key-file KEY_FILE] - \[--ca-file CA_FILE] \[--api-timeout API_TIMEOUT] - \[--os-username OS_USERNAME] \[--os-password OS_PASSWORD] - \[--os-tenant-id OS_TENANT_ID] \[--os-tenant-name OS_TENANT_NAME] - \[--os-auth-url OS_AUTH_URL] \[--os-region-name OS_REGION_NAME] - \[--os-auth-token OS_AUTH_TOKEN] \[--os-no-client-auth] - \[--murano-url MURANO_URL] \[--glance-url GLANCE_URL] - \[--murano-api-version MURANO_API_VERSION] - \[--os-service-type OS_SERVICE_TYPE] - \[--os-endpoint-type OS_ENDPOINT_TYPE] \[--include-password] - \[--murano-repo-url MURANO_REPO_URL] - ... - -Subcommands -=========== - -* *bundle-import* Import a bundle. - -* *category-create* Create a category. - -* *category-delete* Delete a category. - -* *category-list* List all available categories. - -* *category-show* - -* *deployment-list* List deployments for an environment. - -* *env-template-add-app* Add application to the environment template. - -* *env-template-create* Create an environment template. - -* *env-template-del-app* Delete application to the environment template. - -* *env-template-delete* Delete an environment template. - -* *env-template-list* List the environments templates. - -* *env-template-show* Display environment template details. - -* *env-template-update* Update an environment template. - -* *environment-create* Create an environment. - -* *environment-delete* Delete an environment. - -* *environment-list* List the environments. - -* *environment-rename* Rename an environment. - -* *environment-show* Display environment details. - -* *package-create* Create an application package. - -* *package-delete* Delete a package. - -* *package-download* Download a package to a filename or stdout. - -* *package-import* Import a package. - -* *package-list* List available packages. - -* *package-show* Display details for a package. - -* *service-show* - -* *bash-completion* Prints all of the commands and options to stdout. - -* *help* Display help about this program or one of its subcommands. - -Murano optional arguments -========================= - -**--version** - show program's version number and exit - -**-d, --debug** - Defaults to env[MURANOCLIENT_DEBUG] - -**-v, --verbose** - Print more verbose output - -**-k, --insecure** - Explicitly allow muranoclient to perform "insecure" SSL (https) requests. - The server's certificate will not be verified against any certificate - authorities. This option should be used with caution. - -**--os-cacert ** - Specify a CA bundle file to use in verifying a TLS (https) server - certificate. Defaults to env[OS_CACERT] - -**--cert-file CERT_FILE** - Path of certificate file to use in SSL connection. This file can optionally - be prepended with the private key. - -**--key-file KEY_FILE** - Path of client key to use in SSL connection. This option is not necessary - if your key is prepended to your cert file. - -**--ca-file CA_FILE** - Path of CA SSL certificate(s) used to verify the remote server certificate. - Without this option glance looks for the default system CA certificates. - -**--api-timeout API_TIMEOUT** - Number of seconds to wait for an API response, defaults to system socket - timeout - -**--os-username OS_USERNAME** - Defaults to env[OS_USERNAME] - -**--os-password OS_PASSWORD** - Defaults to env[OS_PASSWORD] - -**--os-project-id OS_PROJECT_ID** - Defaults to env[OS_PROJECT_ID] - -**--os-project-name OS_PROJECT_NAME** - Defaults to env[OS_PROJECT_NAME] - -**--os-auth-url OS_AUTH_URL** - Defaults to env[OS_AUTH_URL] - -**--os-region-name OS_REGION_NAME** - Defaults to env[OS_REGION_NAME] - -**--os-auth-token OS_AUTH_TOKEN** - Defaults to env[OS_AUTH_TOKEN] - -**--os-no-client-auth** - Do not contact keystone for a token. Defaults to env[OS_NO_CLIENT_AUTH]. - -**--murano-url MURANO_URL** - Defaults to env[MURANO_URL]** - -**--glance-url GLANCE_URL** - Defaults to env[GLANCE_URL] - -**--murano-api-version MURANO_API_VERSION** - Defaults to env[MURANO_API_VERSION] or 1 - -**--os-service-type OS_SERVICE_TYPE** - Defaults to env[OS_SERVICE_TYPE] - -**--os-endpoint-type OS_ENDPOINT_TYPE** - Defaults to env[OS_ENDPOINT_TYPE] - -**--include-password** - Send os-username and os-password to murano. - -**--murano-repo-url MURANO_REPO_URL** - Defaults to env[MURANO_REPO_URL] or - `http://storage.apps.openstack.org_` - -Application catalog API v1 commands -=================================== - -murano bundle-import -~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano bundle-import \[--is-public] \[--exists-action {a,s,u}] - \[ ...] - -Import a bundle. ``FILE`` can be either a path to a zip file, URL or name from -repo. if ``FILE`` is a local file does not attempt to parse requirements and -treat Names of packages in a bundle as file names, relative to location of -bundle file. - -Positional arguments --------------------- - -**** - Bundle URL, bundle name, or path to the bundle file - -Optional arguments ------------------- - -**--is-public** - Make packages available to users from other project - -**--exists-action {a,s,u}** - Default action when a package already exists - -murano category-create -~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano category-create - -Create a category. - -Positional arguments --------------------- - -**** - Category name - -murano category-delete -~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano category-delete \[ ...] - -Delete a category. - -Positional arguments --------------------- - -**** - ID of a category(s) to delete - -murano category-list -~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano category-list - -List all available categories. - -murano category-show -~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano category-show - -Positional arguments --------------------- - -**** - ID of a category(s) to show - -murano deployment-list -~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano deployment-list - -List deployments for an environment. - -Positional arguments --------------------- - -**** - Environment ID for which to list deployments - -murano env-template-add-app -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-add-app - -Add application to the environment template. - -Positional arguments --------------------- - -**** - Environment template name - -**** - Path to the template. - -murano env-template-create -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-create - -Create an environment template. - -Positional arguments --------------------- - -**** - Environment template name - -murano env-template-del-app -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-del-app - -Delete application to the environment template. - -Positional arguments --------------------- - -**** - Environment template ID - -**** - Application ID - -murano env-template-delete -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-delete \[ ...] - -Delete an environment template. - -Positional arguments --------------------- - -**** - ID of environment(s) template to delete - -murano env-template-list -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-list - -List the environments templates. - -murano env-template-show -~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-show - -Display environment template details. - -Positional arguments --------------------- - -**** - Environment template ID - -murano env-template-update -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano env-template-update - -Update an environment template. - -Positional arguments --------------------- - -**** - Environment template ID - -**** - Environment template name - -murano environment-create -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano environment-create - -Create an environment. - -Positional arguments --------------------- - -**** - Environment name - -murano environment-delete -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano environment-delete \[ ...] - -Delete an environment. - -Positional arguments --------------------- - -**** - ID or name of environment(s) to delete - -Optional arguments ------------------- - -**--abandon** - If set will abandon environment without deleting any of its resources - -murano environment-list -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano environment-list - -List the environments. - -murano environment-rename -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano environment-rename - -Rename an environment. - -Positional arguments --------------------- - -**** - Environment ID or name - -**** - A name to which the environment will be renamed - -murano environment-show -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano environment-show - -Display environment details. - -Positional arguments --------------------- - -**** - Environment ID or name - -murano package-create -~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-create \[-t ] \[-c ] - \[-r ] \[-n ] - \[-f ] \[-a ] - \[--tags \[ \[ ...]]] - \[-d ] \[-o ] - \[-u ] \[--type TYPE] \[-l ] - -Create an application package. - -Optional arguments ------------------- - -**-t , --template ** - Path to the Heat template to import as an Application Definition - -**-c , --classes-dir ** - Path to the directory containing application classes - -**-r , --resources-dir ** - Path to the directory containing application resources - -**-n , --name ** - Display name of the Application in Catalog - -**-f , --full-name ** - Fully-qualified name of the Application in Catalog - -**-a , --author ** - Name of the publisher - -**--tags \[ \[ ...]]** - A list of keywords connected to the application - -**-d , --description ** - Detailed description for the Application in Catalog - -**-o , --output ** - The name of the output file archive to save locally - -**-u , --ui ** - Dynamic UI form definition - -**--type TYPE** - Package type. Possible values: Application or Library - -**-l , --logo ** - Path to the package logo - -murano package-delete -~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-delete \[ ...] - -Delete a package. - -Positional arguments --------------------- - -**** - Package ID to delete - -murano package-download -~~~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-download \[file] - -Download a package to a filename or stdout. - -Positional arguments --------------------- - -**** - Package ID to download - -**file** - Filename for download (defaults to stdout) - -murano package-import -~~~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-import \[-c \[ \[ ...]]] - \[--is-public] \[--package-version VERSION] - \[--exists-action {a,s,u}] - \[ ...] - -Import a package. ``FILE`` can be either a path to a zip file, URL or a FQPN. -``categories`` can be separated by a comma. - -Positional arguments --------------------- - -**** - URL of the murano zip package, FQPN, or path to zip package - -Optional arguments ------------------- - -**-c \[ \[ ...]], --categories \[ \[ ...]]** - Category list to attach - -**--is-public** - Make the package available for user from other project - -**--package-version VERSION** - Version of the package to use from repository (ignored when importing with - multiple packages) - -**--exists-action {a,s,u}** - Default action when package already exists - -murano package-list -~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-list \[--include-disabled] - -List available packages. - -Optional arguments ------------------- - -**--include-disabled** - -murano package-show -~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano package-show - -Display details for a package. - -Positional arguments --------------------- - -**** - Package ID to show - -murano service-show -~~~~~~~~~~~~~~~~~~~ - -.. code-block::console - - usage: murano service-show \[-p ] - -Positional arguments --------------------- - -**** - Environment ID to show applications from - -Optional arguments ------------------- - -**-p , --path ** - -Level of detalization to show. Leave empty to browse -all services in the environment diff --git a/doc/source/reference/appendix/glossary.rst b/doc/source/reference/appendix/glossary.rst deleted file mode 100644 index 0c1e38e76..000000000 --- a/doc/source/reference/appendix/glossary.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _glossary: - -======== -Glossary -======== diff --git a/doc/source/reference/appendix/murano_concepts.rst b/doc/source/reference/appendix/murano_concepts.rst deleted file mode 100644 index 627a52c81..000000000 --- a/doc/source/reference/appendix/murano_concepts.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _murano-concepts: - -========================================= -High-level definitions of Murano concepts -========================================= diff --git a/doc/source/reference/appendix/rest_api_spec.rst b/doc/source/reference/appendix/rest_api_spec.rst deleted file mode 100644 index 4f678f2e6..000000000 --- a/doc/source/reference/appendix/rest_api_spec.rst +++ /dev/null @@ -1,5 +0,0 @@ -.. _rest-api-spec: - -====================== -REST API specification -====================== diff --git a/doc/source/reference/appendix/tutorials.rst b/doc/source/reference/appendix/tutorials.rst deleted file mode 100644 index 08af588f0..000000000 --- a/doc/source/reference/appendix/tutorials.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _tutorials: - -.. meta:: - :robots: noindex - -========= -Tutorials -========= - -Integration with Docker -~~~~~~~~~~~~~~~~~~~~~~~ - -Integration with Kubernetes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -HA and autoscaling -~~~~~~~~~~~~~~~~~~ diff --git a/doc/source/reference/architecture.png b/doc/source/reference/architecture.png deleted file mode 100644 index efbceb10b..000000000 Binary files a/doc/source/reference/architecture.png and /dev/null differ diff --git a/doc/source/reference/architecture.rst b/doc/source/reference/architecture.rst deleted file mode 100644 index 86bdba899..000000000 --- a/doc/source/reference/architecture.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. _architecture: - -============ -Architecture -============ - -Murano is composed of the following major components: - -* murano command-line client -* murano-dashboard -* murano-api -* murano-engine -* murano-agent - -They interact with each other as illustrated in the following diagram: - -.. image:: architecture.png - :width: 600 px - :alt: Murano architecture - -All remote operations on users' servers, such as software installation -and configuration, are carried out through an AMQP queue to the murano-agent. -Such communication can easily be configured on a separate instance of AMQP -to ensure that the infrastructure and servers are isolated. - -Besides, Murano uses other OpenStack services to prevent the reimplementation -of the existing functionality. Murano interacts with these services using -their REST API through their python clients. - -The external services used by Murano are: - -* the **Orchestration service** (Heat) to orchestrate infrastructural - resources such as servers, volumes, and networks. Murano dynamically - creates heat templates based on application definitions. - -* the **Identity service** (Keystone) to make murano API available - to all OpenStack users. \ No newline at end of file diff --git a/doc/source/reference/key_features.rst b/doc/source/reference/key_features.rst deleted file mode 100644 index ed41e0ec3..000000000 --- a/doc/source/reference/key_features.rst +++ /dev/null @@ -1,93 +0,0 @@ -.. _key-features: - -============ -Key features -============ - -Murano has a number of features designed to -interact with the application catalog, for instance -managing what's in the catalog, and determining -how apps in the catalog are deployed. - -Application catalog -~~~~~~~~~~~~~~~~~~~ - -#. Easy browsing: - - * Icons display applications for point-and-click - and drag-and-drop selection and deployment. - - * Each application provides configuration information - required for deploying it to a cloud. - - * An application topology of your environment is available - in a separate tab, and shows the number of instances - spawned by each application. - - * The presence of the :guilabel:`Quick Deploy` button - on the applications page saves the time. - -#. Quick filtering by: - - * Tags and words included in application name and description. - * Recent activity. - * Predefined category. - -#. Dependency tracking: - - * Automatic detection of dependent applications that minimizes - the possibility of an application deployment with incorrect - configuration. - - * No underlying IaaS configuration knowledge is required. - - -Application catalog management -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. Easy application uploading using UI or CLI from: - - * Local zip file. - * URL. - * Package name, using an application repository. - -#. Managing applications include: - - * Application organization in categories or transfer between them. - * Application name, description and tags update. - * Predefined application categories list setting. - -#. Deployment tracking includes the availability of: - - * Logs for deployments via UI. - * Deployment modification history to track the recent changes. - - -Application lifecycle management -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -#. Simplified configuration and integration: - - * It is up to an application developer to decide what their application - will be able to do. - - * Dependencies between applications are easily configured. - - * New applications can be connected with already existing ones. - - * Well specified application actions are available. - -#. HA-mode and auto-scaling: - - * Application authors can set up any available monitoring system to track - application events and call corresponding actions, such as - failover, starting additional instances, and others. - -#. Isolation: - - * Applications in the same environments can easily interact with - each other, though applications between different projects (tenants) are isolated. - - - - diff --git a/doc/source/reference/overview_index.rst b/doc/source/reference/overview_index.rst deleted file mode 100644 index d20ab5d1d..000000000 --- a/doc/source/reference/overview_index.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _overview: - -Overview -~~~~~~~~ - -.. toctree:: - :maxdepth: 1 - - key_features - target_users - architecture - use_cases - appendix/appendix_index diff --git a/doc/source/reference/target_users.rst b/doc/source/reference/target_users.rst deleted file mode 100644 index 1f319be07..000000000 --- a/doc/source/reference/target_users.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. _target_users: - -============ -Target users -============ - -Cloud end users want to simply use applications as -opposed to installing and managing them. Cloud administrators, -in turn, would like to offer a well tested set of on demand -self-service applications to dramatically reduce their support burden. - -Murano solves the problems of both constituents. -It enables cloud administrators to publish cloud-ready -applications in an online catalog. Cloud end users can use -the catalog to deploy these on demand applications, reliably -and consistently, with a button click. - - -Cloud administrators -~~~~~~~~~~~~~~~~~~~~ - -For cloud administrators Murano provides UI and -API to easily compose, deploy, run applications, and manage -their lifecycle. - -Designed to be operating system independent, it can handle apps on all -manner of the environments in the cloud, either Windows -or Linux/Unix-based operating systems. - -It can be used to pre-configure and deploy anything that can -run in the cloud, -from low-level networking services to end-user applications. -By automating these ongoing cloud application management -activities, Murano speeds up the deployment, even for complex -distributed applications, without sacrificing simplicity -of use. - - - -Cloud end users -~~~~~~~~~~~~~~~ - -Murano catalog lets cloud end users choose -from the available applications and services, and compose -reliable distributed environments with an intuitive UI. -Even users unfamiliar with cloud environments can easily -deploy cloud-aware applications. - -Murano masks cloud-infrastructure specifics from end -users, letting them reliably compose and deploy -applications in the cloud for the widest range of -workloads and use cases without touching IaaS internals. - - diff --git a/doc/source/reference/use_cases.rst b/doc/source/reference/use_cases.rst deleted file mode 100644 index 7375b4c19..000000000 --- a/doc/source/reference/use_cases.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. _intro-use-cases: - -========= -Use cases -========= - -**IT-as-a-Service** - - An *IT organization* manages applications and controls the applications - availability to different OpenStack cloud users in a simple and timesaving - manner. - - A *cloud end user* can easily find and deploy any available application - from the catalog. - -**Self-service portal** - - An *application developer* and *quality assurance engineer* reduces efforts - on testing an application for compatibility with other applications, - databases, platforms, and other components it depends on, by configuring - compound combinations of applications dynamically and deploying - environments that satisfy all requirements within minutes. - -**Glue layer use case** - - A *cloud end user* is able to link an ever growing number of technologies - to any application in an OpenStack cloud with a minimum cost due to - the powerful Murano architecture. - - Currently, Murano applications have been integrated with the following - technologies: Docker, Legacy apps VMs or bare metal, apps outside of - OpenStack, and others. - - The following technologies are to become available in the future: - Cloudify and TOSCA, Apache Brooklyn, and APS. - diff --git a/doc/source/user/figures/add_key_pair.png b/doc/source/user/figures/add_key_pair.png deleted file mode 100644 index b451c1c7f..000000000 Binary files a/doc/source/user/figures/add_key_pair.png and /dev/null differ diff --git a/doc/source/user/figures/add_pkg_info.png b/doc/source/user/figures/add_pkg_info.png deleted file mode 100644 index 203da3a3b..000000000 Binary files a/doc/source/user/figures/add_pkg_info.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/add_component.png b/doc/source/user/figures/add_to_env/add_component.png deleted file mode 100644 index 252c63bf5..000000000 Binary files a/doc/source/user/figures/add_to_env/add_component.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/add_from_cat.png b/doc/source/user/figures/add_to_env/add_from_cat.png deleted file mode 100644 index f924a971e..000000000 Binary files a/doc/source/user/figures/add_to_env/add_from_cat.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/add_more_apps.png b/doc/source/user/figures/add_to_env/add_more_apps.png deleted file mode 100644 index 199e46e4c..000000000 Binary files a/doc/source/user/figures/add_to_env/add_more_apps.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/add_to_env.png b/doc/source/user/figures/add_to_env/add_to_env.png deleted file mode 100644 index 734cf79cf..000000000 Binary files a/doc/source/user/figures/add_to_env/add_to_env.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/configure_app.png b/doc/source/user/figures/add_to_env/configure_app.png deleted file mode 100644 index 4f78f16c5..000000000 Binary files a/doc/source/user/figures/add_to_env/configure_app.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/drag_and_drop.png b/doc/source/user/figures/add_to_env/drag_and_drop.png deleted file mode 100644 index b9be3a0e4..000000000 Binary files a/doc/source/user/figures/add_to_env/drag_and_drop.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/quick_deploy.png b/doc/source/user/figures/add_to_env/quick_deploy.png deleted file mode 100644 index 884323d73..000000000 Binary files a/doc/source/user/figures/add_to_env/quick_deploy.png and /dev/null differ diff --git a/doc/source/user/figures/add_to_env/quick_env.png b/doc/source/user/figures/add_to_env/quick_env.png deleted file mode 100644 index 4592551b7..000000000 Binary files a/doc/source/user/figures/add_to_env/quick_env.png and /dev/null differ diff --git a/doc/source/user/figures/app_category.png b/doc/source/user/figures/app_category.png deleted file mode 100644 index e14e05feb..000000000 Binary files a/doc/source/user/figures/app_category.png and /dev/null differ diff --git a/doc/source/user/figures/app_details.png b/doc/source/user/figures/app_details.png deleted file mode 100644 index f27f35635..000000000 Binary files a/doc/source/user/figures/app_details.png and /dev/null differ diff --git a/doc/source/user/figures/app_filter.png b/doc/source/user/figures/app_filter.png deleted file mode 100644 index 661f8628f..000000000 Binary files a/doc/source/user/figures/app_filter.png and /dev/null differ diff --git a/doc/source/user/figures/app_filter_example.png b/doc/source/user/figures/app_filter_example.png deleted file mode 100644 index 61e7394ac..000000000 Binary files a/doc/source/user/figures/app_filter_example.png and /dev/null differ diff --git a/doc/source/user/figures/app_logs.png b/doc/source/user/figures/app_logs.png deleted file mode 100644 index fc2a82cf8..000000000 Binary files a/doc/source/user/figures/app_logs.png and /dev/null differ diff --git a/doc/source/user/figures/browse_zip_file.png b/doc/source/user/figures/browse_zip_file.png deleted file mode 100644 index aadcbed43..000000000 Binary files a/doc/source/user/figures/browse_zip_file.png and /dev/null differ diff --git a/doc/source/user/figures/bundle_name.png b/doc/source/user/figures/bundle_name.png deleted file mode 100644 index 77bfe7002..000000000 Binary files a/doc/source/user/figures/bundle_name.png and /dev/null differ diff --git a/doc/source/user/figures/component-details.png b/doc/source/user/figures/component-details.png deleted file mode 100644 index 6a1f33d8e..000000000 Binary files a/doc/source/user/figures/component-details.png and /dev/null differ diff --git a/doc/source/user/figures/delete_application.png b/doc/source/user/figures/delete_application.png deleted file mode 100644 index 9c182ea5d..000000000 Binary files a/doc/source/user/figures/delete_application.png and /dev/null differ diff --git a/doc/source/user/figures/deploy_env.png b/doc/source/user/figures/deploy_env.png deleted file mode 100644 index 5c16adf3d..000000000 Binary files a/doc/source/user/figures/deploy_env.png and /dev/null differ diff --git a/doc/source/user/figures/deploy_env_2.png b/doc/source/user/figures/deploy_env_2.png deleted file mode 100644 index 77bdbce38..000000000 Binary files a/doc/source/user/figures/deploy_env_2.png and /dev/null differ diff --git a/doc/source/user/figures/env-component-logs.png b/doc/source/user/figures/env-component-logs.png deleted file mode 100644 index 655561c80..000000000 Binary files a/doc/source/user/figures/env-component-logs.png and /dev/null differ diff --git a/doc/source/user/figures/env_default_network.png b/doc/source/user/figures/env_default_network.png deleted file mode 100644 index 4fd7fc1d8..000000000 Binary files a/doc/source/user/figures/env_default_network.png and /dev/null differ diff --git a/doc/source/user/figures/environments.png b/doc/source/user/figures/environments.png deleted file mode 100644 index 43f7b4380..000000000 Binary files a/doc/source/user/figures/environments.png and /dev/null differ diff --git a/doc/source/user/figures/import_bundle.png b/doc/source/user/figures/import_bundle.png deleted file mode 100644 index 527d3f93b..000000000 Binary files a/doc/source/user/figures/import_bundle.png and /dev/null differ diff --git a/doc/source/user/figures/import_package.png b/doc/source/user/figures/import_package.png deleted file mode 100644 index b986f4e11..000000000 Binary files a/doc/source/user/figures/import_package.png and /dev/null differ diff --git a/doc/source/user/figures/logs.png b/doc/source/user/figures/logs.png deleted file mode 100644 index 0bd2c4927..000000000 Binary files a/doc/source/user/figures/logs.png and /dev/null differ diff --git a/doc/source/user/figures/murano_actions.png b/doc/source/user/figures/murano_actions.png deleted file mode 100644 index 0b64ac596..000000000 Binary files a/doc/source/user/figures/murano_actions.png and /dev/null differ diff --git a/doc/source/user/figures/qs_app_category.png b/doc/source/user/figures/qs_app_category.png deleted file mode 100644 index ddbf5ef76..000000000 Binary files a/doc/source/user/figures/qs_app_category.png and /dev/null differ diff --git a/doc/source/user/figures/qs_apps.png b/doc/source/user/figures/qs_apps.png deleted file mode 100644 index c4fe4ca91..000000000 Binary files a/doc/source/user/figures/qs_apps.png and /dev/null differ diff --git a/doc/source/user/figures/qs_package_details.png b/doc/source/user/figures/qs_package_details.png deleted file mode 100644 index e07bc6ac4..000000000 Binary files a/doc/source/user/figures/qs_package_details.png and /dev/null differ diff --git a/doc/source/user/figures/qs_package_import.png b/doc/source/user/figures/qs_package_import.png deleted file mode 100644 index 6560077c2..000000000 Binary files a/doc/source/user/figures/qs_package_import.png and /dev/null differ diff --git a/doc/source/user/figures/qs_package_url.png b/doc/source/user/figures/qs_package_url.png deleted file mode 100644 index 2e6562742..000000000 Binary files a/doc/source/user/figures/qs_package_url.png and /dev/null differ diff --git a/doc/source/user/figures/qs_quick_deploy.png b/doc/source/user/figures/qs_quick_deploy.png deleted file mode 100644 index c798afd49..000000000 Binary files a/doc/source/user/figures/qs_quick_deploy.png and /dev/null differ diff --git a/doc/source/user/figures/qs_quick_deploy_2.png b/doc/source/user/figures/qs_quick_deploy_2.png deleted file mode 100644 index 09927186c..000000000 Binary files a/doc/source/user/figures/qs_quick_deploy_2.png and /dev/null differ diff --git a/doc/source/user/figures/qs_quick_env.png b/doc/source/user/figures/qs_quick_env.png deleted file mode 100644 index 13344b64b..000000000 Binary files a/doc/source/user/figures/qs_quick_env.png and /dev/null differ diff --git a/doc/source/user/figures/repository.png b/doc/source/user/figures/repository.png deleted file mode 100644 index d36247fbf..000000000 Binary files a/doc/source/user/figures/repository.png and /dev/null differ diff --git a/doc/source/user/figures/select_packages.png b/doc/source/user/figures/select_packages.png deleted file mode 100644 index adc01803a..000000000 Binary files a/doc/source/user/figures/select_packages.png and /dev/null differ diff --git a/doc/source/user/figures/topology_element_1.png b/doc/source/user/figures/topology_element_1.png deleted file mode 100644 index a58588bd4..000000000 Binary files a/doc/source/user/figures/topology_element_1.png and /dev/null differ diff --git a/doc/source/user/figures/topology_element_2.png b/doc/source/user/figures/topology_element_2.png deleted file mode 100644 index 40734c498..000000000 Binary files a/doc/source/user/figures/topology_element_2.png and /dev/null differ diff --git a/doc/source/user/figures/topology_kubernetes.png b/doc/source/user/figures/topology_kubernetes.png deleted file mode 100644 index a3d24f6f9..000000000 Binary files a/doc/source/user/figures/topology_kubernetes.png and /dev/null differ diff --git a/doc/source/user/figures/topology_wordpress.png b/doc/source/user/figures/topology_wordpress.png deleted file mode 100644 index 3b8436b88..000000000 Binary files a/doc/source/user/figures/topology_wordpress.png and /dev/null differ diff --git a/doc/source/user/quickstart/quickstart.rst b/doc/source/user/quickstart/quickstart.rst deleted file mode 100644 index a393d975a..000000000 --- a/doc/source/user/quickstart/quickstart.rst +++ /dev/null @@ -1,129 +0,0 @@ -.. _quickstart: - -========== -QuickStart -========== - -This is a brief walkthrough to quickly get you familiar with the basic -operations you can perform when using the Application catalog directly -from the dashboard. - -For the detailed instructions on how to :ref:`manage your environments -` and :ref:`applications `, -please proceed with dedicated sections. - - -Upload an application -~~~~~~~~~~~~~~~~~~~~~ - -To upload an application to the catalog: - -#. Log in to the OpenStack dashboard. - -#. Navigate to :menuselection:`Applications > Manage > Packages`. - -#. Click on the :guilabel:`Import Package` button: - - .. image:: ../figures/qs_package_import.png - :alt: Packages page - :width: 600 px - -#. In the :guilabel:`Import Package` dialog: - - * Select ``URL`` from the ``Package Source`` drop-down list; - - * Specify the URL in the :guilabel:`Package URL` field. Lets upload - the Apache HTTP Server package using - http://storage.apps.openstack.org/apps/com.example.apache.ApacheHttpServer.zip; - - * Click :guilabel:`Next` to continue: - - .. image:: ../figures/qs_package_url.png - :width: 600 px - :alt: Import Package dialog 1 - -#. View the package details in the new dialog, click :guilabel:`Next` - to continue: - - .. image:: ../figures/qs_package_details.png - :width: 600 px - :alt: Import Package dialog 2 - -#. Select the :guilabel:`Application Servers` from the application category list, - click :guilabel:`Create` to import the application package: - - .. image:: ../figures/qs_app_category.png - :width: 600 px - :alt: Import Package dialog 3 - -#. Now your application is available from :menuselection:`Applications > - Catalog > Browse` page. - - -Deploy an application -~~~~~~~~~~~~~~~~~~~~~ - -To add an application to an environment's component list -and deploy the environment: - -#. Log in to the OpenStack dashboard. - -#. Navigate to :menuselection:`Applications > Catalog > Browse`. - -#. Click on the :guilabel:`Quick Deploy` button from the required application - from the list. Lets deploy Apache HTTP Server, for example: - - .. image:: ../figures/qs_apps.png - :width: 600 px - :alt: Applications page - -#. Check :guilabel:`Assign Floating IP` and click :guilabel:`Next` to proceed: - - .. image:: ../figures/qs_quick_deploy.png - :width: 600 px - :alt: Configure Application dialog 1 - -#. Select the :guilabel:`Instance Image` from the drop-down list and click - :guilabel:`Create`: - - .. image:: ../figures/qs_quick_deploy_2.png - :width: 600 px - :alt: Configure Application dialog 2 - -#. Now the Apache HTTP Server application is successfully added to the newly - created ``quick-env-4`` environment. - Click the :guilabel:`Deploy This Environment` button - to start the deployment: - - .. image:: ../figures/qs_quick_env.png - :width: 600 px - :alt: Environment "quick-env-1" page - - It may take some time for the environment to deploy. Wait until the status - is changed from ``Deploying`` to ``Ready``. - -#. Navigate to :menuselection:`Applications > Catalog > Environments` to - view the details. - - -Delete an application -~~~~~~~~~~~~~~~~~~~~~ - -To delete an application that belongs to the environment: - -#. Log in to the OpenStack dashboard. - -#. Navigate to :menuselection:`Applications > Catalog > Environments`. - -#. Click on the name of the environment to view its details, which include - components, topology, and deployment history. - -#. In the :guilabel:`Component List` section, click on the - :guilabel:`Delete Component` button next to the application to be deleted. - Confirm the deletion. - -.. note:: - If an application that you are deleting has already been deployed, - you should redeploy it to apply the recent changes. If the environment - has not been deployed with this component, the changes are applied - immediately on receiving the confirmation. \ No newline at end of file diff --git a/doc/source/user/user_index.rst b/doc/source/user/user_index.rst deleted file mode 100644 index 81c8a0a20..000000000 --- a/doc/source/user/user_index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. _user-guide: - -User Guide -~~~~~~~~~~ - -.. toctree:: - :maxdepth: 2 - - userguide/manage_environments - userguide/manage_applications - userguide/log_in_to_murano_instance - userguide/use_cli - userguide/deploying_using_cli - ../reference/appendix/articles/multi_region diff --git a/doc/source/user/userguide/deploying_using_cli.rst b/doc/source/user/userguide/deploying_using_cli.rst deleted file mode 100644 index db8597818..000000000 --- a/doc/source/user/userguide/deploying_using_cli.rst +++ /dev/null @@ -1,183 +0,0 @@ -.. - Licensed 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. - -.. _deploying-using-cli: - -================================ -Deploying environments using CLI -================================ - -The main tool for deploying murano environments is murano-dashboard. It is -designed to be easy-to-use and intuitive. But it is not the only tool you can -use to deploy a murano environment, murano CLI client also possesses required -functionality for the task. This is an advanced scenario, however, that -requires knowledge of :ref:`internal murano workflow `, -:ref:`murano object model `, and -:ref:`murano environment ` lifecycle. -This scenario is suitable for deployments without -horizon or deployment automation. - - -.. note:: - - This is an advanced mechanism and you should use it only when you are - confident in what you are doing. Otherwise, it is recommended that you use - murano-dashboard. - -Create an environment -~~~~~~~~~~~~~~~~~~~~~ - -The following command creates a new murano environment that is ready for -configuration. For convenience, this guide refers to environment ID as -``$ENV_ID``. - -.. code-block:: console - - $ murano environment-create deployed_from_cli - - +----------------------------------+-------------------+---------------------+---------------------+ - | ID | Name | Created | Updated | - +----------------------------------+-------------------+---------------------+---------------------+ - | a66e5ea35e9d4da48c2abc37b5a9753a | deployed_from_cli | 2015-10-06T13:50:45 | 2015-10-06T13:50:45 | - +----------------------------------+-------------------+---------------------+---------------------+ - -Create a configuration session -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Murano uses configuration sessions to allow several users to edit and configure -the same environment concurrently. Most of environment-related commands -require the ``--session-id`` parameter. For convenience, this guide -refers to session ID as ``$SESS_ID``. - -To create a configuration session, use the -:command:`murano environment-session-create $ENV_ID` command: - -.. code-block:: console - - $ murano environment-session-create $ENV_ID - - +----------+----------------------------------+ - | Property | Value | - +----------+----------------------------------+ - | id | 5cbe7e561ffc484ebf11aabf83f9f4c6 | - +----------+----------------------------------+ - - -Add applications to an environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To manipulate environments object model from CLI, use the -:command:`environment-apps-edit` command: - -.. code-block:: console - - $ murano environment-apps-edit --session-id $SESS_ID $ENV_ID object_model_patch.json - -The :file:`object_model_patch.json` contains the ``jsonpatch`` object. This -object is applied to the ``/services`` key of the environment in question. -Below is an example of the :file:`object_model_patch.json` file content: - -.. code-block:: json - - [ - { "op": "add", "path": "/-", "value": - { - "instance": { - "availabilityZone": "nova", - "name": "xwvupifdxq27t1", - "image": "fa578106-b3c1-4c42-8562-4e2e2d2a0a0c", - "keyname": "", - "flavor": "m1.small", - "assignFloatingIp": false, - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "===id1===" - } - }, - "name": "ApacheHttpServer", - "enablePHP": true, - "?": { - "type": "com.example.apache.ApacheHttpServer", - "id": "===id2===" - } - } - } - ] - -For convenience, the murano client replaces the ``"===id1==="``, ``"===id2==="`` -(and so on) strings with UUIDs. This way you can ensure that object IDs -inside your object model are unique. -To learn more about jsonpatch, consult jsonpatch.com_ and `RFC 6902`_. -The :command:`environment-apps-edit` command fully supports jsonpatch. -This means that you can alter, add, or remove parts of your applications -object model. - -Verify your object model -~~~~~~~~~~~~~~~~~~~~~~~~ - -To verify whether your object model is correct, check the environment by -running the :command:`environment-show` command with the -``--session-id`` parameter: - -.. code-block:: console - - $ murano environment-show $ENV_ID --session-id $SESS_ID --only-apps - - [ - { - "instance": { - "availabilityZone": "nova", - "name": "xwvupifdxq27t1", - "assignFloatingIp": false, - "keyname": "", - "flavor": "m1.small", - "image": "fa578106-b3c1-4c42-8562-4e2e2d2a0a0c", - "?": { - "type": "io.murano.resources.LinuxMuranoInstance", - "id": "fc4fe975f5454bab99bb0e309249e2d2" - } - }, - "?": { - "status": "pending", - "type": "com.example.apache.ApacheHttpServer", - "id": "69cdf10d31e64196b4de894e7ea4f1be" - }, - "enablePHP": true, - "name": "ApacheHttpServer" - } - ] - - -Deploy your environment -~~~~~~~~~~~~~~~~~~~~~~~ - -To deploy a session ``$SESS_ID`` of your environment, use the -:command:`murano environment-deploy` command: - -.. code-block:: console - - $ murano environment-deploy $ENV_ID --session-id $SESS_ID - -You can later use the :command:`murano environment-show` command to -track the deployment status. - -To view the deployed applications of a particular environment, use the -:command:`murano environment-show` command with the ``--only-apps`` -parameter and specifying the environment ID: - -.. code-block:: console - - $ murano environment-show $ENV_ID --only-apps - -.. _jsonpatch.com: http://jsonpatch.com -.. _RFC 6902: http://tools.ietf.org/html/rfc6902 diff --git a/doc/source/user/userguide/install_client.rst b/doc/source/user/userguide/install_client.rst deleted file mode 100644 index cad1799d9..000000000 --- a/doc/source/user/userguide/install_client.rst +++ /dev/null @@ -1,121 +0,0 @@ -.. _install-client: - -Install and use the murano client -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Application Catalog project provides a command-line client, -python-muranoclient, which enables you to access the project API. -For prerequisites, see `Install the prerequisite software `_. - -To install the latest murano CLI client, run the following command in your -terminal: - -.. code-block:: console - - $ pip install python-muranoclient - -Discover the client version number ----------------------------------- - -To discover the version number for the python-muranoclient, run the following -command: - -.. code-block:: console - - $ murano --version - -To check the latest version, see `Client library for Murano API `_. - - -Upgrade or remove the client ----------------------------- - -To upgrade or remove the python-muranoclient, use the corresponding commands. - -**To upgrade the client:** - -.. code-block:: console - - $ pip install --upgrade python-muranoclient - -**To remove the client:** - -.. code-block:: console - - $ pip uninstall python-muranoclient - -Set environment variables -------------------------- - -To use the murano client, you must set the environment variables. To do this, -download and source the OpenStack RC file. For more information, see -`Download and source the OpenStack RC file `_. - -Alternatively, create the ``PROJECT-openrc.sh`` file from scratch. For this, -perform the following steps: - -#. In a text editor, create a file named ``PROJECT-openrc.sh`` containing the - following authentication information: - - .. code-block:: console - - export OS_USERNAME=user - export OS_PASSWORD=password - export OS_PROJECT_NAME=tenant - export OS_USER_DOMAIN_NAME=Default - export OS_PROJECT_DOMAIN_NAME=Default - export OS_AUTH_URL=http://auth.example.com:5000/v3 - export MURANO_URL=http://murano.example.com:8082/ - -#. In the terminal, source the ``PROJECT-openrc.sh`` file. For example: - - .. code-block:: console - - $ . admin-openrc.sh - -Once you have configured your authentication parameters, run -:command:`murano help` to see a complete list of available commands and -arguments. Use :command:`murano help ` to get help on a specific -subcommand. - -.. seealso:: - - `Set environment variables using the OpenStack RC file `_. - -Bash completion ---------------- - -To get the latest bash completion script, download -`murano.bash_completion `_ -from the source repository and add it to your completion scripts. - -If you are not aware of the completion scripts location, perform the following -steps: - -#. Create a new directory: - - .. code-block:: console - - $ mkdir -p ~/.bash_completion/ - -#. Create a file containing the bash completion script: - - .. code-block:: console - - $ curl https://opendev.org/openstack/python-muranoclient/raw/branch/master/tools/murano.bash_completion > ~/.bash_completion/murano.sh - -#. Add the following code to the ``~/.profile`` file: - - .. code-block:: bash - - for file in $HOME/.bash_completion/*.sh; do - if [ -f "$file" ]; then - . "$file" - fi - done - -#. In the current terminal, run: - - .. code-block:: console - - $ . ~/.bash_completion/murano.sh diff --git a/doc/source/user/userguide/log_in_to_murano_instance.rst b/doc/source/user/userguide/log_in_to_murano_instance.rst deleted file mode 100644 index ae925a456..000000000 --- a/doc/source/user/userguide/log_in_to_murano_instance.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. _login-murano-instance: - -================================= -Log in to murano-spawned instance -================================= - -After the application is successfully deployed, you may need to log in to the -virtual machine with the installed application. - -All cloud images, including images imported from the -`OpenStack Application Catalog `_, -have password authentication turned off. Therefore, it is not possible -to log in from the dashboard console. SSH is used to reach an instance spawned -by murano. - -Possible default image users are: - - -* *ec2-user* -* *ubuntu* or *debian* (depending on the operating system) - -To log in to murano-spawned instance, perform the following steps: - -#. Prepare a key pair. - - To log in through SSH, provide a key pair during the application creation. - If you do not have a key pair, click the plus sign to create one directly - from the :guilabel:`Configure Application` dialog. - - .. image:: ../figures/add_key_pair.png - :alt: Application creation: key pair - :width: 630 px - -#. After the deployment is completed, find out the instance IP address. For - this, see: - - * Deployment logs - - .. image:: ../figures/app_logs.png - :alt: Application logs: IP is provided - :width: 630 px - - * Detailed instance parameters - - See the :guilabel:`Instance name` link on the - :guilabel:`Component Details` page. - - .. image:: ../figures/app_details.png - :alt: Application details: instance details link - :width: 630 px - -#. To connect to the instance through SSH with the key pair, run: - - .. code-block:: console - - $ ssh @ -i diff --git a/doc/source/user/userguide/manage_applications.rst b/doc/source/user/userguide/manage_applications.rst deleted file mode 100644 index 18adb0143..000000000 --- a/doc/source/user/userguide/manage_applications.rst +++ /dev/null @@ -1,577 +0,0 @@ -.. _manage_applications: - -===================== -Managing applications -===================== - -In murano, each application, as well as the form of application data entry, -is defined by its package. The murano dashboard allows you to import and -manage packages as well as search, filter, and add applications from catalog -to environments. - -This section provides detailed instructions on how to import application -packages into murano and then add applications to an environment and deploy -it. This section also shows you how to find component details, application -topology, and deployment logs. - -Import an application package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are several ways of importing an application package into -murano: - -* :ref:`from a zip file ` -* :ref:`from murano applications repository ` -* :ref:`from bundles of applications ` - -.. _ui_zip: - -From a zip file ---------------- - -Perform the following steps to import an application package from a -.zip file: - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Manage > Packages`. -#. Click the :guilabel:`Import Package` button on the top right of the - page. - - .. image:: ../figures/import_package.png - :alt: Packages page: Import Package 1 - :width: 630 px - -#. From the :guilabel:`Package source` drop-down list - choose :guilabel:`File`, then click :guilabel:`Browse` to select a - .zip file you want to import, and then click :guilabel:`Next`. - - .. image:: ../figures/browse_zip_file.png - :alt: Import Package dialog: zip file - :width: 630 px - -#. At this step, the package is already uploaded. Choose a category - from the :guilabel:`Application Category` menu. You can select - multiple categories while holding down the :kbd:`Ctrl` key. If - necessary, verify and update the information about the package, - then click the :guilabel:`Create` button. - - .. image:: ../figures/add_pkg_info.png - :alt: Import Package dialog: Description - :width: 630 px - -.. note:: - Though specifying a category is optional, we recommend that you - specify at least one. It helps to filter applications in the - catalog. - -| Green messages appear at the top right corner when the application - is successfully uploaded. In case of a failure, you will see a red - message with the problem description. For more information, please - refer to the logs. - - -.. _ui_repo: - -From a repository ------------------ - -Perform the following steps to import an application package from -murano applications repository: - -.. note:: - To import an application package from a repository, you need to - know the full name of the package. For the packages names, go to - http://apps.openstack.org/#tab=murano-apps and click on the desired - package to see its full name. - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Manage > Packages`. - -#. Click the :guilabel:`Import Package` button on the top right of the - page. - - .. image:: ../figures/import_package.png - :alt: Packages page: Import Package 2 - :width: 630 px - -#. From the :guilabel:`Package source` drop-down list, - choose :guilabel:`Repository`, enter the package name, and then - click :guilabel:`Next`. Note that you may also specify the version - of the package. - - .. image:: ../figures/repository.png - :alt: Import Package dialog: Repository - :width: 630 px - -#. At this step, the package is already uploaded. Choose a category - from the :guilabel:`Application Category` menu. You can select - multiple categories while holding down the :kbd:`Ctrl` key. If - necessary, verify and update the information about the package, - then click the :guilabel:`Create` button. - - .. image:: ../figures/add_pkg_info.png - :alt: Import Package dialog: Description - :width: 630 px - -.. _ui_bundles: - -From a bundle of applications ------------------------------ - -Perform the following steps to import a bundle of applications: - -.. note:: - To import an application bundle from a repository, you need - to know the full name of the package bundle. To find it out, go - to http://apps.openstack.org/#tab=murano-apps and click on the - desired bundle to see its full name. - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Manage > Packages`. - -#. Click the :guilabel:`Import Bundle` button on the top right of the - page. - - .. image:: ../figures/import_bundle.png - :alt: Packages page: Import Bundle - :width: 630 px - -#. From the :guilabel:`Package Bundle Source` drop-down list, choose - :guilabel:`Repository`, enter the bundle name, and then - click :guilabel:`Create`. - - .. image:: ../figures/bundle_name.png - :alt: Import Bundle dialog - :width: 630 px - -Search for an application in the catalog -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you have imported many applications and want to quickly find -a required one, you can filter them by category, tags and words that -the application name or description contains: - -In OpenStack dashboard, navigate to :menuselection:`Applications > Catalog -> Browse`. - -The page is divided into two sections: - -* **Recent Activity** shows the most recently imported or deployed - applications. - -* The bottom section contains all the available applications sorted - alphabetically. - -To view all the applications of a specific category, select it from -the :guilabel:`App Category` drop-down list: - - .. image:: ../figures/app_category.png - :alt: Applications page: App Category - :width: 630 px - -To filter applications by tags or words from the application name or -description, use the rightmost filter: - - .. image:: ../figures/app_filter.png - :alt: Applications page: Filter - :width: 630 px - -.. note:: - - Tags can be specified during the import of an application package. - -For example, there is an application that has the word -*document-oriented* in description. Let's find it with the filter. -The following screenshot shows you the result. - - .. image:: ../figures/app_filter_example.png - :alt: Applications page: example - :width: 630 px - -Delete an application package -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To delete an application package from the catalog, please perform -the following steps: - -#. In OpenStack dashboard, navigate to :menuselection:`Applications > Manage > Packages`. - -#. Select a package or multiple packages you want to delete and click - :guilabel:`Delete Packages`. - - .. image:: ../figures/select_packages.png - :alt: Packages page: Select packages - :width: 630 px - -#. Confirm the deletion. - -Add an application to environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -After uploading an application, the second step is to add it to an -environment. You can do this: - -* :ref:`from environment details page ` -* :ref:`from applications catalog page ` - -.. _from_env: - -From environment details page ------------------------------ - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Catalog > Environments`. - -#. Find the environment you want to manage and click - :guilabel:`Manage Components`, or simply click on the environment's - name. - -#. Procced with the :ref:`Drop Components here ` field - or the :ref:`Add Component ` button. - -.. _drag_and_drop: - -**Use of Drop Components here field** - -#. On the Environment Components page, drag and drop a desired - application into the :guilabel:`Drop Components here` field under - the :guilabel:`Application Components` section. - - .. image:: ../figures/add_to_env/drag_and_drop.png - :alt: Environment Components page: Drag and drop a component - :width: 630 px - -#. Configure the application. Note that the settings may vary from app to app - and are predefined by the application author. When done, click - :guilabel:`Next`, then click :guilabel:`Create`. - -Now the application appears in the :guilabel:`Component List` section on -the Environment Components page. - -.. _add_component: - -**Use of Add Component button** - -#. On the Environment Components page, click :guilabel:`Add Component`. - - .. image:: ../figures/add_to_env/add_component.png - :alt: Environment Components page: Add component - :width: 630 px - -#. Find the application you want to add and click :guilabel:`Add to Env`. - - .. image:: ../figures/add_to_env/add_to_env.png - :alt: Applications page: Add to Env - :width: 630 px - -#. Configure the application and click :guilabel:`Next`. Note that the - settings may vary from app to app and are predefined by the - application author. - -#. To add more applications, check :guilabel:`Continue application adding`, - then click :guilabel:`Create` and repeat the steps above. Otherwise, just - click :guilabel:`Create`. - - .. image:: ../figures/add_to_env/add_more_apps.png - :alt: Configure Application dialog: Add more applications - :width: 630 px - - Now the application appears in the :guilabel:`Component List` section - on the Environment Components page. - -.. _from_cat: - -From applications catalog page ------------------------------- - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Catalog > Browse`. - -#. On the Applications catalog page, use one of the following methods: - - * `Quick deploy`_. Automatically creates an - environment, adds the selected application, and redirects you - to the page with the environment components. - * `Add to Env`_. Adds an application to an already - existing environment. - -.. _Quick deploy: - -**Quick Deploy button** - -#. Find the application you want to add and click - :guilabel:`Quick Deploy`. Let's add Apache Tomcat, for example. - - .. image:: ../figures/add_to_env/quick_deploy.png - :alt: Applications page: Quick Deploy - :width: 630 px - - -#. Configure the application. Note that the settings may vary from app to - app and are predefined by the application author. When done, click - :guilabel:`Next`, then click :guilabel:`Create`. In the example - below we assign a floating IP address. - - .. image:: ../figures/add_to_env/configure_app.png - :alt: Configure Application dialog - :width: 630 px - -Now the Apache Tomcat application is successfully added to an -automatically created ``quick-env-1`` environment. - - .. image:: ../figures/add_to_env/quick_env.png - :alt: Environment Components page: Select packages - :width: 630 px - -.. _Add to Env: - -**Add to Env button** - -#. From the :guilabel:`Environment` drop-down list, select the - required environment. - - .. image:: ../figures/add_to_env/add_from_cat.png - :alt: Applications page: Select environment - :width: 630 px - -#. Find the application you want to add and click - :guilabel:`Add to Env`. Let's add Apache Tomcat, for example. - - .. image:: ../figures/add_to_env/add_to_env.png - :alt: Applications page: Add to Env - :width: 630 px - -#. Configure the application and click :guilabel:`Next`. Note that the - settings may vary from app to app and are predefined by the - application author. In the example below we assign a floating - IP address. - - .. image:: ../figures/add_to_env/configure_app.png - :alt: Configure Application dialog - :width: 630 px - -#. To add more applications, check :guilabel:`Add more applications - to the environment`, then click :guilabel:`Create` and repeat the - steps above. Otherwise, just click :guilabel:`Create`. - - .. image:: ../figures/add_to_env/add_more_apps.png - :alt: Configure Application dialog: Add more applications - :width: 630 px - -Deploy an environment -~~~~~~~~~~~~~~~~~~~~~ - -Make sure to add necessary applications to your environment, then deploy it -following one of the options below: - -* Deploy an environment from the Environments page - - #. In OpenStack dashboard, navigate to :menuselection:`Applications > - Catalog > Environments`. - - #. Select :guilabel:`Deploy Environment` from the Actions drop-down list - next to the environment you want to deploy. - - .. image:: ../figures/deploy_env_2.png - :width: 630 px - :alt: Environments page - - It may take some time for the environment to deploy. Wait for the status - to change from `Deploying` to `Ready`. You cannot add applications to - your environment during deployment. - -* Deploy an environment from the Environment Components page - - #. In OpenStack dashboard, navigate to :menuselection:`Applications > - Catalog > Environments`. - - #. Click the name of the environment you want to deploy. - - .. image:: ../figures/environments.png - :width: 630 px - :alt: Environments page - - #. On the Environment Components page, click :guilabel:`Deploy This Environment` - to start the deployment. - - .. image:: ../figures/deploy_env.png - :width: 630 px - :alt: Environment Components page - - It may take some time for the environment to deploy. You cannot add - applications to your environment during deployment. Wait for the status - to change from `Deploying` to `Ready`. You can check the status either on - the Environments page or on the Environment Components page. - -.. _component-details: - -Browse component details ------------------------- - -You can browse component details to find the following information about -a component: - -* Name -* ID -* Type -* Instance name (available only after deployment) -* Heat orchestration stack name (available only after deployment) - -To browse a component details, perform the following steps: - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Catalog > Environments`. - -#. Click the name of the required environment. - -#. In the :guilabel:`Component List` section, click the name of the required - component. - - .. image:: ../figures/component-details.png - :width: 630 px - :alt: Components details - - The links redirect to corresponding horizon pages with the detailed - information on instance and heat stack. - -.. _application-topology: - -Application topology --------------------- - -Once you add an application to your environment, the application topology of -this environment becomes available in a separate tab. The topology represents -an elastic diagram showing the relationship between a component and the -infrastructure it runs on. To view the topology: - -#. In OpenStack dashboard, navigate to - :menuselection:`Applications > Catalog > Environments`. - -#. Click the name of the necessary environment. - -#. Click the :guilabel:`Topology` tab. - -The topology is helpful to visually display complex components, for example -Kubernetes. The red icons reflect errors during the deployment while the green -ones show success. - -.. image:: ../figures/topology_kubernetes.png - :alt: Topology tab: Deployment failed - :width: 630 px - -The following elements of the topology are virtual machine and an instance of -dependent MuranoPL class: - -+---------------------------------------------+----------------------------+ -| Element | Meaning | -+=============================================+============================+ -| .. image:: ../figures/topology_element_1.png| Virtual machine | -+---------------------------------------------+----------------------------+ -| .. image:: ../figures/topology_element_2.png| Instance | -+---------------------------------------------+----------------------------+ - -Position your mouse pointer over an element to see its name, ID, and other -details. - -.. image:: ../figures/topology_wordpress.png - :alt: Topology tab: Deployment successful - :width: 630 px - - -Deployment logs ---------------- - -To get detailed information on a deployment, use: - -* :ref:`Deployment history `, which contains logs and deployment - structure of an environment. - -* :ref:`Latest deployment log `, which contains information on the - latest deployment of an environment. - -* :ref:`Component logs `, which contain logs on a particular - component in an environment. - -.. _depl-history: - -**Deployment history** - -To see the log of a particular deployment, proceed with the steps -below: - -#. In OpenStack dashboard, navigate to :menuselection:`Applications > Catalog > - Environments`. - -#. Click the name of the required environment. - -#. Click the :guilabel:`Deployment History` tab. - -#. Find the required deployment and click :guilabel:`Show Details`. - -#. Click the :guilabel:`Logs` tab to see the logs. - - .. image:: ../figures/logs.png - :alt: Deployment Logs page - :width: 630 px - -.. _latest-log: - -**Latest deployment log** - -To see the latest deployment log, proceed with the steps below: - -#. In OpenStack dashboard, navigate to :menuselection:`Applications > Catalog > - Environments`. - -#. Click the name of the required environment. - -#. Click the :guilabel:`Latest Deployment Log` tab to see the logs. - -.. _component-logs: - -**Component logs** - -To see the logs of a particular component of an environment, proceed with the -steps below: - -#. In OpenStack dashboard, navigate to :menuselection:`Applications > Catalog > - Environments`. - -#. Click the name of the required environment. - -#. In the :guilabel:`Component List` section, click the required component. - -#. Click the :guilabel:`Logs` tab to see the component logs. - - .. image:: ../figures/env-component-logs.png - :alt: Component Logs page - :width: 630 px - -Delete an application -~~~~~~~~~~~~~~~~~~~~~ - -To delete an application that belongs to the environment: - -#. In OpenStack dashboard, navigate to :menuselection:`Applications > - Catalog > Environments`. - -#. Click on the name of the environment you want to delete an - application from. - - .. image:: ../figures/environments.png - :width: 630 px - :alt: Environments page - -#. In the :guilabel:`Component List` section, click the - :guilabel:`Delete Component` button next to the application you - want to delete. Then confirm the deletion. - - .. image:: ../figures/delete_application.png - :width: 630 px - :alt: Environment Components page - -.. note:: - If the application that you are deleting has already been deployed, - you should redeploy the environment to apply the recent changes. - If the environment has not been deployed with this component, - the changes are applied immediately on receiving the confirmation. \ No newline at end of file diff --git a/doc/source/user/userguide/manage_environments.rst b/doc/source/user/userguide/manage_environments.rst deleted file mode 100644 index be31ff95f..000000000 --- a/doc/source/user/userguide/manage_environments.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. _manage-environments: - -===================== -Managing environments -===================== - -An environment is a set of logically connected applications that are grouped -together for an easy management. By default, each environment has a single -network for all its applications, and the deployment of the environment is -defined in a single heat stack. Applications in different environments are -always independent from one another. - -An environment is a single unit of deployment. This means that you deploy not -an application but an environment that contains one or multiple applications. - -Using OpenStack dashboard you can easily perform such actions with an -environment as creating, editing, reviewing, deploying, and others. - -Create an environment -~~~~~~~~~~~~~~~~~~~~~ - -To create an environment, perform the following steps: - -#. In OpenStack dashboard, navigate to Applications > Catalog > Environments. - -#. On the :guilabel:`Environments` page, click the :guilabel:`Add New` button. - -#. In the :guilabel:`Environment Name` field, enter the name for the new - environment. - -#. From the :guilabel:`Environment Default Network` drop-down list, choose a - specific network, if necessary, or leave the default :guilabel:`Create New` - option to generate a new network. - - .. image:: ../figures/env_default_network.png - :alt: Create an environment: Environment Default Network - :width: 630 px - -#. Click the rightmost :guilabel:`Create` button. You will be redirected to - the page with the environment components. - -Alternatively, you can create an environment automatically using the -:guilabel:`Quick Deploy` button below any application in the Application -Catalog. For more information, see: :ref:`Quick deploy `. - -Edit an environment -~~~~~~~~~~~~~~~~~~~ - -You can edit the name of an environment. For this, perform the following steps: - -#. In OpenStack dashboard, navigate to Applications > Catalog > Environments. - -#. Position your mouse pointer over the environment name and click the - appeared pencil icon. - -#. Edit the name of the environment. - -#. Click the tick icon to apply the change. - -Review an environment -~~~~~~~~~~~~~~~~~~~~~ - -This section provides a general overview of an environment, its structure, -possible statuses, and actions. An environment groups applications together. -An application that is added to an environment is called a component. - -To see an environment status, navigate to :menuselection:`Applications > Catalog > Environments`. -Environments may have one of the following statuses: - -* **Ready to configure**. When the environment is new and contains no - components. -* **Ready to deploy**. When the environment contains a component or multiple - components and is ready for deployment. -* **Ready**. When the environment has been successfully deployed. -* **Deploying**. When the deploying is in progress. -* **Deploy FAILURE**. When the deployment finished with errors. -* **Deleting**. When deleting of an environment is in progress. -* **Delete FAILURE**. You can abandon the environment in this case. - -Currently, the component status corresponds to the environment status. - -To review an environment and its components, or reconfigure the environment, -click the name of an environment or simply click the rightmost -:guilabel:`Manage Components` button. - -* On the :guilabel:`Components` tab you can: - - * Add or delete a component from an environment - * Send an environment to deploy - * Track a component status - * Call murano actions of a particular application in a deployed environment: - - .. figure:: ../figures/murano_actions.png - :width: 100% - - For more information on murano actions, see: - :ref:`Murano actions `. - -* On the :guilabel:`Topology`, :guilabel:`Deployment History`, and - :guilabel:`Latest Deployment Log` tabs of the environment page you can view - the following: - - * The application topology of an environment. For more information, see: - :ref:`Application topology `. - * The log of a particular deployment. For more information, see: - :ref:`Deployment history `. - * The information on the latest deployment of an environment. For more - information, see: :ref:`Latest deployment log `. \ No newline at end of file diff --git a/doc/source/user/userguide/use_cli.rst b/doc/source/user/userguide/use_cli.rst deleted file mode 100644 index 765bf7342..000000000 --- a/doc/source/user/userguide/use_cli.rst +++ /dev/null @@ -1,587 +0,0 @@ -.. _use-cli: - -========= -Using CLI -========= - -This section provides murano end users with information on how they can use -the Application Catalog through the command-line interface (CLI). - -Using python-muranoclient, the CLI client for murano, you can easily manage -your environments, packages, categories, and deploy environments. - -.. toctree:: - :maxdepth: 1 - - install_client - -Manage environments -~~~~~~~~~~~~~~~~~~~ - -An environment is a set of logically connected applications that are grouped -together for an easy management. By default, each environment has a single -network for all its applications, and the deployment of the environment is -defined in a single heat stack. Applications in different environments are -always independent from one another. - -An environment is a single unit of deployment. This means that you deploy not -an application but an environment that contains one or multiple applications. - -Using CLI, you can easily perform such actions with an environment as -creating, renaming, editing, viewing, and others. - -Create an environment ---------------------- - -To create an environment, use the following command specifying the -environment name: - -.. code-block:: console - - $ murano environment-create - -Rename an environment ---------------------- - -To rename an environment, use the following command specifying the old name of -the environment or its ID and the new name: - -.. code-block:: console - - $ murano environment-rename - -Delete an environment ---------------------- - -To delete an environment, use the following command specifying the -environment name or ID: - -.. code-block:: console - - $ murano environment-delete - -List deployments for an environment ------------------------------------ - -To get a list of deployments for a particular environment, use the following -command specifying the environment name or ID: - -.. code-block:: console - - $ murano deployment-list - -List the environments ---------------------- - -To get a list of all existing environments, run: - -.. code-block:: console - - $ murano environment-list - -Show environment object model ------------------------------ - -To get a complete object model of the environment, run: - -.. code-block:: console - - $ murano environment-model-show - -To get some part of the environment model, run: - -.. code-block:: console - - $ murano environment-model-show --path - -For example: - - $ murano environment-model-show 534bcf2f2fc244f2b94ad55ff0f24a42 --path /defaultNetworks/environment - -To get a draft of an object model of environment in pending state, also -specify id of the session: - -.. code-block:: console - - $ murano environment-model-show --path --session-id - -Edit environment object model ------------------------------ - -To edit an object model of the environment, run: - -.. code-block:: console - - $ murano environment-model-edit --session-id - - is the path to the file with the JSON-patch to modify the object model. - -JSON-patch is a valid JSON that contains a list of changes to be applied to -the current object. Each change contains a dictionary with three keys: ``op``, -``path`` and ``value``. ``op`` (operation) can be one of the three values: -`add`, `replace` or remove`. - -Allowed operations for paths: - -* "" (model root): no operations -* "defaultNetworks": "replace" -* "defaultNetworks/environment": "replace" -* "defaultNetworks/environment/?/id": no operations -* "defaultNetworks/flat": "replace" -* "name": "replace" -* "region": "replace" -* "?/type": "replace" -* "?/id": no operations - -For other paths any operation (add, replace or remove) is allowed. - -Example of JSON-patch: - -.. code-block:: javascript - - [{ - "op": "replace", - "path": "/defaultNetworks/flat", - "value": true - }] - -The patch above changes the value of the ``flat`` property of the -environment's ``defaultNetworks`` property to `true`. - -Manage packages -~~~~~~~~~~~~~~~ - -This section describes how to manage packages using the command line -interface. You can easily: - -* :ref:`import a package ` or :ref:`bundles of packages ` -* :ref:`list the existing packages ` -* :ref:`display details for a package ` -* :ref:`download a package ` -* :ref:`delete a package ` -* :ref:`create a package ` - -.. _cli_import: - -Import a package ----------------- - -With the :command:`package-import` command you can import packages -into murano in several different ways: - -* :ref:`from a local .zip file ` -* :ref:`from murano app repository ` -* :ref:`from an http URL ` - -.. _cli_zip: - -**From a local .zip file** - -To import a package from a local .zip file, run: - -.. code-block:: console - - $ murano package-import /path/to/PACKAGE.zip - -where ``PACKAGE`` is the name of the package stored on your -computer. - -For example: - -.. code-block:: console - - $ murano package-import /home/downloads/mysql.zip - Importing package com.example.databases.MySql - +---------------------------------+------+----------------------------+--------------+---------+ - | ID | Name | FQN | Author |Is Public| - +---------------------------------+------+----------------------------+--------------+---------+ - | 83e4038885c248e3a758f8217ff8241f| MySQL| com.example.databases.MySql| Mirantis, Inc| | - +---------------------------------+------+----------------------------+--------------+---------+ - -To make the package available for users from other projects (tenants), use the -``--is-public`` parameter. For example: - -.. code-block:: console - - $ murano package-import --is-public mysql.zip - -.. note:: - - The :command:`package-import` command supports multiple positional - arguments. This means that you can import several packages at once. - -.. _cli_repo: - -**From murano app repository** - -.. |link_location| raw:: html - - murano applications repository - -To import a package from murano applications repository, specify -the URL of the repository with ``--murano-repo-url`` and a fully -qualified package name. For package names, go to |link_location|, -and click on the desired package to see its full name. - -.. note:: - - You can also specify the URL of the repository with the - corresponding MURANO_REPO_URL environment variable. - - -The following example shows how to import the MySQL package from the -murano applications repository: - -.. code-block:: console - - $ murano --murano-repo-url=http://storage.apps.openstack.org \ - package-import com.example.databases.MySql - -This command supports an optional ``--package-version`` parameter that instructs -murano client to download a specified package version. - -The :command:`package-import` command inspects package requirements -specified in the package's manifest under the *Require* section, and -attempts to import them from murano repository. The :command:`package-import` -command also inspects any image prerequisites mentioned in the -:file:`images.lst` file in the package. If there are any image -requirements, client would inspect images already present in the image -database. Unless image with the specific name is present, client would -attempt to download it. - -.. TODO: Add a ref link to step-by-step (on specifying images and requirements - for packages). - - -If any of the packages being installed is already registered in murano, -the client asks you what to do with it. You can specify the default action -with ``--exists-action``, passing ``s`` - for skip, ``u`` - for update, and -``a`` - for abort. - -.. _cli_url: - -**From an URL** - -To import an application package from an URL, use the following command: - -.. code-block:: console - - $ murano package-import http://example.com/path/to/PACKAGE.zip - -The example below shows how to import a MySQL package from the -murano applications repository using the package URL: - -.. code-block:: console - - $ murano package-import http://storage.apps.openstack.org/apps/com.example.databases.MySql.zip - Inspecting required images - Importing package com.example.databases.MySql - +----------------------------------+-------+----------------------------+--------------+--------+----------+------------+ - | ID | Name | FQN | Author | Active | Is Public| Type | - +----------------------------------+-------+----------------------------+--------------+--------+----------+------------+ - | 1aa62196595f411399e4e48cc2f6a512 | MySQL | com.example.databases.MySql| Mirantis, Inc| True | | Application| - +----------------------------------+-------+----------------------------+--------------+--------+----------+------------+ - -.. _cli_bundles: - -Import bundles of packages --------------------------- - -With the :command:`bundle-import` command you can install packages in -several different ways: - -* :ref:`from a local bundle ` -* :ref:`from an URL ` -* :ref:`from murano app repository ` - -When importing bundles, you can set their publicity with ``--is-public``. - -.. _cli_local_bundle: - -**From a local bundle** - -To import a bundle from the a local file system, use the following -command: - -.. code-block:: console - - $ murano bundle-import /path/to/bundle/BUNDLE_NAME - -This command imports all the requirements of packages and -images. - -When importing a bundle from a file system, the murano client -searches for packages in a directory relative to the bundle location -before attempting to download a package from repository. This facilitates -cases with no Internet access. - -The following example shows the import of a monitoring bundle: - -.. code-block:: console - - $ murano bundle-import /home/downloads/monitoring.bundle - Inspecting required images - Importing package com.example.ZabbixServer - Importing package com.example.ZabbixAgent - +----------------------------------+---------------+--------------------------+---------------+--------+----------+------------+ - | ID | Name | FQN | Author | Active | Is Public| Type | - +----------------------------------+---------------+--------------------------+---------------+--------+----------+------------+ - | fb0b35359e384fe18158ff3ed8f969b5 | Zabbix Agent | com.example.ZabbixAgent | Mirantis, Inc | True | | Application| - | 00a77e302a65420c8080dc97cc0f2723 | Zabbix Server | com.example.ZabbixServer | Mirantis, Inc | True | | Application| - +----------------------------------+---------------+--------------------------+---------------+--------+----------+------------+ - -.. note:: - - The :command:`bundle-import` command supports multiple positional - arguments. This means that you can import several bundles at once. - -.. _cli_bundle_url: - -**From an URL** - -To import a bundle from an URL, use the following command: - -.. code-block:: console - - $ murano bundle-import http://example.com/path/to/bundle/BUNDLE_NAME - -Where ``http://example.com/path/to/bundle/BUNDLE_NAME`` is any external http/https -URL to load the bundle from. - -For example: - -.. code-block:: console - - $ murano bundle-import http://storage.apps.openstack.org/bundles/monitoring.bundle - -.. _cli_bundle_repo: - -**From murano applications repository** - -To import a bundle from murano applications repository, use the -following command, where ``bundle_name`` stands for the bundle name: - -.. code-block:: console - - $ murano bundle-import BUNDLE_NAME - -For example: - -.. code-block:: console - - $ murano bundle-import monitoring - -.. |location| raw:: html - - murano applications repository - -.. note:: - - For bundle names, go to |location|, click the - **Format** tab to show bundles first, and then click on - the desired bundle to see its name. - -.. _cli_list: - -List packages -------------- - -To list all the existing packages you have, use the -:command:`package-list` command. The result will show you the package -ID, name, author and if it is public or not. For example: - -.. code-block:: console - - $ murano package-list - +----------------------------------+--------------------+-------------------------------------+---------------+--------+----------+------------+ - | ID | Name | FQN | Author | Active | Is Public| Type | - +----------------------------------+--------------------+-------------------------------------+---------------+--------+----------+------------+ - | daa46cfd78c74c11bcbe66d3239e546e | Apache HTTP Server | com.example.apache.ApacheHttpServer | Mirantis, Inc | True | | Application| - | 5252c9897e864c9f940e08500056f155 | Cloud Foundry | com.example.paas.CloudFoundry | Mirantis, Inc | True | | Application| - | 1aa62196595f411399e4e48cc2f6a512 | MySQL | com.example.databases.MySql | Mirantis, Inc | True | | Application| - | 11d73cfdc6d7447a910984d95090463b | SQL Library | com.example.databases | Mirantis, Inc | True | | Application| - | fb0b35359e384fe18158ff3ed8f969b5 | Zabbix Agent | com.example.ZabbixAgent | Mirantis, Inc | True | | Application| - | 00a77e302a65420c8080dc97cc0f2723 | Zabbix Server | com.example.ZabbixServer | Mirantis, Inc | True | | Application| - +----------------------------------+--------------------+-------------------------------------+---------------+--------+----------+------------+ - -.. _cli_display: - -Show packages -------------- - -To get full information about a package, use the :command:`package-show` -command. For example: - -.. code-block:: console - - $ murano package-show 1aa62196595f411399e4e48cc2f6a512 - +----------------------+-----------------------------------------------------+ - | Property | Value | - +----------------------+-----------------------------------------------------+ - | categories | | - | class_definitions | com.example.databases.MySql | - | description | MySql is a relational database management system | - | | (RDBMS), and ships with no GUI tools to administer | - | | MySQL databases or manage data contained within the | - | | databases. | - | enabled | True | - | fully_qualified_name | com.example.databases.MySql | - | id | 1aa62196595f411399e4e48cc2f6a512 | - | is_public | False | - | name | MySQL | - | owner_id | 1ddb2c610d4e4c5dab5185e32554560a | - | tags | Database, MySql, SQL, RDBMS | - | type | Application | - +----------------------+-----------------------------------------------------+ - -.. _cli_delete: - -Delete a package ----------------- - -To delete a package, use the following command: - -.. code-block:: console - - $ murano package-delete PACKAGE_ID - -.. _cli_download: - -Download a package ------------------- - -With the following command you can download a .zip archive -with a specified package: - -.. code-block:: console - - $ murano package-download PACKAGE_ID > FILE.zip - -You need to specify the package ID and enter the .zip file name -under which to save the package. - -For example: - -.. code-block:: console - - $ murano package-download e44a3f526dfb4e08b3c1018c9968d911 > Wordpress.zip - -.. _cli_create: - -Create a package ----------------- - -With the murano client you can create application packages from package -source files or directories. The :command:`package-create` command is -useful when application package files are spread across several directories. -This command has the following required parameters:: - - -r RESOURCES_DIRECTORY - -c CLASSES_DIRECTORY - --type TYPE - -o PACKAGE_NAME.zip - -f FULL_NAME - -n DISPLAY_NAME - -Example: - -.. code-block:: console - - $ murano package-create -c Downloads/Folder1/Classes -r Downloads/Folder2/Resources \ - -n mysql -f com.example.MySQL -d Package -o MySQL.zip --type Library - Application package is available at /home/Downloads/MySQL.zip - -After this, the package is ready to be imported to the application -catalog. - -The :command:`package-create` command is also useful for autogenerating -packages from heat templates. In this case you do not need to manually -specify so many parameters. For more information on automatic package -composition, please see :ref:`Automatic package composing `. - -Manage categories -~~~~~~~~~~~~~~~~~ - -In murano, applications can belong to a category or multiple categories. -Administrative users can create and delete a category as well as list -available categories and view details for a particular category. - -Create a category ------------------ - -To create a category, use the following command specifying the category name: - -.. code-block:: console - - $ murano category-create - -List available categories -------------------------- - -To get a list of all existing categories, run: - -.. code-block:: console - - $ murano category-list - -Show category details ---------------------- - -To see packages that belong to a particular category, use the following -command specifying the category ID: - -.. code-block:: console - - $ murano category-show - -Delete a category ------------------ - -To delete a category, use the following command specifying the ID of a -category or multiple categories to delete: - -.. code-block:: console - - $ murano category-delete [ ...] - -.. note:: - - Verify that no packages belong to the category to be deleted, otherwise an - error appears. For this, use the :command:`murano category-show ` - command. - -Manage environment templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To manage environment templates, use the following commands specifying -appropriate values: - -:command:`murano env-template-create ` - Creates an environment template. - -:command:`murano env-template-clone ` - Creates a new template, cloned from an existing template. - -:command:`murano env-template-create-env ` - Creates a new environment from template. - -:command:`murano env-template-add-app ` - Adds an application or multiple applications to the environment template. - -:command:`murano env-template-del-app ` - Deletes an application from the environment template. - -:command:`murano env-template-list` - Lists the environments templates. - -:command:`murano env-template-show ` - Displays environment template details. - -:command:`murano env-template-update ` - Updates an environment template. - -:command:`murano env-template-delete ` - Deletes an environment template. - -.. seealso:: - `Application Catalog service command-line client `_. diff --git a/etc/murano/README-murano.conf.txt b/etc/murano/README-murano.conf.txt deleted file mode 100644 index ed882dff7..000000000 --- a/etc/murano/README-murano.conf.txt +++ /dev/null @@ -1,4 +0,0 @@ -To generate the sample murano.conf file, run the following -command from the top level of the murano directory: - -tox -egenconfig diff --git a/etc/murano/logging.conf.sample b/etc/murano/logging.conf.sample deleted file mode 100644 index 549a95c1d..000000000 --- a/etc/murano/logging.conf.sample +++ /dev/null @@ -1,83 +0,0 @@ -[loggers] -keys: root,murano,applications - -[handlers] -keys: watchedfile, applications, stderr, stdout, null - -[formatters] -keys: context, default - -[logger_root] -level = WARNING -handlers = watchedfile - -[logger_applications] -level = DEBUG -handlers = applications -qualname = applications - -[logger_murano] -level = INFO -handlers = watchedfile -qualname = murano - -[logger_amqp] -level = WARNING -handlers = stderr -qualname = amqp - -[logger_amqplib] -level = WARNING -handlers = stderr -qualname = amqplib - -[logger_sqlalchemy] -level = WARNING -handlers = stderr -qualname = sqlalchemy -# "level = INFO" logs SQL queries. -# "level = DEBUG" logs SQL queries and results. -# "level = WARNING" logs neither. (Recommended for production systems.) - -[logger_eventletwsgi] -level = WARNING -handlers = stderr -qualname = eventlet.wsgi.server - -[logger_messaging] -level = WARNING -handlers = stderr -qualname = oslo.messaging - -[handler_null] -class = oslo_log.handlers.NullHandler -formatter = default -args = () - -[handler_stderr] -class = StreamHandler -args = (sys.stderr,) -formatter = context - -[handler_stdout] -class = StreamHandler -args = (sys.stdout,) -formatter = context - -[handler_watchedfile] -class: handlers.WatchedFileHandler -args: ('murano.log',) -formatter: context - -[handler_applications] -class: handlers.WatchedFileHandler -args: ('applications.log',) -formatter: context - -[formatter_default] -format = %(message)s -[formatter_context] -class: oslo_log.formatters.ContextFormatter -args: (datefmt=datefmt) -format: %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user)s %(tenant)s] %(instance)s%(message)s -datefmt: %Y-%m-%d %H:%M:%S diff --git a/etc/murano/murano-cfapi-paste.ini b/etc/murano/murano-cfapi-paste.ini deleted file mode 100644 index 2e603da2a..000000000 --- a/etc/murano/murano-cfapi-paste.ini +++ /dev/null @@ -1,31 +0,0 @@ -[pipeline:cloudfoundry] -pipeline = cors http_proxy_to_wsgi request_id ext_context authtoken context cloudfoundryapi - -[filter:context] -paste.filter_factory = murano.api.middleware.context:ContextMiddleware.factory - -#For more information see Auth-Token Middleware with Username and Password -#https://docs.openstack.org/keystone/latest/configuration.html#service-catalog -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory - -[app:cloudfoundryapi] -paste.app_factory = murano.cfapi.router:API.factory - -[filter:faultwrap] -paste.filter_factory = murano.api.middleware.fault:FaultWrapper.factory - -# Middleware to set x-openstack-request-id in http response header -[filter:request_id] -paste.filter_factory = oslo_middleware.request_id:RequestId.factory - -[filter:ext_context] -paste.filter_factory = murano.api.middleware.ext_context:ExternalContextMiddleware.factory - -[filter:cors] -paste.filter_factory = oslo_middleware.cors:filter_factory -oslo_config_project = murano - -[filter:http_proxy_to_wsgi] -paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory -oslo_config_project = murano diff --git a/etc/murano/murano-paste.ini b/etc/murano/murano-paste.ini deleted file mode 100644 index ed53e9e33..000000000 --- a/etc/murano/murano-paste.ini +++ /dev/null @@ -1,39 +0,0 @@ -[pipeline:murano] -pipeline = cors http_proxy_to_wsgi request_id versionnegotiation faultwrap authtoken context rootapp - -[filter:context] -paste.filter_factory = murano.api.middleware.context:ContextMiddleware.factory - -#For more information see Auth-Token Middleware with Username and Password -#https://docs.openstack.org/keystone/latest/configuration.html#service-catalog -[filter:authtoken] -paste.filter_factory = keystonemiddleware.auth_token:filter_factory - -[composite:rootapp] -use = egg:Paste#urlmap -/: apiversions -/v1: apiv1app - -[app:apiversions] -paste.app_factory = murano.api.versions:create_resource - -[app:apiv1app] -paste.app_factory = murano.api.v1.router:API.factory - -[filter:versionnegotiation] -paste.filter_factory = murano.api.middleware.version_negotiation:VersionNegotiationFilter.factory - -[filter:faultwrap] -paste.filter_factory = murano.api.middleware.fault:FaultWrapper.factory - -# Middleware to set x-openstack-request-id in http response header -[filter:request_id] -paste.filter_factory = oslo_middleware.request_id:RequestId.factory - -[filter:cors] -paste.filter_factory = oslo_middleware.cors:filter_factory -oslo_config_project = murano - -[filter:http_proxy_to_wsgi] -paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory -oslo_config_project = murano diff --git a/etc/murano/netconfig.yaml.sample b/etc/murano/netconfig.yaml.sample deleted file mode 100644 index 3dcd10f58..000000000 --- a/etc/murano/netconfig.yaml.sample +++ /dev/null @@ -1,8 +0,0 @@ -environment: - ?: - type: io.murano.resources.ExistingNeutronNetwork - internalNetworkName: internal - # internalSubnetworkName: subnet1 - # externalNetworkName: ext_net - -flat: null diff --git a/etc/oslo-config-generator/murano-cfapi.conf b/etc/oslo-config-generator/murano-cfapi.conf deleted file mode 100644 index 52a0775b4..000000000 --- a/etc/oslo-config-generator/murano-cfapi.conf +++ /dev/null @@ -1,6 +0,0 @@ -[DEFAULT] -output_file = etc/murano/murano-cfapi.conf.sample -namespace = keystone_authtoken -namespace = murano.cfapi -namespace = oslo.db -namespace = oslo.log diff --git a/etc/oslo-config-generator/murano.conf b/etc/oslo-config-generator/murano.conf deleted file mode 100644 index 66b560f35..000000000 --- a/etc/oslo-config-generator/murano.conf +++ /dev/null @@ -1,11 +0,0 @@ -[DEFAULT] -output_file = etc/murano/murano.conf.sample -namespace = keystone_authtoken -namespace = murano -namespace = oslo.db -namespace = oslo.log -namespace = oslo.messaging -namespace = oslo.middleware.cors -namespace = oslo.policy -namespace = oslo.service.service -namespace = castellan.config diff --git a/etc/oslo-policy-generator/murano-policy-generator.conf b/etc/oslo-policy-generator/murano-policy-generator.conf deleted file mode 100644 index 1d8544ea2..000000000 --- a/etc/oslo-policy-generator/murano-policy-generator.conf +++ /dev/null @@ -1,3 +0,0 @@ -[DEFAULT] -output_file = etc/murano.policy.yaml.sample -namespace = murano diff --git a/meta/README.rst b/meta/README.rst deleted file mode 100644 index 75cc04918..000000000 --- a/meta/README.rst +++ /dev/null @@ -1,11 +0,0 @@ -=================== -Murano Core Classes -=================== - -This folder contains common Murano classes combined to *Core Library*. - -The content of this folder needs to be zipped and imported into Murano. -After that Murano applications can be deployed. - -To find murano-applications and explore how the common classes are used in Murano Applications, -please refer to `Murano Application Repository `_ diff --git a/meta/io.murano.applications/Classes/baseapps.yaml b/meta/io.murano.applications/Classes/baseapps.yaml deleted file mode 100644 index 1112892a7..000000000 --- a/meta/io.murano.applications/Classes/baseapps.yaml +++ /dev/null @@ -1,85 +0,0 @@ -Namespaces: - =: io.murano.applications - res: io.murano.resources - std: io.murano - ---- # ------------------------------------------------------------------ # --- -# A base class for applications running a single software component on a single -# server only - -Name: SingleServerApplication -Extends: - - std:Application - - SoftwareComponent - -Properties: - server: - Contract: $.class(res:Instance).notNull() - - # this is "private output" property. It is not part of the class interface - # and should not be used from outside or by inheritors. It may be removed in - # future in favor of attributes - _serverGroup: - Usage: Out - Contract: $.class(SingleServerGroup) - -Methods: - .init: - Body: - - If: not $this._serverGroup - Then: - - $this._serverGroup: new(SingleServerGroup, $this, server => $this.server) - Else: - - If: $this.server != $this._serverGroup.server - Then: - - $this._serverGroup.setServer($this.server) - deploy: - Body: - - $this.deployAt($this._serverGroup) - ---- # ------------------------------------------------------------------ # --- -# A base class for applications running a single software component on multiple -# servers - -Name: MultiServerApplication -Extends: - - std:Application - - SoftwareComponent - -Properties: - servers: - Contract: $.class(ServerGroup).notNull() - -Methods: - deploy: - Body: - - $this.deployAt($this.servers) - ---- # ------------------------------------------------------------------ # --- -# A base class for applications running a single software component on multiple -# servers which should support scale-out and scale-in scenarios - -Name: MultiServerApplicationWithScaling -Extends: MultiServerApplication - -Properties: - servers: - Contract: $.class(ServerReplicationGroup).notNull() - - scaleFactor: - Contract: $.int().check($ > 0) - Default: 1 - - -Methods: - scaleOut: - Scope: Public - Body: - - $this.servers.scale($this.scaleFactor) - - $this.deploy() - - scaleIn: - Scope: Public - Body: - - $this.servers.scale(-1 * $this.scaleFactor) - - $this.deploy() diff --git a/meta/io.murano.applications/Classes/component.yaml b/meta/io.murano.applications/Classes/component.yaml deleted file mode 100644 index 97badb690..000000000 --- a/meta/io.murano.applications/Classes/component.yaml +++ /dev/null @@ -1,440 +0,0 @@ -Namespaces: - =: io.murano.applications - std: io.murano - res: io.murano.resources - m: io.murano.metadata.engine - ---- # ------------------------------------------------------------------ # --- - -Name: Installable - -Properties: - allowedInstallFailures: - Contract: $.string().notNull().check($ in ['none', 'one', 'two', 'three', 'any', 'quorum']) - Default: 'none' - - beforeInstallEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: beforeInstall - - installServerEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: installServer - - completeInstallationEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: completeInstallation - -Methods: - install: - Arguments: - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $serversToInstall: $serverGroup.getServers().pselect( - switch($this.checkServerIsInstalled($) => null, true => $)).where($ != null) - - If: any($serversToInstall) - Then: - - $.beforeInstall($serversToInstall, $serverGroup) - - $failures: $serversToInstall.pselect($this.installServer($, $serverGroup)).where($ != null) - - $.completeInstallation($serversToInstall, $serverGroup, $failures) - - checkServerIsInstalled: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance) - Body: - - Return: $server.getAttr(installed, false) - - beforeInstall: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $this.report(format('Installing {0}', name($this))) - - $this.beforeInstallEvent.notify($this, $servers, $serverGroup) - - $this.onBeforeInstall($servers, $serverGroup) - - onBeforeInstall: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - installServer: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - Try: - - $this.report(format('Began installing {0} on {1}', name($this), - $server.name)) - - $this.installServerEvent.notify($this, $server, $serverGroup) - - $this.onInstallServer($server, $serverGroup) - - Catch: - - As: e - Do: - - $this.report(format('Unable to install {0} on {1} due to {2}', - name($this), $server.name, $e.message)) - - Return: $server - Else: - - $this.report(format('{0} is installed on {1}', - name($this), - $server.name)) - - $server.setAttr(installed, true) - - Return: null - - onInstallServer: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - completeInstallation: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $success: :SoftwareComponent.detectSuccess($this.allowedInstallFailures, $serverGroup, $failedServers) - - If: $success - Then: - - $this.completeInstallationEvent.notify($this, $servers, $serverGroup, $failedServers) - - $this.onCompleteInstallation($servers, $serverGroup, $failedServers) - - - $this.report(format('Finished installing {0} ({1} errors encountered)', - name($this), len($failedServers) or 'no')) - Else: - - Throw: TooManyInstallationErrors - Message: format('Too many errors ({0}) encountered while installing {1}', - len($failedServers), name($this)) - - onCompleteInstallation: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - - report: - Arguments: - - message: - Contract: $.string().notNull() - Body: - - $env: $this.find(std:Environment) - - If: $env - Then: - - $env.reporter.report($this, $message) - ---- # ------------------------------------------------------------------ # --- - -Name: Configurable - -Properties: - allowedConfigurationFailures: - Contract: $.string().notNull().check($ in ['none', 'one', 'two', 'three', 'any', 'quorum']) - Default: 'none' - - preConfigureEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: preConfigure - - configureServerEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: configureServer - - completeConfigurationEvent: - Contract: $.class(Event).notNull() - Usage: Runtime - Default: - name: completeConfiguration - -Methods: - .init: - Body: - - $this._randomName: randomName() - - configure: - Arguments: - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - If: $this.checkClusterIsConfigured($serverGroup) - Then: - - Return: - - $serversToConfigure: $serverGroup.getServers().pselect( - switch($this.checkServerIsConfigured($, $serverGroup) => null, true => $)).where($ != null) - - $this.preConfigure($serversToConfigure, $serverGroup) - - $failures: $serversToConfigure.pselect($this.configureServer($, $serverGroup)).where($ != null) - - $.completeConfiguration($serversToConfigure, $serverGroup, $failures) - - checkClusterIsConfigured: - Arguments: - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $key: list($this.getConfigurationKey()) + $serverGroup.getKey() - - $state: $serverGroup.getAttr(configuration) - - Return: $key = $state - - checkServerIsConfigured: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $key: $this.getConfigurationKey() - - $state: $server.getAttr(configuration, null) - - Return: $key = $state - - getConfigurationKey: - Body: - # should be redefined in subclasses to contain semantical signature - # of the object's configuration - - Return: $this._randomName - - preConfigure: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $this.report(format('Applying configuration of {0}', name($this))) - - $this.configureSecurity($servers, $serverGroup) - - $this.preConfigureEvent.notify($this, $servers, $serverGroup) - - $this.onPreConfigure($servers, $serverGroup) - - configureSecurity: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - onPreConfigure: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - configureServer: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - Try: - - $this.report(format('Began configuring {0} on {1}', - name($this), $server.name)) - - $this.configureServerEvent.notify($this, $server, $serverGroup) - - $this.onConfigureServer($server, $serverGroup) - - Catch: - - As: e - Do: - - $this.report(format('Unable to configure {0} on {1} due to {2}', - name($this), $server.name, $e.message)) - - Return: $server - Else: - - $key: $this.getConfigurationKey() - - $server.setAttr(configuration, $key) - - $this.report(format('{0} is configured at {1}', name($this), $server.name)) - - Return: null - - onConfigureServer: - Meta: - - m:Synchronize: - onArgs: server - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - completeConfiguration: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $success: :SoftwareComponent.detectSuccess($this.allowedConfigurationFailures, $serverGroup, $failedServers) - - If: $success - Then: - - $this.completeConfigurationEvent.notify($this, $servers, $serverGroup, $failedServers) - - $this.onCompleteConfiguration($servers, $serverGroup, $failedServers) - - $key: list($this.getConfigurationKey()) + $serverGroup.getKey() - - $serverGroup.setAttr(configuration, $key) - - $this.report(format('Finished configuring {0} ({1} errors encountered)', - name($this), $numFailures or 'no')) - Else: - - Throw: TooManyConfigurationErrors - Message: format('Too many errors ({0}) encountered while configuring {1}', - $numFailures, name($this)) - - onCompleteConfiguration: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - - report: - Arguments: - - message: - Contract: $.string().notNull() - Body: - - $env: $this.find(std:Environment) - - If: $env - Then: - - $env.reporter.report($this, $message) - ---- # ------------------------------------------------------------------ # --- - -Name: OpenStackSecurityConfigurable -Extends: Configurable - -Methods: - configureSecurity: - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $sr: $this.getSecurityRules() - - If: $sr - Then: - - $regions: $servers.select($.getRegion()).distinct() - - $regions.pselect($this._configureSecurityGroup($, $sr)) - - _configureSecurityGroup: - Usage: Static - Arguments: - - region: - Contract: $.class(std:CloudRegion).notNull() - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - Body: - - $region.securityGroupManager.addGroupIngress($rules) - - $region.stack.push() - - getSecurityRules: - Body: - - Return: {} - ---- # ------------------------------------------------------------------ # --- -Name: SoftwareComponent -Extends: - - Installable - - Configurable - -Methods: - deployAt: - Arguments: - - serverGroup: - Contract: $.class(ServerGroup).notNull() - Body: - - $serverGroup.deploy() - - $this.install($serverGroup) - - $this.configure($serverGroup) - - report: - Arguments: - - message: - Contract: $.string().notNull() - Body: - - cast($this, Installable).report($message) - - detectSuccess: - Usage: Static - Arguments: - - allowedFailures: - Contract: $.string().notNull().check($ in ['none', 'one', 'two', 'three', 'any', 'quorum']) - - serverGroup: - Contract: $.class(ServerGroup).notNull() - - failedServers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $numFailures: len($failedServers) - - Match: - none: - - Return: $numFailures = 0 - one: - - Return: $numFailures <= 1 - two: - - Return: $numFailures <= 2 - three: - - Return: $numFailures <= 3 - any: - - Return: true - quorum: - - $numServers: $serverGroup.getServers().count() - - $maxFailures: $numServers - ($numServers/2 + 1) - - Return: $numFailures <= $maxFailures - Value: $allowedFailures diff --git a/meta/io.murano.applications/Classes/events.yaml b/meta/io.murano.applications/Classes/events.yaml deleted file mode 100644 index d73781296..000000000 --- a/meta/io.murano.applications/Classes/events.yaml +++ /dev/null @@ -1,104 +0,0 @@ - -Namespaces: - =: io.murano.applications - std: io.murano - py: # empty, for python-originating exceptions - - ---- # ------------------------------------------------------------------ # --- - -Name: Event - -Properties: - name: - Contract: $.string().notNull() - -Methods: - - .init: - Body: - - $this._handlers: {} - - subscribe: - Arguments: - - subscriber: - Contract: $.class(std:Object).notNull() - - methodName: - Contract: $.string() - Body: - - If: not $methodName - Then: - - $methodName: format('handle{0}', $this.name.substring(0,1).toUpper()+ - $this.name.substring(1)) - - - Try: - - $method: typeinfo($subscriber).methods.where($.name = $methodName).single() - Catch: - With: py:StopIteration - Do: - - Throw: NoHandlerMethodException - Message: format('Unknown method {0} for - receiver {1} to handle event {2}', - $methodName, $subscriber, $this.name) - - # This check ensures that the method passed as a handler has at least one - # standard (i.e. non vararg or kwarg) argument which is supposed to be - # "sender" object of the event. - # Although having the sender in the handler is not always nessesary it's - # still better to enforce its presence since it helps to prevent many - # hard-to-debug errors - - If: not $method.arguments.where($.usage=Standard).any() - Then: - - Throw: WrongHandlerMethodException - Message: format("Method {0} of handler {1} should accept at least - a 'sender' argument to handle event {2}", - $methodName, $subscriber, $this.name) - - $key: list($subscriber, $methodName) - - $this._handlers[$key]: $this._handlers.get($key, 0) + 1 - - unsubscribe: - Arguments: - - subscriber: - Contract: $.class(std:Object).notNull() - - methodName: - Contract: $.string() - Body: - - If: not $methodName - Then: - - $methodName: format('handle{0}', $this.name.substring(0,1).toUpper()+ - $this.name.substring(1)) - - $key: list($subscriber, $methodName) - - If: $key in $this._handlers.keys() - Then: - - $this._handlers[$key]: $this._handlers[$key] - 1 - - If: $this._handlers[$key] = 0 - Then: - - $this._handlers: $this._handlers.delete($key) - - notify: - Arguments: - - sender: - Contract: $.notNull() - - args: - Contract: $ - Usage: VarArgs - - kwargs: - Contract: $ - Usage: KwArgs - Body: - - $combinedArgs: list($sender) + $args - - $this._handlers.keys().select(call($[1], $combinedArgs, $kwargs, $[0])) - - notifyInParallel: - Arguments: - - sender: - Contract: $.notNull() - - args: - Contract: $ - Usage: VarArgs - - kwargs: - Contract: $ - Usage: KwArgs - Body: - - $combinedArgs: list($sender) + $args - - $this._handlers.keys().pselect(call($[1], $combinedArgs, $kwargs, $[0])) diff --git a/meta/io.murano.applications/Classes/replication.yaml b/meta/io.murano.applications/Classes/replication.yaml deleted file mode 100644 index aa7d06247..000000000 --- a/meta/io.murano.applications/Classes/replication.yaml +++ /dev/null @@ -1,218 +0,0 @@ -# Licensed 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. - - -Namespaces: - =: io.murano.applications - std: io.murano - ---- # ------------------------------------------------------------------ # --- - -Name: ReplicationGroup - -Properties: - provider: - Contract: $.class(ReplicaProvider).notNull() - - minItems: - Contract: $.int().notNull() - Default: 0 - - maxItems: - Contract: $.int() - Default: null - - numItems: - Usage: InOut - Contract: $.int().notNull() - Default: 1 - - items: - Usage: Out - Contract: - - $.class(std:Object) - -Methods: - deploy: - Body: - # release excessive replicas - - $oldItems: $this.items.take($this.numItems) - - $itemsToRelease: $this.items.skip($this.numItems) - - $this.provider.releaseReplicas($itemsToRelease) - - - $delta: $this.numItems - len($this.items) - - $startIndex: len($this.items) + 1 - - $endIndex: $startIndex + $delta - - $createdItems: range($startIndex, $endIndex).select( - $this.provider.createReplica($, $this)).takeWhile($ != null) - - $this.items: $oldItems + $createdItems - - scale: - Arguments: - - delta: - Contract: $.int().notNull() - Default: 1 - Body: - - $this.numItems: max($this.numItems + $delta, $this.minItems) - - If: $this.maxItems != null - Then: - - $this.numItems: min($this.numItems, $this.maxItems) - - $this.deploy() - - .destroy: - Body: - - $this.numItems: 0 - - $this.deploy() - ---- # ------------------------------------------------------------------ # --- - -Name: ReplicaProvider - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - - releaseReplicas: - Arguments: - replicas: - Contract: - - $.class(std:Object) - Body: - Return: $replicas - - ---- # ------------------------------------------------------------------ # --- - - -Name: CloneReplicaProvider -Extends: ReplicaProvider - -Properties: - template: - Contract: $.template(std:Object).notNull() - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - - Body: - - Return: new($this.template, $owner) - ---- # ------------------------------------------------------------------ # --- -# Replica provider that is a composition of other replica providers - -Name: CompositeReplicaProvider -Extends: ReplicaProvider - -Properties: - providers: - Contract: - - $.class(ReplicaProvider).notNull() - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - Body: - - Return: $this.providers.select($.createReplica($index, $owner)).where($ != null).first(null) - - releaseReplicas: - Arguments: - replicas: - Contract: - - $.class(std:Object) - Body: - - For: provider - In: $this.providers - Do: - - $replicas: $provider.releaseReplicas($replicas) - - If: $replicas = null or not $replicas.any() - Then: - Break: - - Return: $replicas - ---- # ------------------------------------------------------------------ # --- -# A replica provider from the prepopulated pool - -Name: PoolReplicaProvider -Extends: ReplicaProvider - -Properties: - pool: - Contract: - - $.class(std:Object).notNull() - - consumedItems: - Usage: Out - Contract: - - $.class(std:Object).notNull() - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - Body: - - $item: $this.pool.where(not $this.consumedItems.contains($)).first(null) - - If: $item != null - Then: - - $this.consumedItems: $this.consumedItems.append($item) - - Return: $item - - releaseReplicas: - Arguments: - replicas: - Contract: - - $.class(std:Object) - Body: - - $poolReplicas: $replicas.where($this.consumedItems.contains($)) - - $this.consumedItems: $this.consumedItems.where(not $poolReplicas.contains($)) - - Return: $replicas.where(not $poolReplicas.contains($)) - ---- # ------------------------------------------------------------------ # --- -# Replica provider with a load balancing that returns instance from the -# prepopulated list. Once the provider runs out of free items it goes to -# beginning of the list and returns the same instances again. - -Name: RoundrobinReplicaProvider -Extends: ReplicaProvider - -Properties: - items: - Contract: - - $.class(std:Object).notNull() - - 1 - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - Body: - - $index: $.getAttr(lastIndex, 0) - - $.setAttr(lastIndex, ($index + 1) mod len($this.items)) - - Return: $this.items[$index] diff --git a/meta/io.murano.applications/Classes/servers.yaml b/meta/io.murano.applications/Classes/servers.yaml deleted file mode 100644 index 754385f28..000000000 --- a/meta/io.murano.applications/Classes/servers.yaml +++ /dev/null @@ -1,244 +0,0 @@ -# Licensed 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. - - -Namespaces: - =: io.murano.applications - res: io.murano.resources - std: io.murano - ---- # ------------------------------------------------------------------ # --- -# A group of Servers - -Name: ServerGroup -Methods: - getServers: - - getKey: - Body: - - Return: $this.getServers().select(id($)).orderBy($) - - - deployServers: - Usage: Static - Arguments: - - serverGroup: - Contract: $.class(ServerGroup) - - servers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $environment: $serverGroup.find(std:Environment) - - $servers.select($this._deployServer($, $environment, $serverGroup)) - - $servers.select($.endDeploy()) - - releaseServers: - Usage: Static - Arguments: - - servers: - Contract: - - $.class(res:Instance).notNull() - Body: - - $servers.select($.beginReleaseResources()) - - $servers.select($.endReleaseResources()) - - _deployServer: - Usage: Static - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - - environment: - Contract: $.class(std:Environment) - - serverGroup: - Contract: $.class(ServerGroup) - Body: - - If: $environment and not $server.openstackId - Then: - - $environment.reporter.report($serverGroup, 'Provisioning VM for ' + (name($server) or $server.name)) - - $server.beginDeploy() - ---- # ------------------------------------------------------------------ # --- -# A group of prepopulated servers - -Name: ServerList -Extends: ServerGroup - -Properties: - servers: - Contract: - - $.class(res:Instance).notNull() - -Methods: - deploy: - Body: - - $this.deployServers($this, $this.servers) - - .destroy: - Body: - - $this.releaseServers($this.servers) - - getServers: - Body: - Return: $.servers - ---- # ------------------------------------------------------------------ # --- -# Degenrate case of a server group which consists of a single server - -Name: SingleServerGroup -Extends: ServerGroup - -Properties: - server: - Contract: $.class(res:Instance).notNull() - -Methods: - setServer: - Arguments: - - server: - Contract: $.class(res:Instance).notNull() - Body: - - $this.items: $server - - deploy: - Body: - - $this.deployServers($this, [$this.server]) - - .destroy: - Body: - - $this.releaseServers([$this.server]) - - getServers: - Body: - Return: [$.server] - ---- # ------------------------------------------------------------------ # --- -# A replication group aggregating Servers -# Adds a logic to concurrently provision and unprovision servers - - -Name: ServerReplicationGroup -Extends: - - ReplicationGroup - - ServerGroup - -Properties: - provider: - Contract: $.class(ReplicaProvider).notNull() - - items: - Usage: Out - Contract: - - $.class(res:Instance) - -Methods: - .init: - Body: - - $this._env: $.find(std:Environment).require() - - deploy: - Body: - - $delta: $this.numItems - len($this.items) - - If: abs($delta) > 1 - Then: - - $verb: switch($delta > 0 => Creating, $delta < 0 => Removing) - - $target: switch($delta > 0 => for, $delta < 0 => from) - - If: name($this) - Then: - - $target: format(' {0} {1}', $target, name($this)) - Else: - - $target: '' - - $this._env.reporter.report($this, format('{0} {1} servers{2}', - $verb, abs($delta), $target)) - - cast($this, ReplicationGroup).deploy() - - $this.deployServers($this, $this.items) - - getServers: - Body: - Return: $.items - ---- # ------------------------------------------------------------------ # --- -# A server group that composed of other server groups - -Name: CompositeServerGroup -Extends: ServerGroup - -Properties: - serverGroups: - Contract: - - $.class(ServerGroup).notNull() - -Methods: - deploy: - Body: - - $this.serverGroups.pselect($.deploy()) - - getServers: - Body: - Return: $this.serverGroups.selectMany($.getServers()) - ---- # ------------------------------------------------------------------ # --- -# A replication provider acting as a default factory class for Servers - -Name: TemplateServerProvider -Extends: ReplicaProvider - -Properties: - template: - Contract: $.template(res:Instance, excludeProperties => [name]).notNull() - - serverNamePattern: - Contract: $.string().notNull() - - allocated: - Usage: Out - Contract: $.int().notNull() - Default: 0 - - capacity: - Contract: $.int() - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int().notNull() - - owner: - Contract: $.class(std:Object) - Body: - - If: $this.capacity = null or $this.allocated < $this.capacity - Then: - - $template: $this.template - - $template.name: $this.serverNamePattern.format($index) - - $ownerGroup: $this.find(ServerGroup) - - If: $ownerGroup and name($ownerGroup) - Then: - - $groupName: format(' ({0})', name($ownerGroup)) - Else: - - $groupName: '' - - $template['?'].name: format('Server {0}{1}', $index, $groupName) - - $this.allocated: $this.allocated + 1 - - Return: new($template, $owner) - Else: - - Return: null - - releaseReplicas: - Arguments: - replicas: - Contract: - - $.class(res:Instance) - Body: - - $replicas.select($.beginReleaseResources()) - - $replicas.select($.endReleaseResources()) - - $this.allocated: max(0, $this.allocated - len($replicas)) - - Return: [] - - diff --git a/meta/io.murano.applications/Classes/tests/TestEvents.yaml b/meta/io.murano.applications/Classes/tests/TestEvents.yaml deleted file mode 100644 index fca36ab0f..000000000 --- a/meta/io.murano.applications/Classes/tests/TestEvents.yaml +++ /dev/null @@ -1,262 +0,0 @@ -Namespaces: - =: io.murano.applications.tests - tst: io.murano.test - apps: io.murano.applications - ---- # ------------------------------------------------------------------ # --- - -Name: TestSubscriber - -Properties: - called: - Usage: Runtime - Default: 0 - Contract: $.int() - - lastSender: - Usage: Runtime - Contract: $ - - lastFoo: - Usage: Runtime - Contract: $ - - lastBar: - Usage: Runtime - Contract: $ - -Methods: - handleFoo: - Arguments: - - sender: - Contract: $.notNull() - - foo: - Contract: $ - - bar: - Contract: $ - Body: - - $this.called: $this.called + 1 - - $this.lastFoo: $foo - - $this.lastBar: $bar - - $this.lastSender: $sender - - handleWithNoExtraArgs: - Arguments: - - sender: - Contract: $.notNull() - Body: - - $this.called: $this.called + 1 - - $this.lastSender: $sender - - noArgsMethod: - Body: - - varArgsKwArgsOnlyMethod: - Arguments: - - args: - Usage: VarArgs - Contract: $ - - kwargs: - Usage: KwArgs - Contract: $ - Body: - - reset: - Body: - - $this.called: 0 - - $this.lastSender: null - - $this.lastFoo: null - - $this.lastBar: null ---- # ------------------------------------------------------------------ # --- -Name: TestEmitter - -Properties: - foo: - Usage: Runtime - Contract: $.class(apps:Event).notNull() - Default: - name: foo - -Methods: - onFoo: - Body: - - $this.foo.notify($this) - - ---- # ------------------------------------------------------------------ # --- - -Name: TestEvents -Extends: tst:TestFixture - -Methods: - testSubscribeAndNotify: - Body: - - $subscriber: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber, handleFoo) - - $event.notify($this, 'Hello Events', 42) - - $this.assertEqual(1, $subscriber.called) - - $this.assertEqual('Hello Events', $subscriber.lastFoo) - - $this.assertEqual(42, $subscriber.lastBar) - - $event.notify($this) - - $this.assertEqual(2, $subscriber.called) - - testNotifyWithNoSubscribers: - Body: - - $event: new(apps:Event, name=>testEvent) - - $event.notify($this) - - testUnableToNotifyWithUnexpectedArgs: - Body: - - $subscriber: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber, handleFoo) - - $event.notify($this) - - $cought: false - - Try: - - $event.notify($this, qux=>1, baz=>2) - Catch: - With: 'yaql.language.exceptions.NoMatchingMethodException' - Do: - - $cought: true - - $this.assertTrue($cought) - - testUnsubscribe: - Body: - - $subscriber: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - $event.unsubscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - testSubscribeManyNotifyOnce: - Body: - - $subscriber: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - testUnsubscribeAsManyAsSubscribe: - Body: - - $subscriber: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.subscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - $subscriber.reset() - - $event.unsubscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - $subscriber.reset() - - $event.unsubscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - $subscriber.reset() - - $event.unsubscribe($subscriber, handleFoo) - - $event.notify($this) - - $this.assertEqual(0, $subscriber.called) - - testSubscribeSimple: - Body: - - $event: new(apps:Event, name=>foo) - - $subscriber: new(TestSubscriber) - - $event.subscribe($subscriber) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - testHandleWithNoExtraArgs: - Body: - - $event: new(apps:Event, name=>foo) - - $subscriber: new(TestSubscriber) - - $event.subscribe($subscriber, handleWithNoExtraArgs) - - $event.notify($this) - - $this.assertEqual(1, $subscriber.called) - - $this.assertEqual($this, $subscriber.lastSender) - - testUnableToSubscribeWithWrongMethod: - Body: - - $event: new(apps:Event, name=>testEvent) - - $subscriber: new(TestSubscriber) - - $cought: false - - Try: - - $event.subscribe($subscriber, handleBar) - Catch: - With: apps:NoHandlerMethodException - Do: - - $cought: true - - $this.assertTrue($cought) - - - testUnableToSubscribeWithWrongSimpleMethod: - Body: - - $event: new(apps:Event, name=>testEvent) - - $subscriber: new(TestSubscriber) - - $cought: false - - Try: - - $event.subscribe($subscriber) - Catch: - With: apps:NoHandlerMethodException - Do: - - $cought: true - - $this.assertTrue($cought) - - testUnableToSubscribeWithoutSender: - Body: - - $event: new(apps:Event, name=>testEvent) - - $subscriber: new(TestSubscriber) - - $cought: false - - Try: - - $event.subscribe($subscriber, noArgsMethod) - Catch: - With: apps:WrongHandlerMethodException - Do: - - $cought: true - - $this.assertTrue($cought) - - testUnableToSubscribeWithoutStandardArgs: - Body: - - $event: new(apps:Event, name=>testEvent) - - $subscriber: new(TestSubscriber) - - $cought: false - - Try: - - $event.subscribe($subscriber, varArgsKwArgsOnlyMethod) - Catch: - With: apps:WrongHandlerMethodException - Do: - - $cought: true - - $this.assertTrue($cought) - - testMultipleSubscribers: - Body: - - $subscriber1: new(TestSubscriber) - - $subscriber2: new(TestSubscriber) - - $event: new(apps:Event, name=>testEvent) - - $event.subscribe($subscriber1, handleFoo) - - $event.subscribe($subscriber2, handleFoo) - - $event.notify($this, 'Hello Events', 42) - - $this.assertEqual(1, $subscriber1.called) - - $this.assertEqual('Hello Events', $subscriber1.lastFoo) - - $this.assertEqual(42, $subscriber1.lastBar) - - $this.assertEqual(1, $subscriber2.called) - - $this.assertEqual('Hello Events', $subscriber2.lastFoo) - - $this.assertEqual(42, $subscriber2.lastBar) - - testEmitterWithEvent: - Body: - - $emitter: new(TestEmitter) - - $subscriber: new(TestSubscriber) - - $emitter.foo.subscribe($subscriber) - - $emitter.onFoo() - - $this.assertEqual(1, $subscriber.called) - - $this.assertEqual($emitter, $subscriber.lastSender) - diff --git a/meta/io.murano.applications/Classes/tests/TestReplication.yaml b/meta/io.murano.applications/Classes/tests/TestReplication.yaml deleted file mode 100644 index 0c1aae13c..000000000 --- a/meta/io.murano.applications/Classes/tests/TestReplication.yaml +++ /dev/null @@ -1,183 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.applications.tests - tst: io.murano.test - std: io.murano - apps: io.murano.applications - ---- # ------------------------------------------------------------------ # --- - -Name: Replica - -Properties: - name: - Contract: $.string() - ---- # ------------------------------------------------------------------ # --- - -Name: DummyReplicaProvider -Extends: apps:ReplicaProvider - -Properties: - allocated: - Usage: InOut - Contract: $.int() - Default: 0 - -Methods: - createReplica: - Arguments: - - index: - Contract: $.int() - - owner: - Contract: $.class(std:Object) - Body: - - $replica: new(Replica, name => format('replica-{0}', $index)) - - $this.allocated: $this.allocated + 1 - - Return: $replica - - releaseReplicas: - Arguments: - replicas: - Contract: - - $ - Body: - - $this.allocated: $this.allocated - len($replicas) - - ---- # ------------------------------------------------------------------ # --- - -Name: TestReplication -Extends: tst:TestFixture - -Methods: - setUp: - Body: - - $this.provider: new(DummyReplicaProvider) - - testCreateDefault: - Body: - - $group: new(apps:ReplicationGroup, provider => $this.provider) - - $group.deploy() - - $.assertEqual(1, len($group.items)) - - $.assertEqual(1, $this.provider.allocated) - - $.assertEqual('replica-1', $group.items[0].name) - - testCreateMultiple: - Body: - - $group: new(apps:ReplicationGroup, provider => $this.provider, numItems => 5) - - $group.deploy() - - $.assertEqual(5, len($group.items)) - - - testScale: - Body: - - $group: new(apps:ReplicationGroup, provider => $this.provider) - - $group.deploy() - - $.assertEqual(1, len($group.items)) - - $group.scale(1) - - $.assertEqual(2, len($group.items)) - - $.assertEqual(2, $this.provider.allocated) - - $group.scale(-1) - - $.assertEqual(1, len($group.items)) - - $.assertEqual(1, $this.provider.allocated) - ---- # ------------------------------------------------------------------ # --- - -Name: TestPoolReplicaProvider -Extends: tst:TestFixture - -Methods: - setUp: - Body: - - $this.object1: new(std:Object) - - $this.object2: new(std:Object) - - $this.provider: new(apps:PoolReplicaProvider, pool => [$this.object1, $this.object2]) - - testReplicas: - Body: - - $.assertEqual(2, len($this.provider.pool)) - - $.assertEqual(0, len($this.provider.consumedItems)) - - $obj: $this.provider.createReplica(1, $this) - - $.assertEqual($this.object1, $obj) - - $.assertEqual(2, len($this.provider.pool)) - - $.assertEqual(1, len($this.provider.consumedItems)) - - $obj: $this.provider.createReplica(2, $this) - - $.assertEqual($this.object2, $obj) - - $.assertEqual(2, len($this.provider.pool)) - - $.assertEqual(2, len($this.provider.consumedItems)) - - $obj: $this.provider.createReplica(3, $this) - - $.assertEqual(null, $obj) - - $.assertEqual(2, len($this.provider.pool)) - - $.assertEqual(2, len($this.provider.consumedItems)) - - testReleaseReplicas: - Body: - - $obj: $this.provider.createReplica(1, $this) - - $.assertEqual(1, len($this.provider.consumedItems)) - - $foreignObj: new(std:Object) - - $res: $this.provider.releaseReplicas([$obj, $this.object1, $this.object2, $foreignObj]) - - $.assertEqual(0, len($this.provider.consumedItems)) - - $.assertEqual([$this.object2, $foreignObj], $res) - - $this.testReplicas() - ---- # ------------------------------------------------------------------ # --- - -Name: TestRoundrobinReplicaProvider -Extends: tst:TestFixture - -Methods: - setUp: - Body: - - $this.object1: new(std:Object) - - $this.object2: new(std:Object) - - $this.provider: new(apps:RoundrobinReplicaProvider, items => [$this.object1, $this.object2]) - - testReplicas: - Body: - - $obj: $this.provider.createReplica(1, $this) - - $.assertEqual($this.object1, $obj) - - $obj: $this.provider.createReplica(2, $this) - - $.assertEqual($this.object2, $obj) - - $obj: $this.provider.createReplica(3, $this) - - $.assertEqual($this.object1, $obj) - - $obj: $this.provider.createReplica(4, $this) - - $.assertEqual($this.object2, $obj) - ---- # ------------------------------------------------------------------ # --- - -Name: TestCompositeReplicaProvider -Extends: tst:TestFixture - -Methods: - setUp: - Body: - - $this.objects: range(4).select(new(std:Object)) - - $this.object2: new(std:Object) - - $this.provider1: new(apps:PoolReplicaProvider, pool => [$this.objects[0], $this.objects[1]]) - - $this.provider2: new(apps:RoundrobinReplicaProvider, items => [$this.objects[2], $this.objects[3]]) - - $this.provider: new(apps:CompositeReplicaProvider, providers => [$this.provider1, $this.provider2]) - - testReplicas: - Body: - - $obj: $this.provider.createReplica(1, $this) - - $.assertEqual($this.objects[0], $obj) - - $obj: $this.provider.createReplica(2, $this) - - $.assertEqual($this.objects[1], $obj) - - $obj: $this.provider.createReplica(3, $this) - - $.assertEqual($this.objects[2], $obj) - - $obj: $this.provider.createReplica(4, $this) - - $.assertEqual($this.objects[3], $obj) - - $obj: $this.provider.createReplica(5, $this) - - $.assertEqual($this.objects[2], $obj) diff --git a/meta/io.murano.applications/Classes/tests/TestServerProviders.yaml b/meta/io.murano.applications/Classes/tests/TestServerProviders.yaml deleted file mode 100644 index 7b44be115..000000000 --- a/meta/io.murano.applications/Classes/tests/TestServerProviders.yaml +++ /dev/null @@ -1,173 +0,0 @@ -Namespaces: - =: io.murano.applications.tests - tst: io.murano.test - apps: io.murano.applications - sys: io.murano.system - res: io.murano.resources - ---- # ------------------------------------------------------------------ # --- -Name: TestMockedServerFactory -Extends: tst:TestFixtureWithEnvironment - -Properties: - reports: - Contract: - - $.string() - Usage: Out - Default: [] - -Methods: - report: - Arguments: - - server: - Contract: $ - - message: - Contract: $.string() - Body: - - $this.reports: $this.reports.append($message) - - heatPushInjection: - Body: - - $this.currentTemplate: $this.environment.stack.current() - - $this.pushCalled: $this.pushCalled + 1 - - - heatOutputInjection: - Body: - # generate simulated output - - $outputKeys: $this.currentTemplate.outputs.keys() - - $idIndex: 1000 - - $ipIndex: 100 - - $ipPrefix: '10.0.0.' - - $defaultNetworkName: testNetwork - - $output: {} - - For: key - In: $outputKeys - Do: - # if output name ends with -id then it is some openstack-id of a resource - - If: $key.endsWith('-id') - Then: - - $output[$key]: $idIndex - - $idIndex: $idIndex + 1 - - # if output name ends with -assigned-ips then it is some ip of a vm - - If: $key.endsWith('-assigned-ips') - Then: - - $output[$key]: - $defaultNetworkName: list(format('{0}{1}', $ipPrefix, $ipIndex)) - - $ipIndex: $ipIndex + 1 - - Return: $output - - neutronListExtensionsInjection: - Body: - - Return: - - alias: 'security-group' - - - setUp: - Body: - - $this.currentTemplate: {} - - inject(sys:NetworkExplorer, listNeutronExtensions, $this, neutronListExtensionsInjection) - - inject(sys:NetworkExplorer, getDefaultRouter, '42') - - inject(sys:NetworkExplorer, getAvailableCidr, '10.0.0.0/24') - - super($this, $.setUp()) - - inject($this.environment.stack, push, $this, heatPushInjection) - - inject($this.environment.stack, output, $this, heatOutputInjection) - - inject($this.environment.stack, delete, '') - - inject(sys:Agent, prepare, '') - - inject($this.environment.instanceNotifier, trackCloudInstance, '') - - inject($this.environment.instanceNotifier, untrackCloudInstance, '') - - $this.reports: [] - - inject($this.environment.reporter, report, $this, report) - - $this.pushCalled: 0 - - - - $serverTemplate: - image: 'murano-latest' - flavor: 't1.medium' - - $this.provider: new(apps:TemplateServerProvider, - template => $serverTemplate, - serverNamePattern => 'testNode-{0}') - - testCreateSingleServer: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, - provider => $this.provider) - - $ssg.deploy() - - $this.assertServerCount(1) - - testServersHaveProperName: - Body: - - $model: - apps:ServerReplicationGroup: - numItems: 2 - provider: - apps:TemplateServerProvider: - template: $this.provider.template - serverNamePattern: 'testNode-{0}' - name: testGroup - - $namedSsg: new($model, $this.environment) - - $namedSsg.deploy() - - $this.assertEqual('Server 1 (testGroup)', name($namedSsg.items[0])) - - $this.assertEqual('Server 2 (testGroup)', name($namedSsg.items[1])) - - - testCreateMultipleServers: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, - provider => $this.provider, numItems => 5) - - $ssg.deploy() - - $this.assertServerCount(5) - - testCreateScaleUp: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, - provider => $this.provider, numItems => 3) - - $ssg.deploy() - - $this.assertServerCount(3) - - $ssg.scale(4) - - $ssg.deploy() - - $this.assertServerCount(7) - - testCreateScaleDown: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, provider => $this.provider, numItems => 3) - - $ssg.deploy() - - $this.assertServerCount(3) - - $ssg.scale(-2) - - $ssg.deploy() - - $this.assertServerCount(1) - - testMultipleServersReporting: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, TestGroup, - provider => $this.provider, numItems => 3) - - $ssg.deploy() - - $this.assertEqual('Creating 3 servers for TestGroup', $this.reports[0]) - - $ssg.scale(-2) - - $this.assertEqual('Removing 2 servers from TestGroup', $this.reports[4]) - - testMultipleServersReportingNoGroupName: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, null, - provider => $this.provider, numItems => 3) - - $ssg.deploy() - - $this.assertEqual('Creating 3 servers', $this.reports[0]) - - $ssg.scale(-2) - - $this.assertEqual('Removing 2 servers', $this.reports[4]) - - testNoReportingIfSingleServer: - Body: - - $ssg: new(apps:ServerReplicationGroup, $this.environment, TestGroup, - provider => $this.provider, numItems => 1) - - $ssg.deploy() - - $this.assertEqual(1, len($this.reports)) - - - assertServerCount: - Arguments: - - count: - Contract: $.int() - Body: - - $this.assertEqual($count, $this.currentTemplate.resources.values().where( - $.type = 'OS::Nova::Server').len()) diff --git a/meta/io.murano.applications/Classes/tests/TestSoftwareComponent.yaml b/meta/io.murano.applications/Classes/tests/TestSoftwareComponent.yaml deleted file mode 100644 index 04c06a163..000000000 --- a/meta/io.murano.applications/Classes/tests/TestSoftwareComponent.yaml +++ /dev/null @@ -1,119 +0,0 @@ -Namespaces: - =: io.murano.applications.tests - tst: io.murano.test - apps: io.murano.applications - res: io.murano.resources - ---- # ------------------------------------------------------------------ # --- -Name: InstallableToTest -Extends: - - apps:Installable - ---- # ------------------------------------------------------------------ # --- - -Name: ConfigurableToTest -Extends: - - apps:Configurable - ---- # ------------------------------------------------------------------ # --- - -Name: SoftwareComponentToTest -Extends: - - apps:SoftwareComponent - - ---- # ------------------------------------------------------------------ # --- -Name: TestSoftwareComponent -Extends: tst:TestFixtureWithEnvironment - -Properties: - reports: - Contract: - - $.string() - Usage: Out - Default: [] - -Methods: - report: - Arguments: - - server: - Contract: $ - - message: - Contract: $.string() - Body: - - $this.reports: $this.reports.append($message) - - setUp: - Body: - - super($this, $.setUp()) - - inject(res:LinuxMuranoInstance, beginDeploy, '') - - inject(res:LinuxMuranoInstance, endDeploy, '') - - $server: new(res:LinuxMuranoInstance, $this.environment, - name => 'noop', - image => 'noop', - flavor => 'noop') - - $provider: new(apps:TemplateServerProvider, $this.environment, - template => $server, serverNamePattern => 'testNode-{0}') - - $this.group: new(apps:ServerReplicationGroup, $this.environment, provider => $provider, numItems => 5) - - $this.group.deploy() - - $this.reports: [] - - inject($this.environment.reporter, report, $this, report) - - testInstallReportingSequence: - Body: - - $cmp: new(InstallableToTest, $this.environment, testComp) - - $cmp.install($this.group) - - $this.assertInstallingSequence(0) - - testConfigureReportingSequence: - Body: - - $cmp: new(ConfigurableToTest, $this.environment, testComp) - - $cmp.configure($this.group) - - $this.assertConfiguringSequence(0) - - testCombinedSequence: - Body: - - $cmp: new(SoftwareComponentToTest, $this.environment, testComp) - - $cmp.deployAt($this.group) - - $this.assertProvisioningSequence(0) - - $this.assertInstallingSequence($this.group.numItems) - - $this.assertConfiguringSequence(3 * $this.group.numItems + 2) - - assertProvisioningSequence: - Arguments: - - offset: - Contract: $.int().notNull() - Body: - - $this.assertEqual( - range(1, $this.group.numItems + 1).select('Provisioning VM for Server {0}'.format($)), - $this.reports.skip($offset).take($this.group.numItems)) - - - assertInstallingSequence: - Arguments: - - offset: - Contract: $.int().notNull() - Body: - - $this.assertEqual('Installing testComp', $this.reports[$offset]) - - $nodeReports: range(0, $this.group.numItems * 2).select($this.reports[$offset + 1 + $]) - - range(1, $this.group.numItems + 1).select( - $this.assertTrue(format('Began installing testComp on testNode-{0}', $) in $nodeReports)) - - range(1, $this.group.numItems + 1).select( - $this.assertTrue(format('testComp is installed on testNode-{0}', $) in $nodeReports)) - - $this.assertEqual('Finished installing testComp (no errors encountered)', - $this.reports[$offset + 2 * $this.group.numItems + 1]) - - assertConfiguringSequence: - Arguments: - - offset: - Contract: $.int().notNull() - Body: - - $this.assertEqual('Applying configuration of testComp', $this.reports[$offset]) - - $nodeReports: range(0, $this.group.numItems * 2).select($this.reports[$offset + 1 + $]) - - range(1, $this.group.numItems + 1).select( - $this.assertTrue(format('Began configuring testComp on testNode-{0}', $) in $nodeReports)) - - range(1, $this.group.numItems + 1).select( - $this.assertTrue(format('testComp is configured at testNode-{0}', $) in $nodeReports)) - - $this.assertEqual('Finished configuring testComp (no errors encountered)', - $this.reports[$offset + 2 * $this.group.numItems + 1]) - diff --git a/meta/io.murano.applications/LICENSE b/meta/io.murano.applications/LICENSE deleted file mode 100644 index 67db85882..000000000 --- a/meta/io.murano.applications/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/meta/io.murano.applications/manifest.yaml b/meta/io.murano.applications/manifest.yaml deleted file mode 100644 index 12bf371d7..000000000 --- a/meta/io.murano.applications/manifest.yaml +++ /dev/null @@ -1,59 +0,0 @@ -# Licensed 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. - -Format: 1.4 - -Type: Library - -FullName: io.murano.applications - -Name: Application Development Library - -Description: | - Library of base class to develop scalable Applications with MuranoPL - -Author: Mirantis, Inc. - -Classes: - io.murano.applications.ReplicaProvider: replication.yaml - io.murano.applications.ReplicationGroup: replication.yaml - io.murano.applications.CloneReplicaProvider: replication.yaml - io.murano.applications.CompositeReplicaProvider: replication.yaml - io.murano.applications.PoolReplicaProvider: replication.yaml - io.murano.applications.RoundrobinReplicaProvider: replication.yaml - - io.murano.applications.Event: events.yaml - - io.murano.applications.ServerGroup: servers.yaml - io.murano.applications.ServerList: servers.yaml - io.murano.applications.CompositeServerGroup: servers.yaml - io.murano.applications.SingleServerGroup: servers.yaml - io.murano.applications.ServerReplicationGroup: servers.yaml - io.murano.applications.TemplateServerProvider: servers.yaml - - io.murano.applications.Installable: component.yaml - io.murano.applications.Configurable: component.yaml - io.murano.applications.OpenStackSecurityConfigurable: component.yaml - io.murano.applications.SoftwareComponent: component.yaml - - io.murano.applications.SingleServerApplication: baseapps.yaml - io.murano.applications.MultiServerApplication: baseapps.yaml - io.murano.applications.MultiServerApplicationWithScaling: baseapps.yaml - - # Tests - io.murano.applications.tests.TestReplication: tests/TestReplication.yaml - io.murano.applications.tests.TestPoolReplicaProvider: tests/TestReplication.yaml - io.murano.applications.tests.TestRoundrobinReplicaProvider: tests/TestReplication.yaml - io.murano.applications.tests.TestCompositeReplicaProvider: tests/TestReplication.yaml - io.murano.applications.tests.TestEvents: tests/TestEvents.yaml - io.murano.applications.tests.TestMockedServerFactory: tests/TestServerProviders.yaml - io.murano.applications.tests.TestSoftwareComponent: tests/TestSoftwareComponent.yaml diff --git a/meta/io.murano/Classes/Application.yaml b/meta/io.murano/Classes/Application.yaml deleted file mode 100644 index 7d774660f..000000000 --- a/meta/io.murano/Classes/Application.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - -Name: Application - -Methods: - reportDeployed: - Arguments: - - title: - Contract: $.string() - Default: null - - unitCount: - Contract: $.int() - Default: null - Body: - - $this.find(Environment).instanceNotifier.trackApplication($this, $title, $unitCount) - - reportDestroyed: - Body: - - $this.find(Environment).instanceNotifier.untrackApplication($this) - - deploy: \ No newline at end of file diff --git a/meta/io.murano/Classes/CloudRegion.yaml b/meta/io.murano/Classes/CloudRegion.yaml deleted file mode 100644 index 4ed343623..000000000 --- a/meta/io.murano/Classes/CloudRegion.yaml +++ /dev/null @@ -1,57 +0,0 @@ -Namespaces: - res: io.murano.resources - sys: io.murano.system - =: io.murano - -Name: CloudRegion - -Properties: - name: - Contract: $.string() - - agentListener: - Contract: $.class(sys:AgentListener) - Usage: Runtime - - stack: - Contract: $.class(sys:HeatStack) - Usage: Runtime - - defaultNetworks: - Contract: - environment: $.class(res:Network) - flat: $.class(res:Network) - - securityGroupManager: - Contract: $.class(sys:SecurityGroupManager) - Usage: Runtime - -Methods: - getConfig: - Body: - - Return: $._environment.regionConfigs.get( - $.name, $._environment.regionConfigs.get('')) - - .init: - Body: - - $._environment: $.find(Environment).require() - - $generatedStackName: $.getAttr(generatedStackName) - - If: $generatedStackName = null - Then: - - $generatedStackName: list($.name, randomName()).join('-') - - $.setAttr(generatedStackName, $generatedStackName) - - $this.agentListener: new(sys:AgentListener, $this, name => $generatedStackName) - - $stackDescriptionFormat: 'This stack was generated by Murano for environment {0} (ID: {1}) - region {2}' - - $this.stack: new(sys:HeatStack, - regionName => $.name, - name => 'murano-' + $generatedStackName, - description => $stackDescriptionFormat.format($._environment.name, id($._environment), $.name)) - - sys:GC.subscribeDestruction($this, $this.stack) - - - $this.securityGroupManager: - coalesce($.defaultNetworks.environment, $.defaultNetworks.flat)?. - generateSecurityGroupManager() - - .destroy: - Body: - - $.stack.delete() diff --git a/meta/io.murano/Classes/CloudResource.yaml b/meta/io.murano/Classes/CloudResource.yaml deleted file mode 100644 index be1b1f0db..000000000 --- a/meta/io.murano/Classes/CloudResource.yaml +++ /dev/null @@ -1,33 +0,0 @@ -Namespaces: - =: io.murano - sys: io.murano.system - -Name: CloudResource - -Properties: - regionName: - Contract: $.string() - -Methods: - .init: - Body: - $._region: null - - getRegion: - Meta: - 'io.murano.metadata.engine.Synchronize': - onThis: false - Body: - - If: $._region = null - Then: - - $env: $.find(Environment).require() - - $regionName: generate($this, $ != null, $.find(CloudResource)). - select($.regionName).where($ != null).first($env.region) - - $._region: $.find(CloudRegion) - - If: $._region = null or $._region.name != $regionName - Then: - $._region: $env.regions[$regionName] - - If: $._region != null - Then: - - sys:GC.subscribeDestruction($this, $._region) - - Return: $._region diff --git a/meta/io.murano/Classes/Environment.yaml b/meta/io.murano/Classes/Environment.yaml deleted file mode 100644 index 91bb399e3..000000000 --- a/meta/io.murano/Classes/Environment.yaml +++ /dev/null @@ -1,143 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - res: io.murano.resources - sys: io.murano.system - -Name: Environment - -Properties: - name: - Contract: $.string().notNull() - - applications: - Contract: [$.class(Application).owned().notNull()] - - agentListener: - Contract: $.class(sys:AgentListener) - Usage: Runtime - - stack: - Contract: $.class(sys:HeatStack) - Usage: Runtime - - instanceNotifier: - Contract: $.class(sys:InstanceNotifier) - Usage: Runtime - - defaultNetworks: - Contract: - environment: $.template(res:Network) - flat: $.template(res:Network) - - securityGroupManager: - Contract: $.class(sys:SecurityGroupManager) - Usage: Runtime - - reporter: - Contract: $.class(sys:StatusReporter) - Usage: Runtime - - regionConfigs: - Contract: - $.string(): - agentRabbitMq: - host: $.string().notNull() - port: $.int() or 5672 - login: $.string().notNull() - password: $.string().notNull() - virtual_host: $.string() or '/' - ssl: $.bool() or false - insecure: $.bool() or false - Usage: Config - - region: - Contract: $.string() - Usage: InOut - - homeRegionName: - Contract: $.string() - Usage: Runtime - - regions: - Contract: - $.string(): $.class(CloudRegion) - Usage: InOut - - -Methods: - .init: - Body: - - $.homeRegionName: config(home_region) or '' - - $._assignRegions() - - $.instanceNotifier: new(sys:InstanceNotifier, environment => $this) - - $.reporter: new(sys:StatusReporter, environment => $this) - - $.regions: $.regions + $.regionConfigs.keys(). - where($ and not $this.regions.containsKey($)). - select($this._createRegion($)). - toDict($.name) - - If: not $.regions.containsKey('') - Then: - - If: $.homeRegionName - Then: - $.regions['']: $.regions[$.homeRegionName] - Else: - $.regions['']: $._createRegion('') - - - $defaultRegion: $.regions[''] - - $.stack: $defaultRegion.stack - - $.securityGroupManager: $defaultRegion.securityGroupManager - - _createRegion: - Arguments: - regionName: - Contract: $.string() - Body: - - $envNet: $.defaultNetworks.environment?.set(regionName => $regionName) - - $flatNet: $.defaultNetworks.flat?.set(regionName => $regionName) - - Return: new(CloudRegion, $this, - name => $regionName, - defaultNetworks => { - environment => $envNet, - flat => $flatNet - } - ) - - deploy: - Scope: Public - Body: - - $.applications.pselect($.deploy()) - - _assignRegions: - Body: - - If: $.region = null - Then: - $.region: $.homeRegionName - - - $defaultRegionConfig: - agentRabbitMq: - host: config(rabbitmq, host) - port: config(rabbitmq, port) - login: config(rabbitmq, login) - password: config(rabbitmq, password) - virtual_host: config(rabbitmq, virtual_host) - ssl: config(rabbitmq, ssl) - - - If: not $.regionConfigs.containsKey('') - Then: - - $.regionConfigs: $.regionConfigs.set('' => $defaultRegionConfig) - - - If: $.homeRegionName and not $.regionConfigs.containsKey($.homeRegionName) - Then: - - $.regionConfigs: $.regionConfigs.set($.homeRegionName => $defaultRegionConfig) diff --git a/meta/io.murano/Classes/Exception.yaml b/meta/io.murano/Classes/Exception.yaml deleted file mode 100644 index 54a3e9d4f..000000000 --- a/meta/io.murano/Classes/Exception.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - -Name: Exception - -Properties: - name: - Contract: $.string() - Usage: Runtime - - message: - Contract: $.string() - Usage: Runtime - - stackTrace: - Contract: $ - Usage: Runtime - - extra: - Contract: {} - Usage: Runtime - - nativeException: - Contract: $ - Usage: Runtime - - cause: - Contract: $.class(Exception) - Usage: Runtime diff --git a/meta/io.murano/Classes/File.yaml b/meta/io.murano/Classes/File.yaml deleted file mode 100644 index d65b2f4a9..000000000 --- a/meta/io.murano/Classes/File.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - -Name: File - -Properties: - base64Content: - Contract: $.string().notNull() - Default: '' - - mimeType: - Contract: $.string().notNull() - Default: 'application/octet-stream' - - filename: - Contract: $.string() diff --git a/meta/io.murano/Classes/Object.yaml b/meta/io.murano/Classes/Object.yaml deleted file mode 100644 index 6d53173a9..000000000 --- a/meta/io.murano/Classes/Object.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Licensed 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. - -Name: io.murano.Object diff --git a/meta/io.murano/Classes/Project.yaml b/meta/io.murano/Classes/Project.yaml deleted file mode 100644 index 553d683b8..000000000 --- a/meta/io.murano/Classes/Project.yaml +++ /dev/null @@ -1,14 +0,0 @@ -Name: io.murano.Project - -Properties: - id: - Contract: $.string().notNull() - name: - Contract: $.string().notNull() - domain: - Contract: $.string().notNull() - description: - Contract: $.string() - extra: - Contract: - $.string().notNull(): $ diff --git a/meta/io.murano/Classes/SharedIp.yaml b/meta/io.murano/Classes/SharedIp.yaml deleted file mode 100644 index 05384d6a4..000000000 --- a/meta/io.murano/Classes/SharedIp.yaml +++ /dev/null @@ -1,136 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - res: io.murano.resources - -Name: SharedIp -Extends: CloudResource - -Properties: - assignFloatingIp: - Contract: $.bool().notNull() - Default: false - virtualIp: - Contract: $.string() - Usage: Out - floatingIpAddress: - Contract: $.string() - Usage: Out - network: - Contract: $.class(res:Network) - Usage: InOut - - -Methods: - initialize: - Body: - - $._environment: $.find(Environment).require() - - $.instances: [] - - - deployNetwork: - Body: - - If: $.network = null - Then: - $.network: $.getRegion().defaultNetworks.environment - - - $.network.deploy() - - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $region: $.getRegion() - - $reporter: $._environment.reporter - - - $.deployNetwork() - - $networkData: $.network.describe() - - - $aapPortName: format('AllowedAddressPairsPort-{0}', id($)) - - $template: - resources: - $aapPortName: - type: 'OS::Neutron::Port' - properties: - network_id: $networkData.netId - replacement_policy: AUTO - outputs: - $aapPortName+'-virtualIp': - value: - get_attr: [$aapPortName, fixed_ips, 0, ip_address] - description: format('SharedIP Address of SharedIp group {0}', id($)) - - If: $networkData.subnetId - Then: - - $t: - resources: - $aapPortName: - properties: - fixed_ips: - - subnet_id: $networkData.subnetId - - $template: $template.mergeWith($t) - - $region.stack.updateTemplate($template) - - If: $.assignFloatingIp and $networkData.floatingIpNetId - Then: - - $extNetId: $networkData.floatingIpNetId - - $fipName: format('Shared-Floating-ip-{0}', id($)) - - - $template: - resources: - $fipName: - type: 'OS::Neutron::FloatingIP' - properties: - floating_network_id: $extNetId - port_id: - get_resource: $aapPortName - outputs: - $fipName + '-val': - value: - get_attr: [$fipName, floating_ip_address] - description: Shared Floating IP assigned - - $region.stack.updateTemplate($template) - - - $reporter.report($this, 'Allocating shared ip address') - - $region.stack.push() - - $outputs: $region.stack.output() - - $.virtualIp: $outputs.get(format('AllowedAddressPairsPort-{0}-virtualIp', id($))) - - $.floatingIpAddress: $outputs.get(format('Shared-Floating-ip-{0}-val', id($))) - - $reporter.report($this, format('Shared IP allocated at {0}', $.virtualIp)) - - If: $.assignFloatingIp - Then: - - $reporter.report($this, format('Floating shared IP is {0}', $.floatingIpAddress)) - - $.setAttr(deployed, true) - - - getSharedIpRef: - Body: - - $aapPortName: format('AllowedAddressPairsPort-{0}', id($)) - - Return: - get_attr: [$aapPortName, fixed_ips, 0, ip_address] - - - releaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - $template.resources: $template.resources.delete(format('AllowedAddressPairsPort-{0}', id($))) - - $template.outputs: $template.outputs.delete(format('AllowedAddressPairsPort-{0}-virtualIp', id($))) - - If: $.assignFloatingIp - Then: - - $template.resources: $template.resources.delete(format('Shared-Floating-ip-{0}', id($))) - - $template.outputs: $template.outputs.delete(format('Shared-Floating-ip-{0}-val', id($))) - - $region.stack.setTemplate($template) - - $region.stack.push() - - $.floatingIpAddress: null - - $.virtualIp: null diff --git a/meta/io.murano/Classes/SharedIpRange.yaml b/meta/io.murano/Classes/SharedIpRange.yaml deleted file mode 100644 index 4d10851b2..000000000 --- a/meta/io.murano/Classes/SharedIpRange.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - -Name: SharedIpRange -Extends: SharedIp - -Properties: - cidr: - Contract: $.string().notNull() - Usage: InOut - - -Methods: - initialize: - Body: - - $._environment: $.find(Environment).require() - - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $region: $.getRegion() - - $reporter: $._environment.reporter - - - $.deployNetwork() - - $networkData: $.network.describe() - - - $aapSubnetName: format('AllowedAddressPairsSubnet-{0}', id($)) - - $template: - resources: - $aapSubnetName: - type: 'OS::Neutron::Subnet' - properties: - enable_dhcp: false - network: $networkData.netId - cidr: $.cidr - outputs: - $aapSubnetName+'-cidr': - value: - get_attr: [$aapSubnetName, cidr] - description: format('Shared IP Range of group {0}', id($)) - - - $region.stack.updateTemplate($template) - - - $region.stack.push() - - $outputs: $region.stack.output() - - $.cidr: $outputs.get(format('AllowedAddressPairsSubnet-{0}-cidr', id($))) - - $.virtualIp: $outputs.get(format('AllowedAddressPairsSubnet-{0}-cidr', id($))) - - $reporter.report($this, format('Shared IP Range allocated at {0}', $.cidr)) - - $.setAttr(deployed, true) - - - getSharedIpRef: - Body: - - $aapSubnetName: format('AllowedAddressPairsSubnet-{0}', id($)) - - Return: - get_attr: [$aapSubnetName, cidr] - - - releaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - $template.resources: $template.resources.delete(format('AllowedAddressPairsSubnet-{0}', id($))) - - $template.outputs: $template.outputs.delete(format('AllowedAddressPairsSubnet-{0}-cidr', id($))) - - $region.stack.setTemplate($template) - - $region.stack.push() - - $.cidr: null \ No newline at end of file diff --git a/meta/io.murano/Classes/StackTrace.yaml b/meta/io.murano/Classes/StackTrace.yaml deleted file mode 100644 index ad375f9bb..000000000 --- a/meta/io.murano/Classes/StackTrace.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano - -Name: StackTrace - -Properties: - frames: - Contract: - - instruction: $.string() - location: $ - methodName: $ - typeName: $ diff --git a/meta/io.murano/Classes/User.yaml b/meta/io.murano/Classes/User.yaml deleted file mode 100644 index ea203adb9..000000000 --- a/meta/io.murano/Classes/User.yaml +++ /dev/null @@ -1,14 +0,0 @@ -Name: io.murano.User - -Properties: - id: - Contract: $.string().notNull() - name: - Contract: $.string().notNull() - domain: - Contract: $.string().notNull() - email: - Contract: $.string() - extra: - Contract: - $.string().notNull(): $ diff --git a/meta/io.murano/Classes/configuration/Linux.yaml b/meta/io.murano/Classes/configuration/Linux.yaml deleted file mode 100644 index 5cbc38758..000000000 --- a/meta/io.murano/Classes/configuration/Linux.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.configuration - std: io.murano - sys: io.murano.system - m: io.murano.metadata.engine - -Name: Linux - -Methods: - runCommand: - Meta: - - m:Synchronize: - onArgs: agent - Usage: Static - Arguments: - - agent: - Contract: $.class(sys:Agent) - - command: - Contract: $.string().notNull() - - helpText: - Contract: $.string() - Default: null - - captureStderr: - Contract: $.bool().notNull() - Default: true - - captureStdout: - Contract: $.bool().notNull() - Default: true - - ignoreErrors: - Contract: $.bool().notNull() - Default: false - - timeout: - Contract: $.int() - Default: null - - Body: - - $resources: new(sys:Resources) - - If: $helpText != null - Then: - - $planName: $helpText - Else: - - $planName: format('Execute {0}', $command) - - $template: $resources.yaml('RunCommand.template').bind(dict( - command => $command, - planName => $planName, - captureStderr => $captureStderr, - captureStdout => $captureStdout, - verifyExitcode => not $ignoreErrors - )) - - Return: $agent.call($template, $resources, $timeout) - - putFile: - Usage: Static - Meta: - - m:Synchronize: - onArgs: agent - Arguments: - - agent: - Contract: $.class(sys:Agent) - - fileContent: - Contract: $.string().notNull() - - path: - Contract: $.string().notNull() - - helpText: - Contract: $.string() - Default: null - - ignoreErrors: - Contract: $.bool().notNull() - Default: false - - timeout: - Contract: $.int() - Default: null - - Body: - - $data: base64encode($fileContent) - - $resources: new(sys:Resources) - - If: $helpText != null - Then: - - $planName: $helpText - Else: - - $planName: format('Put to {0}', $path) - - $template: $resources.yaml('PutFile.template').bind(dict( - path => $path, - fileContent => $data, - planName => $planName, - verifyExitcode => not $ignoreErrors - )) - - Return: $agent.call($template, $resources, $timeout) diff --git a/meta/io.murano/Classes/metadata/Description.yaml b/meta/io.murano/Classes/metadata/Description.yaml deleted file mode 100644 index 30b4cbb95..000000000 --- a/meta/io.murano/Classes/metadata/Description.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata - -Name: Description -Usage: Meta -Inherited: true - -Properties: - text: - Contract: $.string().notNull() diff --git a/meta/io.murano/Classes/metadata/HelpText.yaml b/meta/io.murano/Classes/metadata/HelpText.yaml deleted file mode 100644 index c41e120e4..000000000 --- a/meta/io.murano/Classes/metadata/HelpText.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata - -Name: HelpText -Usage: Meta -Inherited: true - -Properties: - text: - Contract: $.string().notNull() diff --git a/meta/io.murano/Classes/metadata/ModelBuilder.yaml b/meta/io.murano/Classes/metadata/ModelBuilder.yaml deleted file mode 100644 index 5c09b3806..000000000 --- a/meta/io.murano/Classes/metadata/ModelBuilder.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata - -Name: ModelBuilder -Usage: Meta -Applies: Method -Inherited: true - -Properties: - enabled: - Contract: $.bool().notNull() - Default: true \ No newline at end of file diff --git a/meta/io.murano/Classes/metadata/Title.yaml b/meta/io.murano/Classes/metadata/Title.yaml deleted file mode 100644 index e65cf18ac..000000000 --- a/meta/io.murano/Classes/metadata/Title.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata - -Name: Title -Usage: Meta -Inherited: true - -Properties: - text: - Contract: $.string().notNull() diff --git a/meta/io.murano/Classes/metadata/engine/Serialize.yaml b/meta/io.murano/Classes/metadata/engine/Serialize.yaml deleted file mode 100644 index e1d2b20cf..000000000 --- a/meta/io.murano/Classes/metadata/engine/Serialize.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata.engine - -Name: Serialize -Usage: Meta -Cardinality: One -Inherited: true -Applies: - - Property - -Properties: - as: - Contract: $.check($ in ['reference', 'copy']) diff --git a/meta/io.murano/Classes/metadata/engine/Synchronize.yaml b/meta/io.murano/Classes/metadata/engine/Synchronize.yaml deleted file mode 100644 index 3bae02075..000000000 --- a/meta/io.murano/Classes/metadata/engine/Synchronize.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata.engine - -Name: Synchronize -Usage: Meta -Inherited: true -Cardinality: One -Applies: [Method] -Properties: - onThis: - Contract: $.bool().notNull() - Default: true - onArgs: - Contract: - - $.string().notNull() diff --git a/meta/io.murano/Classes/metadata/forms/Hidden.yaml b/meta/io.murano/Classes/metadata/forms/Hidden.yaml deleted file mode 100644 index d9621d7eb..000000000 --- a/meta/io.murano/Classes/metadata/forms/Hidden.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata.forms - -Name: Hidden -Usage: Meta -Applies: [Property, Argument] -Inherited: true - -Properties: - visible: - Contract: $.bool().notNull() - Default: false diff --git a/meta/io.murano/Classes/metadata/forms/Position.yaml b/meta/io.murano/Classes/metadata/forms/Position.yaml deleted file mode 100644 index 7c7c46b40..000000000 --- a/meta/io.murano/Classes/metadata/forms/Position.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata.forms - -Name: Position -Usage: Meta -Applies: [Property, Argument] -Inherited: true - -Properties: - index: - Contract: $.int() - Default: null - - section: - Contract: $.string() - Default: null diff --git a/meta/io.murano/Classes/metadata/forms/Section.yaml b/meta/io.murano/Classes/metadata/forms/Section.yaml deleted file mode 100644 index 81f3365bc..000000000 --- a/meta/io.murano/Classes/metadata/forms/Section.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.metadata.forms - -Name: Section -Usage: Meta -Cardinality: Many -Applies: [Type, Method] -Inherited: true - -Properties: - name: - Contract: $.string().notNull() - - title: - Contract: $.string() - Default: $.name - - index: - Contract: $.int() - Default: null diff --git a/meta/io.murano/Classes/resources/CinderVolume.yaml b/meta/io.murano/Classes/resources/CinderVolume.yaml deleted file mode 100644 index a23dd0b21..000000000 --- a/meta/io.murano/Classes/resources/CinderVolume.yaml +++ /dev/null @@ -1,154 +0,0 @@ -# Licensed 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. - -Namespaces: - std: io.murano - =: io.murano.resources - sys: io.murano.system - -Name: CinderVolume - -Extends: - - Volume - - MetadataAware - -Properties: - name: - Contract: $.string() - - size: - Contract: $.int().notNull().check($ >= 1) - - availabilityZone: - Contract: $.string() - - readOnly: - Contract: $.bool().notNull() - Default: false - - sourceImage: - Contract: $.string() - - sourceVolume: - Contract: $.class(Volume) - - sourceSnapshot: - Contract: $.class(CinderVolumeSnapshot) - - sourceVolumeBackup: - Contract: $.class(CinderVolumeBackup) - - attachments: - Contract: [] - Usage: Out - -Methods: - buildResourceDefinition: - Body: - - $properties: - size: $.size - metadata: $this.getMetadata($this.getRegion()) - - - If: $.availabilityZone != null - Then: - $properties.availability_zone: $.availabilityZone - - If: $.name != null - Then: - $properties.name: $.name - - If: $.sourceVolumeBackup != null - Then: - $properties.backup_id: $.sourceVolumeBackup.openstackId - - If: $.sourceImage != null - Then: - $properties.image: $.sourceImage - - If: $.sourceSnapshot != null - Then: - $properties.snapshot_id: $.sourceSnapshot.openstackId - - If: $.sourceVolume != null - Then: - $properties.source_volid: $.sourceVolume.openstackId - - # Available only since Heat 5.0.0 (Liberty) - - If: $.readOnly - Then: - $properties.read_only: $.readOnly - - - Return: - resources: - format('vol-{0}', id($)): - type: $this.getResourceType() - properties: $properties - outputs: - format('vol-{0}-id', id($)): - value: $.getRef() - format('vol-{0}-attachments', id($)): - value: - get_attr: [$.getResourceName(), attachments_list] - - deploy: - Body: - - $region: $.getRegion() - - If: $.sourceSnapshot != null - Then: - $.sourceSnapshot.validate() - - If: $.sourceVolumeBackup != null - Then: - $.sourceVolumeBackup.validate() - - If: $.sourceVolume != null - Then: - $.sourceVolume.deploy() - - - $snippet: $.buildResourceDefinition() - - If: $.getAttr(lastTemplate) != $snippet - Then: - - $template: $region.stack.current() - - $template: $template.mergeWith($snippet, maxLevels => 2) - - $region.stack.setTemplate($template) - - $region.stack.push() - - $outputs: $region.stack.output() - - $.openstackId: $outputs.get(format('vol-{0}-id', id($))) - - $.setAttr(lastTemplate, $snippet) - - releaseResources: - Body: - - If: $.getAttr(lastTemplate) != null - Then: - - $region: $.getRegion() - - $template: $region.stack.current() - - $template.resources: $template.resources.delete(format('vol-{0}', id($))) - - $template.outputs: $template.outputs.delete(format('vol-{0}-id', id($))) - - $region.stack.setTemplate($template) - - $region.stack.push() - - $.setAttr(lastTemplate, null) - - $.openstackId: null - - $.attachments: null - - getRef: - Body: - Return: - get_resource: format('vol-{0}', id($)) - - getResourceName: - Body: - Return: - format('vol-{0}', id($)) - - getResourceType: - Body: - - Return: 'OS::Cinder::Volume' - - setAttachments: - Arguments: - - attachments: - Contract: [] - Body: - - $.attachments: $attachments diff --git a/meta/io.murano/Classes/resources/CinderVolumeBackup.yaml b/meta/io.murano/Classes/resources/CinderVolumeBackup.yaml deleted file mode 100644 index e06c3d817..000000000 --- a/meta/io.murano/Classes/resources/CinderVolumeBackup.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - -Name: CinderVolumeBackup - -Properties: - openstackId: - Contract: $.string().notNull() - -Methods: - validate: - Body: - # TODO: add validation that backup does exist \ No newline at end of file diff --git a/meta/io.murano/Classes/resources/CinderVolumeSnapshot.yaml b/meta/io.murano/Classes/resources/CinderVolumeSnapshot.yaml deleted file mode 100644 index e59165818..000000000 --- a/meta/io.murano/Classes/resources/CinderVolumeSnapshot.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - -Name: CinderVolumeSnapshot - -Properties: - openstackId: - Contract: $.string().notNull() - -Methods: - validate: - Body: - # TODO: add validation that snapshot does exist \ No newline at end of file diff --git a/meta/io.murano/Classes/resources/ConfLangInstance.yaml b/meta/io.murano/Classes/resources/ConfLangInstance.yaml deleted file mode 100644 index 92d440482..000000000 --- a/meta/io.murano/Classes/resources/ConfLangInstance.yaml +++ /dev/null @@ -1,56 +0,0 @@ -Namespaces: - =: io.murano.resources - sys: io.murano.system - std: io.murano - -Name: ConfLangInstance - -Extends: - - LinuxMuranoInstance - -Methods: - prepareUserData: - Body: - - $userData: $.generateUserData() - - Return: - data: $._generateInstanceConfigResources($userData) - format: RAW - - _generateInstanceConfigResources: - Arguments: - - userData: - Contract: $.string().notNull() - Body: - - $region: $.getRegion() - - $cloudInitConf: $.generateCloudConfig() - - $bootConfigResourceName: format('boot_config_{0}', $.name) - - $bootScriptResourceName: format('boot_script_{0}', $.name) - - $userDataResourceName: format('user_data-{0}', $.name) - - $template: - resources: - $bootConfigResourceName: - type: 'OS::Heat::CloudConfig' - properties: - cloud_config: $cloudInitConf - $bootScriptResourceName: - type: 'OS::Heat::SoftwareConfig' - properties: - group: ungrouped - config: $userData - $userDataResourceName: - type: 'OS::Heat::MultipartMime' - properties: - parts: - - config: {get_resource: $bootConfigResourceName} - - config: {get_resource: $bootScriptResourceName} - - - $region.stack.updateTemplate($template) - - Return: {get_resource: $userDataResourceName} - - - generateCloudConfig: - Body: - - $cloudConfigData: cast($, LinuxMuranoInstance).generateCloudConfig() - - $confLang: sys:Resources.yaml('conflang.conf') - - $cloudInitConf: $cloudConfigData.mergeWith($confLang) - - Return: $cloudInitConf diff --git a/meta/io.murano/Classes/resources/ExistingCinderVolume.yaml b/meta/io.murano/Classes/resources/ExistingCinderVolume.yaml deleted file mode 100644 index ade58c30e..000000000 --- a/meta/io.murano/Classes/resources/ExistingCinderVolume.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - -Name: ExistingCinderVolume - -Extends: Volume - -Properties: - openstackId: - Contract: $.string().notNull() - -Methods: - getRef: - Body: - Return: $.openstackId diff --git a/meta/io.murano/Classes/resources/ExistingNeutronNetwork.yaml b/meta/io.murano/Classes/resources/ExistingNeutronNetwork.yaml deleted file mode 100644 index b0dfab173..000000000 --- a/meta/io.murano/Classes/resources/ExistingNeutronNetwork.yaml +++ /dev/null @@ -1,174 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: ExistingNeutronNetwork - -Extends: NeutronNetworkBase - -Properties: - internalNetworkName: - Contract: $.string() - Default: null - Usage: InOut - - internalSubnetworkName: - Contract: $.string() - Default: null - Usage: InOut - - externalNetworkName: - Contract: $.string() - Default: null - Usage: InOut - -Workflow: - initialize: - Body: - - $._networks: null - - $._subnetworks: null - - $._ports: null - - $._internalNetworkId: null - - $._internalSubnetworkId: null - - $._externalNetworkId: null - - deploy: - Body: - - $netExplorer: $._getNetExplorer() - - If: $.internalNetworkName = null - Then: - $.internalNetworkName: $._getNetworks().where( - $.get('router:external') = false).first().name - - - If: $._internalNetworkId = null - Then: - $._internalNetworkId: $._getNetworks().where( - $.name = $this.internalNetworkName or - $.id = $this.internalNetworkName).first().id - - - If: $._internalNetworkId = $.internalNetworkName - Then: - $._internalNetworkName: $._getNetworks().where( - $.id = $this._internalNetworkId).first().name - Else: - $._internalNetworkName: $.internalNetworkName - - - If: $.internalSubnetworkName = null - Then: - $.internalSubnetworkName: $._getSubnetworks().where( - $.network_id = $this._internalNetworkId).first().name - - If: $._internalSubnetworkId = null - Then: - # Specify subnetwork id only if the network is owned by the - # environment owner tenant (otherwise we may not be allowed to create - # a port to that specific subnet) - - $net: $this._getNetworks().where($.id = $this._internalNetworkId).first() - - If: $net.tenant_id = std:Project.getEnvironmentOwner().id - Then: - - $._internalSubnetworkId: $._getSubnetworks().where( - ($.name = $this.internalSubnetworkName or - $.id = $this.internalSubnetworkName) and - $.network_id = $this._internalNetworkId).first().id - Else: - - $._internalSubnetworkId: null - - - If: $.externalNetworkName = null and $._internalNetworkId != null - Then: - - $ports: $netExplorer.listPorts() - - $routerCandidates: $ports.where( - $.network_id = $this._internalNetworkId and $.device_owner = 'network:router_interface'). - select($.device_id) - - $networkCandidates: $ports.where( - $.device_id in $routerCandidates and $.network_id != $this._internalNetworkId). - select($.network_id) - - $externalNetwork: $._getNetworks().where( - $.get('router:external') = true and $.id in $networkCandidates).first(null) - - If: $externalNetwork != null - Then: - - $.externalNetworkName: $externalNetwork.name - - $._externalNetworkId: $externalNetwork.id - - - If: $.externalNetworkName = null - Then: - $.externalNetworkName: $._getNetworks().where( - $.get('router:external') = true).select($.name).first(null) - - If: $._externalNetworkId = null and $.externalNetworkName != null - Then: - $._externalNetworkId: $._getNetworks().where( - $.name = $this.externalNetworkName or - $.id = $this.externalNetworkName).first().id - - _getNetworks: - Body: - - If: not $._networks - Then: - $._networks: $._getNetExplorer().listNetworks() - - Return: $._networks - - _getSubnetworks: - Body: - - If: $._subnetworks = null - Then: - $._subnetworks : $._getNetExplorer().listSubnetworks() - - Return: $._subnetworks - - joinInstance: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - securityGroupName: - Contract: $.string() - - securityGroups: - Contract: [$.string()] - - assignFloatingIp: - Contract: $.bool().notNull() - - sharedIps: - Contract: - - $.class(std:SharedIp) - Body: - - $.deploy() - - $fipName: null - - $floatingIpNetRef: null - - If: $assignFloatingIp - Then: - - $floatingIpNetRef: $._externalNetworkId - - $fipName: format('fip-{0}-{1}', id($), $instance.name) - - - Return: $.joinInstanceToNetwork( - instance => $instance, - securityGroupName => $securityGroupName, - sharedIps => $sharedIps, - netRef => $._internalNetworkId, - subnetRef => $._internalSubnetworkId, - floatingIpResourceName => $fipName, - floatingIpNetRef => $floatingIpNetRef, - netName => $._internalNetworkName - ) - - describe: - Body: - - $.deploy() - - $subnet: $._getSubnetworks().where( - $.network_id = $this._internalNetworkId).first() - - Return: - provider: Neutron - netId: $._internalNetworkId - netName: $.internalNetworkName - subnetId: $._internalSubnetworkId - cidr: $subnet.cidr - dns: $subnet.dns_nameservers - gateway: $subnet.gateway_ip - floatingIpNetId: $._externalNetworkId diff --git a/meta/io.murano/Classes/resources/HeatSWConfigInstance.yaml b/meta/io.murano/Classes/resources/HeatSWConfigInstance.yaml deleted file mode 100644 index f73038648..000000000 --- a/meta/io.murano/Classes/resources/HeatSWConfigInstance.yaml +++ /dev/null @@ -1,150 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - sys: io.murano.system - std: io.murano - -Name: HeatSWConfigInstance - -Extends: - - Instance - -Methods: - initialize: - Body: - - $.softwareConfigs: [] - - # configName will be prepended with the instance name - # configSection should be a map representing the 'config' - # fragment in a StructuredConfig - # inputValues should be a map with any required inputs - # signalTransport: null (==CFN_SIGNAL), HEAT_SIGNAL, NO_TRANSPORT - # - # A StructuredConfig and StructuredDeployment will be added - # to the Instance - addStructuredConfig: - Arguments: - - configName: - Contract: $.string().notNull() - - configSection: - Contract: {} - - inputValues: - Contract: {} - Default: {} - - signalTransport: - Contract: $.string() - Default: null - Body: - - $group: Heat::Ungrouped - - $.addSoftwareConfig($configName, $configSection, - inputValues=>$inputValues, - configGroup=>$group, isStructured=>True, - signalTransport=>$signalTransport) - - # Adds a SoftwareConfig and SoftwareDeployment. - # configName will be prepended with the instance name - # configSection should be of a suitable form (structured config takes maps, - # ordinary software config can take a string or a map), - # configGroup can be Heat::Ungrouped, script, puppet etc - # inputValues should be a map with any inputs required by the Config - # signalTransport: null (==CFN_SIGNAL), HEAT_SIGNAL, NO_TRANSPORT - addSoftwareConfig: - Arguments: - - configName: - Contract: $.string().notNull() - - configSection: - # Should be string unless for a structured config - Contract: $.notNull() - - inputValues: - Contract: {} - Default: {} - - configGroup: - Contract: $.string() - Default: Heat::Ungrouped - - isStructured: - Contract: $.bool() - Default: False - - signalTransport: - Contract: $.string() - Default: null - - Body: - - $full_config_name: $.name + '-' + $configName - - $deployment_name: $full_config_name + '-deployment' - - $deployment_stderr: $deployment_name + '-stderr' - - $deployment_stdout: $deployment_name + '-stdout' - - $injectConfig: $configSection - - $configType: OS::Heat::SoftwareConfig - - $deploymentType: OS::Heat::SoftwareDeployment - - If: $isStructured - Then: - - $configType: OS::Heat::StructuredConfig - - $deploymentType: OS::Heat::StructuredDeployment - - $injectConfig['completion-signal']: {get_input: deploy_signal_id} - - $fragment: - resources: - $full_config_name: - type: $configType - properties: - group: $configGroup - config: - $injectConfig - $deployment_name: - type: $deploymentType - properties: - config: { get_resource: $full_config_name } - server: { get_resource: $.name } - signal_transport: $signalTransport - input_values: - $inputValues - outputs: - $deployment_stdout: - value: {get_attr: [$deployment_name, deploy_stdout]} - $deployment_stderr: - value: {get_attr: [$deployment_name, deploy_stderr]} - - - $.softwareConfigs: $.softwareConfigs + list($fragment) - - $.setAttr(scResources, $.getAttr(scResources, []).concat([$full_config_name, $deployment_name])) - - $.setAttr(scOutputs, $.getAttr(scOutputs, []).concat([$deployment_stdout, $deployment_stderr])) - - # Adds to the stack any heat SW config elements - prepareStackTemplate: - Arguments: - instanceTemplate: - Contract: {} - Body: - - For: fragment - In: $.softwareConfigs - Do: - - $instanceTemplate: $instanceTemplate.mergeWith($fragment) - - Return: $instanceTemplate - - prepareUserData: - Body: - - Return: - data: - format: SOFTWARE_CONFIG - - releaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - If: $template.get(resources) and $template.get(outputs) - Then: - - $template.resources: $template.resources.deleteAll($.getAttr(scResources, [])) - - $template.outputs: $template.outputs.deleteAll($.getAttr(scOutputs, [])) - - $region.stack.setTemplate($template) - - super($, $.releaseResources()) - - $.setAttr(scResources, []) - - $.setAttr(scOutputs, []) diff --git a/meta/io.murano/Classes/resources/HeatSWConfigLinuxInstance.yaml b/meta/io.murano/Classes/resources/HeatSWConfigLinuxInstance.yaml deleted file mode 100644 index 509f86e62..000000000 --- a/meta/io.murano/Classes/resources/HeatSWConfigLinuxInstance.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - sys: io.murano.system - std: io.murano - -Name: HeatSWConfigLinuxInstance - -Extends: - - LinuxInstance - - HeatSWConfigInstance - diff --git a/meta/io.murano/Classes/resources/Instance.yaml b/meta/io.murano/Classes/resources/Instance.yaml deleted file mode 100644 index c8d396f39..000000000 --- a/meta/io.murano/Classes/resources/Instance.yaml +++ /dev/null @@ -1,427 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - - -Name: Instance -Extends: - - std:CloudResource - - MetadataAware - -Properties: - name: - Contract: $.string().notNull() - flavor: - Contract: $.string().notNull() - image: - Contract: $.string() - Default: null - keyname: - Contract: $.string() - Default: null - openstackId: - Contract: $.string() - Usage: Out - availabilityZone: - Contract: $.string().notNull() - Default: nova - agent: - Contract: $.class(sys:Agent) - Usage: Runtime - ipAddresses: - Contract: [$.string()] - Usage: Out - networks: - Contract: - useEnvironmentNetwork: $.bool().notNull() - useFlatNetwork: $.bool().notNull() - customNetworks: [$.class(Network).notNull()] - primaryNetwork: $.class(Network).notOwned() - Default: - useEnvironmentNetwork: true - useFlatNetwork: false - customNetworks: [] - primaryNetwork: null - assignFloatingIp: - Contract: $.bool().notNull() - Default: false - floatingIpAddress: - Contract: $.string() - Usage: Out - securityGroupName: - Contract: $.string() - Default: null - securityGroups: - Contract: [$.string()] - Default: [] - sharedIps: - Contract: - - $.class(std:SharedIp) - Usage: InOut # as it is set in setSharedIps - volumes: - Contract: - $.string().notNull(): $.class(Volume).notNull() - blockDevices: - Contract: - - volume: $.class(Volume).notNull() - deviceName: $.string() - deviceType: $.string().check($ in list(disk, cdrom, null)) - bootIndex: $.int() - Default: [] - joinedNetworks: - Contract: - - network: $.class(Network).notNull() - ipList: [$.string().notNull()] - Usage: Out - #policies: anti-affinity, affinity - instanceAffinityGroup: - Contract: $.class(InstanceAffinityGroup) - -Methods: - .init: - Body: - - $._environment: $.find(std:Environment).require() - - $.agent: new(sys:Agent, host => $) - - $.resources: new(sys:Resources) - - $.instanceTemplate: {} - - $._floatingIpOutputName: null - - - $.volumes.values().concat($this.blockDevices.volume).select( - sys:GC.subscribeDestruction($, $this, _onReferencedResourceDelete)) - - - If: $.instanceAffinityGroup != null - Then: sys:GC.subscribeDestruction( - $this.instanceAffinityGroup, $this, _onReferencedResourceDelete) - - - # Called after the Instance template pieces are in place. It - # is at this stage alterations to the template should be made - prepareStackTemplate: - Arguments: - instanceTemplate: - Contract: {} - Body: - - Return: $instanceTemplate - - setSharedIps: - Arguments: - ips: - Contract: - - $.class(std:SharedIp) - Body: - $.sharedIps: $ips - - beginDeploy: - Body: - - $.validateBootSource() - - $region: $.getRegion() - - $securityGroupName: coalesce( - $.securityGroupName, - $region.securityGroupManager.defaultGroupName - ) - - $.createDefaultInstanceSecurityGroupRules($securityGroupName) - - $.detectPrimaryNetwork() - - $.ensureSharedIpsDeployed() - - $.ensureNetworksDeployed() - - If: $.networks.useEnvironmentNetwork and $region.defaultNetworks.environment!=null - Then: - $.joinNet($region.defaultNetworks.environment, $securityGroupName, $this.securityGroups) - - If: $.networks.useFlatNetwork and $region.defaultNetworks.flat!=null - Then: - $.joinNet($region.defaultNetworks.flat, $securityGroupName, $this.securityGroups) - - $.networks.customNetworks.select($this.joinNet($, $securityGroupName, $this.securityGroups)) - - - $preparedUserData: $.prepareUserData() - - $properties: - name: $.name - flavor: $.flavor - availability_zone: $.availabilityZone - user_data: $preparedUserData.data - user_data_format: $preparedUserData.format - key_name: $.keyname - metadata: $this.getMetadata($region) - - - If: len($.blockDevices) > 0 - Then: - - $bdmDefinition: $.blockDevices.select($this.buidBlockDeviceDefinition($)) - - $properties.block_device_mapping_v2: $bdmDefinition - Else: - $properties.image: $.image - - If: $.instanceAffinityGroup != null - Then: - - $.instanceAffinityGroup.deploy() - - $properties.scheduler_hints: - group: $.instanceAffinityGroup.getRef() - - $template: - resources: - $.name: - type: $this.getResourceType() - properties: $properties - - outputs: - format('{0}-id', $.name): - description: format('ID of {0} instance', $.name) - value: - get_resource: $.name - - $.instanceTemplate: $.instanceTemplate.mergeWith($template) - - $.volumes.items().select( - $.unpack(path, volume) -> $this.attachVolume($path, $volume)) - - # Any additional template preparation - - $.instanceTemplate: $.prepareStackTemplate($.instanceTemplate) - - $region.stack.updateTemplate($.instanceTemplate) - - endDeploy: - Body: - - $region: $.getRegion() - - $region.stack.push() - - $outputs: $region.stack.output() - - For: blockDevice - In: $.blockDevices - Do: - - $blockDevice.volume.setAttachments($outputs.get(format('{0}-attachments', $blockDevice.volume.getResourceName()))) - - $.openstackId: $outputs.get(format('{0}-id', $this.name)) - - If: $._floatingIpOutputName != null - Then: - - $.floatingIpAddress: $outputs.get($._floatingIpOutputName) - - If: $.floatingIpAddress != null - Then: $.setAttr(fipAssigned, true) - - $._environment.instanceNotifier.trackCloudInstance($this) - - $.joinedNetworks: $this.joinedNetworks.distinct().select({ - network => $.network, - ipList => $.network.getInstanceIpList($this)}) - - $.ipAddresses: $this.joinedNetworks.selectMany($.ipList).where($ != $this.floatingIpAddress).distinct() - - If: $.floatingIpAddress != null - Then: - - $.ipAddresses: $.ipAddresses.append($.floatingIpAddress) - - deploy: - Body: - - $this.beginDeploy() - - $this.endDeploy() - - detectPrimaryNetwork: - Body: - - $region: $.getRegion() - - $._primaryNetwork: null - - If: $.networks.primaryNetwork != null - Then: - - $._primaryNetwork: $.networks.primaryNetwork - Else: - - If: $.networks.useEnvironmentNetwork and $region.defaultNetworks.environment!=null - Then: - - $._primaryNetwork: $region.defaultNetworks.environment - Else: - - If: $.networks.useFlatNetwork and $region.defaultNetworks.flat!=null - Then: - - $._primaryNetwork: $region.defaultNetworks.flat - Else: - - If: $.networks.customNetworks!= null - Then: - - $._primaryNetwork: $.networks.customNetworks.first(null) - - ensureNetworksDeployed: - Body: - - $region: $.getRegion() - - If: $.networks.useEnvironmentNetwork and $region.defaultNetworks.environment!=null - Then: - - $region.defaultNetworks.environment.deploy() - - If: $.networks.useFlatNetwork and $region.defaultNetworks.flat!=null - Then: - - $region.defaultNetworks.flat.deploy() - - $.networks.customNetworks.pselect($.deploy()) - - ensureSharedIpsDeployed: - Body: - - $.sharedIps.pselect($.deploy()) - - joinNet: - Arguments: - - net: - Contract: $.class(Network).notNull() - - securityGroupName: - Contract: $.string() - - securityGroups: - Contract: [$.string()] - Body: - - $primary: $net = $._primaryNetwork - - $assignFip: $primary and $.assignFloatingIp and not $.getAttr(fipAssigned, false) - - - $sharedIps: [] - - For: sharedIp - In: $.sharedIps - Do: - - If: $sharedIp.network = $net - Then: - - $sharedIps: $sharedIps.append($sharedIp) - - - $joinResult: $net.joinInstance( - instance => $this, - securityGroupName => $securityGroupName, - securityGroups => $securityGroups, - assignFloatingIp => $assignFip, - sharedIps => $sharedIps - ) - - $.setAttr(instanceResources, $.getAttr(instanceResources, []).concat($joinResult.get(instanceResources, []))) - - $.setAttr(instanceOutputs, $.getAttr(instanceOutputs, []).concat($joinResult.get(instanceOutputs, []))) - - - If: $joinResult.template != null - Then: - - $.instanceTemplate: $.instanceTemplate.mergeWith($joinResult.template) - - - If: $joinResult.get(portRef) != null - Then: - - $template: - resources: - $.name: - properties: - networks: - - port: - $joinResult.portRef - - $.instanceTemplate: $.instanceTemplate.mergeWith($template) - - If: $joinResult.get(secGroupName) != null - Then: - - $template: - resources: - $.name: - properties: - security_groups: - - $joinResult.secGroupName - - $.instanceTemplate: $.instanceTemplate.mergeWith($template) - - - $._floatingIpOutputName: coalesce($._floatingIpOutputName, $joinResult.instanceFipOutput) - - $this.joinedNetworks: $this.joinedNetworks.append({network => $net}) - - attachVolume: - Arguments: - - path: - Contract: $.string().notNull() - - volume: - Contract: $.class(Volume).notNull() - Body: - - $attachment: $volume.attachTo($this, $path) - - $.instanceTemplate: $.instanceTemplate.mergeWith($attachment.template) - - $.setAttr(instanceResources, $.getAttr(instanceResources, []).concat($attachment.get(instanceResources, []))) - - $.setAttr(instanceOutputs, $.getAttr(instanceOutputs, []).concat($attachment.get(instanceOutputs, []))) - - buidBlockDeviceDefinition: - Arguments: - blockDevice: - Contract: - volume: $.class(Volume).notNull() - deviceName: $.string() - deviceType: $.string().check($ in list(disk, cdrom, null)) - bootIndex: $.int() - - Body: - - $blockDevice.volume.deploy() - - Return: - device_name: $blockDevice.deviceName - volume_id: $blockDevice.volume.getRef() - device_type: $blockDevice.deviceType - boot_index: $blockDevice.bootIndex - - beginReleaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - If: $template.get(resources) and $template.get(outputs) - Then: - - $resourcesToDelete: list($.name).concat($.getAttr(instanceResources, [])) - - $lenBefore: len($template.resources) - - $template.resources: $template.resources.deleteAll($resourcesToDelete) - - If: $lenBefore > len($template.resources) - Then: - - $._environment.instanceNotifier.untrackCloudInstance($this) - - - $outputsToDelete: list( - '{0}-id'.format($.name)).concat($.getAttr(instanceOutputs, [])) - - $template.outputs: $template.outputs.deleteAll($outputsToDelete) - - - $region.stack.setTemplate($template) - - _onReferencedResourceDelete: - Arguments: - - resource: - Contract: $.class(std:CloudResource).notNull() - Body: - - If: sys:GC.isDoomed($this) - Then: - - $.beginReleaseResources() - - endReleaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - If: $template.get(resources) and $template.get(outputs) and not sys:GC.isDoomed($region) - Then: - - $region.stack.push(async => true) - - $.setAttr(instanceResources, []) - - $.setAttr(instanceOutputs, []) - - $.setAttr(fipAssigned, false) - - $.openstackId: null - - $.ipAddresses: [] - - $.floatingIpAddress: null - - releaseResources: - Body: - - $this.beginReleaseResources() - - $this.endReleaseResources() - - validateBootSource: - Body: - - If: $.image = null and len($.blockDevices) = 0 - Then: - - $msg: format('Neither image nor bootable volumes is specified for instance {0}', $.name) - - Throw: ResourceNotFound - Message: $msg - - If: $.image != null and len($.blockDevices) > 0 - Then: - - $msg: 'Both image and list of bootable volumes are specified' - - Throw: ResourceConflict - Message: $msg - - destroy: - Body: - - $.releaseResources() - - - createDefaultInstanceSecurityGroupRules: - Arguments: - - groupName: - Contract: $.string().notNull() - - prepareUserData: - Body: - Return: - data: null - # Valid values are HEAT_CFNTOOLS, RAW and SOFTWARE_CONFIG - format: HEAT_CFNTOOLS - - isDeployed: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - Return: $template.get(resources, {}).containsKey($.name) - - getRef: - Body: - Return: - get_resource: $.name - - getResourceType: - Body: - - Return: 'OS::Nova::Server' diff --git a/meta/io.murano/Classes/resources/InstanceAffinityGroup.yaml b/meta/io.murano/Classes/resources/InstanceAffinityGroup.yaml deleted file mode 100644 index 3ba4fffce..000000000 --- a/meta/io.murano/Classes/resources/InstanceAffinityGroup.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# Licensed 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. - -Namespaces: - std: io.murano - =: io.murano.resources - -Name: InstanceAffinityGroup -Extends: std:CloudResource - -Properties: - - affinity: - Contract: $.bool().notNull() - Default: false #affinity: true, anti-affinity: false - - -Methods: - deploy: - Body: - - If: $.affinity - Then: - - $policies: ['affinity'] - Else: - - $policies: ['anti-affinity'] - - $name: $._getHeatName() - - $affinityTemplate: - resources: - $name: - type: 'OS::Nova::ServerGroup' - properties: - name: $name - policies: $policies - - $region: $.getRegion() - - $region.stack.updateTemplate($affinityTemplate) - - $region.stack.push(async => true) - - .destroy: - Body: - - $name: $._getHeatName() - - $region: $.getRegion() - - $template: $region.stack.current() - - $template.resources: $template.resources.delete($name) - - $region.stack.setTemplate($template) - - $region.stack.push(async => true) - - - getRef: - Body: - - Return: - get_resource: $._getHeatName() - - _getHeatName: - Body: - - Return: format('NovaServerGroup-{0}', id($this)) diff --git a/meta/io.murano/Classes/resources/LinuxInstance.yaml b/meta/io.murano/Classes/resources/LinuxInstance.yaml deleted file mode 100644 index 316f669a4..000000000 --- a/meta/io.murano/Classes/resources/LinuxInstance.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - -Name: LinuxInstance - -Extends: Instance - -Methods: - createDefaultInstanceSecurityGroupRules: - Arguments: - - groupName: - Contract: $.string().notNull() - Body: - - $region: $.getRegion() - - $rules: - - ToPort: 22 - IpProtocol: tcp - FromPort: 22 - External: true - - $region.securityGroupManager.addGroupIngress( - rules => $rules, groupName => $groupName) diff --git a/meta/io.murano/Classes/resources/LinuxMuranoInstance.yaml b/meta/io.murano/Classes/resources/LinuxMuranoInstance.yaml deleted file mode 100644 index 3ea2253ad..000000000 --- a/meta/io.murano/Classes/resources/LinuxMuranoInstance.yaml +++ /dev/null @@ -1,111 +0,0 @@ -Namespaces: - =: io.murano.resources - sys: io.murano.system - std: io.murano - -Name: LinuxMuranoInstance - -Extends: - - LinuxInstance - -Methods: - prepareUserData: - Body: - - $userData: $.generateUserData() - - Return: - data: $._generateInstanceConfigResources($userData) - format: RAW - - _generateInstanceConfigResources: - Arguments: - - userData: - Contract: $.string().notNull() - Body: - - $region: $.getRegion() - - $resources: new(sys:Resources) - - $muranoInitConf: $.generateCloudConfig() - - $bootScriptResourceName: format('boot_script_{0}', $.name) - - $userDataResourceName: format('user_data-{0}', $.name) - - $bootConfigResourceName: format('boot_config-{0}', $.name) - - $template: - resources: - $bootConfigResourceName: - type: 'OS::Heat::CloudConfig' - properties: - cloud_config: $muranoInitConf - $bootScriptResourceName: - type: 'OS::Heat::SoftwareConfig' - properties: - group: ungrouped - config: $userData - $userDataResourceName: - type: 'OS::Heat::MultipartMime' - properties: - parts: - - config: {get_resource: $bootConfigResourceName} - - config: {get_resource: $bootScriptResourceName} - - - $.setAttr(resourceCloudConfig, [$bootScriptResourceName, $userDataResourceName, $bootConfigResourceName]) - - $region.stack.updateTemplate($template) - - Return: {get_resource: $userDataResourceName} - - - generateCloudConfig: - Body: - - $resources: new(sys:Resources) - - $muranoInitConf: $resources.yaml('murano-init.conf').bind(dict( - instanceHostname => $.name - )) - - Return: $muranoInitConf - - generateUserData: - Body: - - $region: $.getRegion() - - $rabbitMqParams: $region.getConfig().agentRabbitMq - - $resources: new(sys:Resources) - - $configFile: $resources.string('Agent-v2.template') - - $initScript: $resources.string('linux-init.sh') - - $muranoScript: $resources.string('murano-init.sh') - - $muranoAgentConf: $resources.string('murano-agent.conf') - - $muranoAgentService: $resources.string('murano-agent.service') - - $muranoAgent: $resources.string('murano-agent') - - $configReplacements: - "%RABBITMQ_HOST%": $rabbitMqParams.host - "%RABBITMQ_PORT%": $rabbitMqParams.port - "%RABBITMQ_USER%": $rabbitMqParams.login - "%RABBITMQ_PASSWORD%": $rabbitMqParams.password - "%RABBITMQ_VHOST%": $rabbitMqParams.virtual_host - "%RABBITMQ_SSL%": str($rabbitMqParams.ssl).toLower() - "%RABBITMQ_INSECURE%": str($rabbitMqParams.insecure).toLower() - "%RABBITMQ_INPUT_QUEUE%": $.agent.queueName() - "%RESULT_QUEUE%": $region.agentListener.queueName() - "%SIGNING_KEY%": $.agent.signingKey('\t') - - $scriptReplacements: - "%AGENT_CONFIG_BASE64%": base64encode($configFile.replace($configReplacements)) - "%INTERNAL_HOSTNAME%": $.name - "%MURANO_SERVER_ADDRESS%": coalesce(config(file_server), $rabbitMqParams.host) - - If: config(rabbitmq, ca_certs) - Then: - - $scriptReplacements["%CA_ROOT_CERT_BASE64%"]: base64encode(config(rabbitmq, ca_certs, true)) - Else: - - $scriptReplacements["%CA_ROOT_CERT_BASE64%"]: '' - - $muranoReplacements: - "%MURANO_AGENT_CONF%": base64encode($muranoAgentConf) - "%MURANO_AGENT_SERVICE%": base64encode($muranoAgentService) - "%MURANO_AGENT%": base64encode($muranoAgent) - "%PIP_SOURCE%": config(engine, agent_source) - - - $userData: $muranoScript.replace($muranoReplacements) + $initScript.replace($scriptReplacements) - - Return: $userData - - releaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - If: $template.get(resources) and $template.get(outputs) - Then: - - $resourcesToDelete: $.getAttr(resourceCloudConfig, []) - - $template.resources: $template.resources.deleteAll($resourcesToDelete) - - $region.stack.setTemplate($template) - - - super($, $.releaseResources()) diff --git a/meta/io.murano/Classes/resources/LinuxUDInstance.yaml b/meta/io.murano/Classes/resources/LinuxUDInstance.yaml deleted file mode 100644 index a655ba089..000000000 --- a/meta/io.murano/Classes/resources/LinuxUDInstance.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - -Name: LinuxUDInstance - -Extends: - - LinuxInstance - -Methods: - initialize: - Body: - - $.customUserData: null - - prepareUserData: - Body: - - Return: - data: $.customUserData - format: HEAT_CFNTOOLS - - setCustomUserData: - Arguments: - - data: - Contract: $.notNull() - Body: - - $.customUserData: $data diff --git a/meta/io.murano/Classes/resources/MetadataAware.yaml b/meta/io.murano/Classes/resources/MetadataAware.yaml deleted file mode 100644 index 522df5a3f..000000000 --- a/meta/io.murano/Classes/resources/MetadataAware.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: MetadataAware - -Properties: - checkApplicability: - Contract: $.bool().notNull() - Default: true - -Methods: - .init: - Body: - - $this._metadefBrowsers: {} - - getMetadefBrowser: - Arguments: - - region: - Contract: $.class(std:CloudRegion).notNull() - Body: - - $browser: $this._metadefBrowsers.get($region.name) - - If: $browser = null - Then: - - $browser: new(sys:MetadefBrowser, $region) - - $this._metadefBrowsers[$region.name]: $browser - - Return: $browser - - getMetadata: - Arguments: - - region: - Contract: $.class(std:CloudRegion).notNull() - Body: - - $thisMeta: metadata($this) or {} - - $parentsMeta: {} - - $p: $this.find(std:Object) - - While: $p != null - Do: - - $pmeta: metadata($p) or {} - - $pmeta: dict($pmeta.items().where(not $[0] in $parentsMeta.keys())) - - $parentsMeta: $parentsMeta.set($pmeta) - - $p: $p.find(std:Object) - - $resourceType: $this.getResourceType() - - If: $this.checkApplicability and $resourceType - Then: - - $browser: $this.getMetadefBrowser($region) - - $parentsMeta: dict($parentsMeta.items().where($browser.canBeAppliedTo($[0], $resourceType))) - - Return: $parentsMeta.set($thisMeta) - - getResourceType: - diff --git a/meta/io.murano/Classes/resources/Network.yaml b/meta/io.murano/Classes/resources/Network.yaml deleted file mode 100644 index 755e9fce3..000000000 --- a/meta/io.murano/Classes/resources/Network.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - -Name: Network -Extends: std:CloudResource - -Methods: - deploy: - - joinInstance: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - securityGroupName: - Contract: $.string() - - assignFloatingIp: - Contract: $.bool().notNull() - - sharedIps: - Contract: - - $.class(std:SharedIp) - - generateSecurityGroupManager: - - describe: - - getInstanceIpList: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - Body: \ No newline at end of file diff --git a/meta/io.murano/Classes/resources/NeutronNetwork.yaml b/meta/io.murano/Classes/resources/NeutronNetwork.yaml deleted file mode 100644 index 4c34f1e4e..000000000 --- a/meta/io.murano/Classes/resources/NeutronNetwork.yaml +++ /dev/null @@ -1,234 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: NeutronNetwork - -Extends: NeutronNetworkBase - -Properties: - name: - Contract: $.string().notNull() - - ipVersion: - Contract: $.int() - Default: 4 - - externalRouterId: - Contract: $.string() - Usage: InOut - - autoUplink: - Contract: $.bool().notNull() - Default: true - - autogenerateSubnet: - Contract: $.bool().notNull() - Default: true - - openstackId: - Contract: $.string() - Usage: Out - - subnetCidr: - Contract: $.string() - Usage: InOut - - dnsNameservers: - # This property is optional, - # since neutron default dns will be used in case of empty - Contract: [$.string()] - Usage: InOut - -Methods: - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $netExplorer: $._getNetExplorer() - - If: len($.dnsNameservers) = 0 - Then: - - $.dnsNameservers: $netExplorer.getDefaultDns($.ipVersion) - - $template: $._createNetwork() - - If: $.autoUplink and (not bool($.externalRouterId)) - Then: - - $.externalRouterId: $netExplorer.getDefaultRouter() - - If: $.autogenerateSubnet and (not bool($.subnetCidr)) - Then: - - $.subnetCidr: $netExplorer.getAvailableCidr($.externalRouterId, id($), $.ipVersion) - - - $template: $template.mergeWith($._createSubnet()) - - If: $.externalRouterId != null - Then: - - $template: $template.mergeWith($._createRouterInterface()) - - - $region: $.getRegion() - - $region.stack.updateTemplate($template) - - $region.stack.push() - - $outputs: $region.stack.output() - - $.openstackId: $outputs.get(format('{0}-id', $this.name)) - - $.setAttr(deployed, true) - - - _createNetwork: - Body: - - $netName: $._getNetworkName() - - $template: - resources: - $netName: - type: 'OS::Neutron::Net' - properties: - name: $._getHeatName() - outputs: - format('{0}-id', $.name): - description: format('ID of {0} network', $.name) - value: - get_resource: $netName - - Return: $template - - - _createSubnet: - Body: - - Return: - resources: - $._getSubnetName(): - type: 'OS::Neutron::Subnet' - properties: - network: { get_resource: $._getNetworkName() } - ip_version: $.ipVersion - dns_nameservers: $.dnsNameservers - cidr: $.subnetCidr - - - _createRouterInterface: - Body: - - Return: - resources: - $._getRouterInterfaceName(): - type: 'OS::Neutron::RouterInterface' - properties: - router_id: $.externalRouterId - subnet: { get_resource: $._getSubnetName() } - - - joinInstance: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - securityGroupName: - Contract: $.string() - - securityGroups: - Contract: [$.string()] - - assignFloatingIp: - Contract: $.bool().notNull() - - sharedIps: - Contract: - - $.class(std:SharedIp) - Body: - - $.deploy() - - $netRef: { get_resource: $._getNetworkName() } - - $subnetRef: { get_resource: $._getSubnetName() } - - $extNetId: null - - $fipName: null - - If: $assignFloatingIp - Then: - - $extNetId: $._getExternalNetId() - - $fipName: format('fip-{0}-{1}', id($), $instance.name) - - - $result: $.joinInstanceToNetwork( - instance => $instance, - securityGroupName => $securityGroupName, - securityGroups => $securityGroups, - sharedIps => $sharedIps, - netRef => $netRef, - subnetRef => $subnetRef, - floatingIpResourceName => $fipName, - floatingIpNetRef => $extNetId, - netName => $._getHeatName() - ) - - # (sjmc7) This is a workaround for https://bugs.launchpad.net/heat/+bug/1299259 - - If: $externalRouterId != null - Then: - - $template: - resources: - $fipName: - depends_on: - - $._getRouterInterfaceName() - - $result.template: $result.template.mergeWith($template) - - - Return: $result - - describe: - Body: - - $.deploy() - - $subnet: $._getNetExplorer().listSubnetworks().where( - $.network_id = $this.openstackId).first() - - Return: - provider: Neutron - netName: $.name - netId: $.openstackId - subnetId: $subnet.id - cidr: $subnet.cidr - dns: $subnet.dns_nameservers - gateway: $subnet.gateway_ip - floatingIpNetId: $._getExternalNetId() - - releaseResources: - Body: - - $region: $.getRegion() - - $template: $region.stack.current() - - - $template.resources: $template.resources.delete($._getHeatName()) - - $template.resources: $template.resources.delete($._getSubnetName()) - - - $template.outputs: $template.outputs.delete(format('{0}-id', $.name)) - - - If: $.externalRouterId != null - Then: - $template.resources: $template.resources.delete($._getRouterInterfaceName()) - - - $regiont.stack.setTemplate($template) - - $region.stack.push() - - $.openstackId: null - - _getRouterInterfaceName: - Body: - Return: format('ri-{0}', id($)) - - - _getNetworkName: - Body: - Return: format('network-{0}', id($)) - - - _getSubnetName: - Body: - Return: format('subnet-{0}', id($)) - - - _getExternalNetId: - Body: - - If: $.externalRouterId != null - Then: - Return: $._getNetExplorer().getExternalNetworkIdForRouter($.externalRouterId) - Else: - Return: null - - _getHeatName: - Body: - Return: format('{0}-{1}', $.name, id($)) diff --git a/meta/io.murano/Classes/resources/NeutronNetworkBase.yaml b/meta/io.murano/Classes/resources/NeutronNetworkBase.yaml deleted file mode 100644 index e7723ce1c..000000000 --- a/meta/io.murano/Classes/resources/NeutronNetworkBase.yaml +++ /dev/null @@ -1,184 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: NeutronNetworkBase - -Properties: - port_security_disable: - Contract: $.bool() - -Extends: Network - -Methods: - .init: - Body: - - $._netExplorer: null - - $._environment: $.find(std:Environment) - - _getNetExplorer: - Body: - - If: $._netExplorer = null - Then: - - $._netExplorer: new(sys:NetworkExplorer, $this.getRegion()) - - Return: $._netExplorer - - joinInstanceToNetwork: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - securityGroupName: - Contract: $.string() - - securityGroups: - Contract: [$.string()] - - sharedIps: - Contract: - - $.class(std:SharedIp) - - netRef: - Contract: $ - - subnetRef: - Contract: $ - - floatingIpResourceName: - Contract: $.string() - - floatingIpNetRef: - Contract: $ - - netName: - Contract: $.string().notNull() - Body: - - $netExplorer: $._getNetExplorer() - - $securityGroupsEnabled: $netExplorer.listNeutronExtensions().alias.contains('security-group') - - $portName: format('port-{0}-{1}', id($), $instance.name) - - - $addressesOutputName: format('addresses-{0}-{1}', $instance.name, id($this)) - - $patchTemplate: - resources: - $portName: - type: 'OS::Neutron::Port' - properties: - network: $netRef - replacement_policy: AUTO - outputs: - $addressesOutputName: - description: format('Addresses for {0} in {1}', $instance.name, $netName) - value: - get_attr: [$instance.name, 'addresses', $netName] - - - If: $subnetRef - Then: - - $template: - resources: - $portName: - properties: - fixed_ips: - - subnet: $subnetRef - - $patchTemplate: $patchTemplate.mergeWith($template) - - - If: $securityGroupsEnabled and not $.port_security_disable - Then: - - If: len($securityGroups) > 0 and $securityGroups[0] != "" - Then: - - For: securityGroup - In: $securityGroups - Do: - - $template: - resources: - $portName: - properties: - security_groups: - - $securityGroup - - $patchTemplate: $patchTemplate.mergeWith($template) - Else: - - If: bool($securityGroupName) - Then: - - $template: - resources: - $portName: - properties: - security_groups: - - get_resource: $securityGroupName - - $patchTemplate: $patchTemplate.mergeWith($template) - - - If: $.port_security_disable - Then: - - $template: - resources: - $portName: - properties: - port_security_enabled: false - - - $patchTemplate: $patchTemplate.mergeWith($template) - - - $instanceResources: [$portName] - - $instanceOutputs: [$addressesOutputName] - - - For: sip - In: $sharedIps - Do: - - $template: - resources: - $portName: - properties: - allowed_address_pairs: - - ip_address: $sip.virtualIp + '/32' - - $patchTemplate: $patchTemplate.mergeWith($template) - - - $instanceFipOutput: null - - If: $floatingIpResourceName != null and $floatingIpNetRef != null - Then: - - $instanceFipOutput: $instance.name + '-floatingIPaddress' - - $template: - resources: - $floatingIpResourceName: - type: 'OS::Neutron::FloatingIP' - properties: - floating_network: $floatingIpNetRef - port_id: - get_resource: $portName - outputs: - $instanceFipOutput: - value: - get_attr: [$floatingIpResourceName, floating_ip_address] - description: format('Floating IP of {0}', $instance.name) - - $instanceResources: $instanceResources.append($floatingIpResourceName) - - $instanceOutputs: $instanceOutputs.append($instanceFipOutput) - - $patchTemplate: $patchTemplate.mergeWith($template) - - Return: - template: $patchTemplate - portRef: - get_resource: $portName - instanceFipOutput: $instanceFipOutput - instanceResources: $instanceResources - instanceOutputs: $instanceOutputs - - generateSecurityGroupManager: - Body: - - $region: $.getRegion() - - $netExplorer: $._getNetExplorer() - - $securityGroupsEnabled: $netExplorer.listNeutronExtensions().alias.contains('security-group') - - If: $securityGroupsEnabled - Then: - - Return: new(sys:NeutronSecurityGroupManager, $region) - Else: - - $._environment.reporter.report($this, "Warning! Security groups are disabled!") - - Return: new(sys:DummySecurityGroupManager, $region) - - getInstanceIpList: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - Body: - - Return: $.getRegion().stack.output()[ - format('addresses-{0}-{1}', $instance.name, id($this))].addr.distinct() diff --git a/meta/io.murano/Classes/resources/NovaNetwork.yaml b/meta/io.murano/Classes/resources/NovaNetwork.yaml deleted file mode 100644 index 42f57dae3..000000000 --- a/meta/io.murano/Classes/resources/NovaNetwork.yaml +++ /dev/null @@ -1,98 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: NovaNetwork - -Extends: Network - -Methods: - joinInstance: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - securityGroupName: - Contract: $.string() - - securityGroups: - Contract: [$.string()] - - assignFloatingIp: - Contract: $.bool().notNull() - - sharedIps: - Contract: - - $.class(std:SharedIp) - Body: - - $fipName: null - - $template: null - - $instanceFipOutput: null - - $instanceResources: [] - - $instanceOutputs: [] - - $instanceNetworkOutput: format('{0}-assigned-ips', $instance.name) - - $template: - outputs: - $instanceNetworkOutput: - description: format('Network IPs assigned to {0} instance', $instance.name) - value: - get_attr: [ $instance.name, networks ] - - If: $assignFloatingIp - Then: - - $instanceFipOutput: $instance.name + '-floatingIPaddress' - - $fipName: format('fip-nn-{0}', $instance.name) - - $fipTemplate: - resources: - $fipName: - type: 'OS::Nova::FloatingIP' - $fipName + 'Assignment': - type: 'OS::Nova::FloatingIPAssociation' - properties: - floating_ip: - get_resource: $fipName - server_id: - get_resource: $instance.name - outputs: - $instanceFipOutput: - value: - get_attr: [$fipName, ip] - description: format('Floating IP of {0}', $instance.name) - - $template: $template.mergeWith($fipTemplate) - - $instanceResources: [$fipName, $fipName + 'Assignment'] - - $instanceOutputs: [$instanceFipOutput, $instanceNetworkOutput] - - Return: - template: $template - secGroupName: - get_resource: $securityGroupName - instanceFipOutput: $instanceFipOutput - instanceResources: $instanceResources - instanceOutputs: $instanceOutputs - - - generateSecurityGroupManager: - Body: - - Return: new(sys:AwsSecurityGroupManager, $this) - - - describe: - Body: - - Return: - provider: NovaNetwork - - getInstanceIpList: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - Body: - - Return: $instance.getRegion().stack.output().get( - format('{0}-assigned-ips', $instance.name) - ).values().flatten().distinct() diff --git a/meta/io.murano/Classes/resources/Volume.yaml b/meta/io.murano/Classes/resources/Volume.yaml deleted file mode 100644 index 07bcdef4e..000000000 --- a/meta/io.murano/Classes/resources/Volume.yaml +++ /dev/null @@ -1,58 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - - -Name: Volume -Extends: std:CloudResource - - -Properties: - openstackId: - Contract: $.string() - Usage: Out - - -Methods: - deploy: - - getRef: - - releaseResources: - - .destroy: - Body: - - $.releaseResources() - - attachTo: - Arguments: - - instance: - Contract: $.class(Instance).notNull() - - path: - Contract: $.string().notNull() - Body: - - $.deploy() - - $resourceName: format('vol-attachment-{0}-{1}', id($), $instance.name) - - Return: - template: - resources: - $resourceName: - type: 'OS::Cinder::VolumeAttachment' - properties: - instance_uuid: $instance.getRef() - mountpoint: $path - volume_id: $.getRef() - instanceResources: [$resourceName] - instanceOutputs: [] diff --git a/meta/io.murano/Classes/resources/WindowsInstance.yaml b/meta/io.murano/Classes/resources/WindowsInstance.yaml deleted file mode 100644 index 15dd20317..000000000 --- a/meta/io.murano/Classes/resources/WindowsInstance.yaml +++ /dev/null @@ -1,61 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.resources - std: io.murano - sys: io.murano.system - -Name: WindowsInstance - -Extends: Instance - -Methods: - createDefaultInstanceSecurityGroupRules: - Arguments: - - groupName: - Contract: $.string().notNull() - Body: - - $region: $.getRegion() - - $rules: - - ToPort: 3389 - IpProtocol: tcp - FromPort: 3389 - External: true - - $region.securityGroupManager.addGroupIngress( - rules => $rules, groupName => $groupName) - - prepareUserData: - Body: - - $region: $.getRegion() - - $rabbitMqParams: $region.getConfig().agentRabbitMq - - $resources: new(sys:Resources) - - $configFile: $resources.string('Agent-v1.template') - - $initScript: $resources.string('windows-init.ps1') - - $configReplacements: - "%RABBITMQ_HOST%": $rabbitMqParams.host - "%RABBITMQ_PORT%": $rabbitMqParams.port - "%RABBITMQ_USER%": $rabbitMqParams.login - "%RABBITMQ_PASSWORD%": $rabbitMqParams.password - "%RABBITMQ_VHOST%": $rabbitMqParams.virtual_host - "%RABBITMQ_SSL%": str($rabbitMqParams.ssl).toLower() - "%RABBITMQ_INPUT_QUEUE%": $.agent.queueName() - "%RESULT_QUEUE%": $region.agentListener.queueName() - "%SIGNING_KEY%": $.agent.signingKey('') - - $scriptReplacements: - "%AGENT_CONFIG_BASE64%": base64encode($configFile.replace($configReplacements)) - "%INTERNAL_HOSTNAME%": $.name - "%MURANO_SERVER_ADDRESS%": coalesce(config(file_server), $rabbitMqParams.host) - "%CA_ROOT_CERT_BASE64%": "" - - Return: - data: $initScript.replace($scriptReplacements) - format: HEAT_CFNTOOLS diff --git a/meta/io.murano/Classes/system/Agent.yaml b/meta/io.murano/Classes/system/Agent.yaml deleted file mode 100644 index a5b678f35..000000000 --- a/meta/io.murano/Classes/system/Agent.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: Agent diff --git a/meta/io.murano/Classes/system/AgentListener.yaml b/meta/io.murano/Classes/system/AgentListener.yaml deleted file mode 100644 index 7cafdbfb6..000000000 --- a/meta/io.murano/Classes/system/AgentListener.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: AgentListener diff --git a/meta/io.murano/Classes/system/AwsSecurityGroupManager.yaml b/meta/io.murano/Classes/system/AwsSecurityGroupManager.yaml deleted file mode 100644 index dcf08625a..000000000 --- a/meta/io.murano/Classes/system/AwsSecurityGroupManager.yaml +++ /dev/null @@ -1,117 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.system - std: io.murano - -Name: AwsSecurityGroupManager - -Extends: SecurityGroupManager - -Methods: - .init: - Body: - - $._environment: $.find(std:Environment) - - $._region: $.find(std:CloudRegion).require() - - addGroupIngress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - Body: - - $._addGroup(ingress, $rules, $groupName) - - addGroupEgress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - Body: - - $._addGroup(egress, $rules, $groupName) - - _addGroup: - Arguments: - - direction: - Contract: $.string().notNull().check($ in list(ingress, egress)) - Default: ingress - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - Body: - - $ext_keys: - true: - ext_key: remote_ip_prefix - ext_val: '0.0.0.0/0' - false: - ext_key: remote_mode - ext_val: remote_group_id - - - $ethertype: $rules.where($.get(Ethertype) = IPv6) - - If: len($ethertype) > 0 - Then: - - $msg: 'Unable to add security group. IPv6 is not supported.' - - $._environment.reporter.report_error($this, $msg) - - Throw: UnsupportedPropertyValue - Message: $msg - - $groupDirection: dict(egress => SecurityGroupEgress).get($direction, SecurityGroupIngress) - - - $stack: $._region.stack - - $template: - resources: - $groupName: - type: 'AWS::EC2::SecurityGroup' - properties: - GroupDescription: format('Composite security group of Murano environment {0}', $._environment.name) - $groupDirection: - - FromPort: '-1' - ToPort: '-1' - IpProtocol: icmp - CidrIp: '0.0.0.0/0' - - $._region.stack.updateTemplate($template) - - - $rulesList: $rules.select(dict( - FromPort => str($.FromPort), - ToPort => str($.ToPort), - IpProtocol => $.IpProtocol, - CidrIp => '0.0.0.0/0' - )) - - - $template: - resources: - $groupName: - type: 'AWS::EC2::SecurityGroup' - properties: - $groupDirection: $rulesList - - $._region.stack.updateTemplate($template) diff --git a/meta/io.murano/Classes/system/DummySecurityGroupManager.yaml b/meta/io.murano/Classes/system/DummySecurityGroupManager.yaml deleted file mode 100644 index 04dde1918..000000000 --- a/meta/io.murano/Classes/system/DummySecurityGroupManager.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.system - std: io.murano - -Name: DummySecurityGroupManager -Extends: SecurityGroupManager - -# This class actually adds nothing to the base SecurityGroupManager, -# so a base class could be used instead. However, it's better to explicitly -# declare this class and use it, since the base one is supposed to remain -# "abstract" and never be instantiated. diff --git a/meta/io.murano/Classes/system/HeatStack.yaml b/meta/io.murano/Classes/system/HeatStack.yaml deleted file mode 100644 index b06c7a576..000000000 --- a/meta/io.murano/Classes/system/HeatStack.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: HeatStack diff --git a/meta/io.murano/Classes/system/InstanceNotifier.yaml b/meta/io.murano/Classes/system/InstanceNotifier.yaml deleted file mode 100644 index 7ba3a7924..000000000 --- a/meta/io.murano/Classes/system/InstanceNotifier.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: InstanceNotifier diff --git a/meta/io.murano/Classes/system/Logger.yaml b/meta/io.murano/Classes/system/Logger.yaml deleted file mode 100644 index 1af8f86ea..000000000 --- a/meta/io.murano/Classes/system/Logger.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: Logger diff --git a/meta/io.murano/Classes/system/MetadefBrowser.yaml b/meta/io.murano/Classes/system/MetadefBrowser.yaml deleted file mode 100644 index e60ccdc09..000000000 --- a/meta/io.murano/Classes/system/MetadefBrowser.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.system - -Name: MetadefBrowser - -Methods: - canBeAppliedTo: - Arguments: - - tag: - Contract: $.string().notNull() - - resourceType: - Contract: $.string().notNull() - Body: - - $nss: $this.getNamespaces($resourceType) - - $objects: $nss.select($this.getObjects($.namespace)).flatten() - - $keys: $objects.properties.select($.keys()).flatten() - - Return: $tag in $keys - diff --git a/meta/io.murano/Classes/system/MistralClient.yaml b/meta/io.murano/Classes/system/MistralClient.yaml deleted file mode 100644 index 2043870fe..000000000 --- a/meta/io.murano/Classes/system/MistralClient.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: MistralClient diff --git a/meta/io.murano/Classes/system/NetworkExplorer.yaml b/meta/io.murano/Classes/system/NetworkExplorer.yaml deleted file mode 100644 index cc58d0d3e..000000000 --- a/meta/io.murano/Classes/system/NetworkExplorer.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: NetworkExplorer diff --git a/meta/io.murano/Classes/system/NeutronSecurityGroupManager.yaml b/meta/io.murano/Classes/system/NeutronSecurityGroupManager.yaml deleted file mode 100644 index e7454f1e3..000000000 --- a/meta/io.murano/Classes/system/NeutronSecurityGroupManager.yaml +++ /dev/null @@ -1,108 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.system - std: io.murano - -Name: NeutronSecurityGroupManager - -Extends: SecurityGroupManager - -Methods: - .init: - Body: - - $._environment: $.find(std:Environment) - - $._region: $.find(std:CloudRegion).require() - - $.addGroupIngress() - - addGroupIngress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - Body: - - $._addGroup(ingress, $rules, $groupName) - - addGroupEgress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - Body: - - $._addGroup(egress, $rules, $groupName) - - _addGroup: - Arguments: - - direction: - Contract: $.string().notNull().check($ in list(ingress, egress)) - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Body: - - $ext_keys: - true: - ext_key: remote_ip_prefix - ext_val: '0.0.0.0/0' - false: - ext_key: remote_mode - ext_val: remote_group_id - - - $template: - resources: - $groupName: - type: 'OS::Neutron::SecurityGroup' - properties: - description: format( - 'Composite security group of Murano environment {0}', - $._environment.name) - rules: - - protocol: icmp - remote_ip_prefix: '0.0.0.0/0' - - $._region.stack.updateTemplate($template) - - - $rulesList: $rules.select(dict( - port_range_min => $.FromPort, - port_range_max => $.ToPort, - protocol => $.IpProtocol, - ethertype => $.get(Ethertype, IPv4), - $ext_keys.get($.External).ext_key => $ext_keys.get($.External).ext_val, - direction => $direction - )) - - - $template: - resources: - $groupName: - type: 'OS::Neutron::SecurityGroup' - properties: - rules: $rulesList - - $._region.stack.updateTemplate($template) \ No newline at end of file diff --git a/meta/io.murano/Classes/system/Resources.yaml b/meta/io.murano/Classes/system/Resources.yaml deleted file mode 100644 index f61d8ad23..000000000 --- a/meta/io.murano/Classes/system/Resources.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: Resources diff --git a/meta/io.murano/Classes/system/SecurityGroupManager.yaml b/meta/io.murano/Classes/system/SecurityGroupManager.yaml deleted file mode 100644 index c43484fcf..000000000 --- a/meta/io.murano/Classes/system/SecurityGroupManager.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.system - std: io.murano - -Name: SecurityGroupManager - -Properties: - defaultGroupName: - Contract: $.string() - Default: id($.find(std:CloudRegion)) - -Methods: - addGroupIngress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName - - addGroupEgress: - Arguments: - - rules: - Contract: - - FromPort: $.int().notNull() - ToPort: $.int().notNull() - IpProtocol: $.string().notNull() - External: $.bool().notNull() - Ethertype: $.string().check($ in list(null, 'IPv4', 'IPv6')) - - groupName: - Contract: $.string().notNull() - Default: $this.defaultGroupName diff --git a/meta/io.murano/Classes/system/StatusReporter.yaml b/meta/io.murano/Classes/system/StatusReporter.yaml deleted file mode 100644 index f25478b53..000000000 --- a/meta/io.murano/Classes/system/StatusReporter.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Licensed 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. - -# Blank class for in-python system libraries, for caching purposes -Namespaces: - =: io.murano.system - -Name: StatusReporter diff --git a/meta/io.murano/Classes/test/TestFixture.yaml b/meta/io.murano/Classes/test/TestFixture.yaml deleted file mode 100644 index c099408ac..000000000 --- a/meta/io.murano/Classes/test/TestFixture.yaml +++ /dev/null @@ -1,83 +0,0 @@ -# Licensed 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. - -Namespaces: - =: io.murano.test - std: io.murano - sys: io.murano.system - res: io.murano.resources ---- # ------------------------------------------------------------------ # --- - -Name: TestFixture - -Methods: - setUp: - Body: - - tearDown: - Body: - ---- # ------------------------------------------------------------------ # --- -Name: DummyNetwork -Extends: res:Network - -Methods: - joinInstance: - Arguments: - - instance: - Contract: $.class(res:Instance).notNull() - - securityGroupName: - Contract: $.string() - - assignFloatingIp: - Contract: $.bool().notNull() - - sharedIps: - Contract: - - $.class(std:SharedIp) - Body: - - Return: - template: {} - portRef: - instanceFipOutput: - - generateSecurityGroupManager: - Body: - - Return: new(sys:DummySecurityGroupManager, $this) - - describe: - Body: - - Return: {} - ---- # ------------------------------------------------------------------ # --- - -Name: TestFixtureWithEnvironment -Extends: TestFixture - -Properties: - environment: - Usage: Runtime - Contract: $.class(std:Environment) - -Methods: - setUp: - Body: - - $testClassName: typeinfo($).name - - $envName: format('environment-of-testclass-{0}', $testClassName) - - $netName: format('default-network-of-testclass-{0}', $testClassName) - - $envSnippet: - Objects: - std:Environment: - name: $envName - defaultNetworks: - environment: - :DummyNetwork: - - - $this.environment: $this.load($envSnippet) diff --git a/meta/io.murano/LICENSE b/meta/io.murano/LICENSE deleted file mode 100644 index 67db85882..000000000 --- a/meta/io.murano/LICENSE +++ /dev/null @@ -1,175 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. diff --git a/meta/io.murano/Resources/Agent-v1.template b/meta/io.murano/Resources/Agent-v1.template deleted file mode 100644 index 0e151dd5e..000000000 --- a/meta/io.murano/Resources/Agent-v1.template +++ /dev/null @@ -1,38 +0,0 @@ - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/meta/io.murano/Resources/Agent-v2.template b/meta/io.murano/Resources/Agent-v2.template deleted file mode 100644 index 747e99022..000000000 --- a/meta/io.murano/Resources/Agent-v2.template +++ /dev/null @@ -1,41 +0,0 @@ -[DEFAULT] -debug=True -verbose=True -log_file = /var/log/murano-agent.log - -storage=/var/murano/plans - -engine_key = -%SIGNING_KEY% - -[rabbitmq] - -# Input queue name -input_queue = %RABBITMQ_INPUT_QUEUE% - -# Output routing key (usually queue name) -result_routing_key = %RESULT_QUEUE% - -# Connection parameters to RabbitMQ service - -# Hostname or IP address where RabbitMQ is located. -host = %RABBITMQ_HOST% - -# RabbitMQ port (5672 is a default) -port = %RABBITMQ_PORT% - -# Use SSL for RabbitMQ connections (True or False) -ssl = %RABBITMQ_SSL% - -# Do not verify SSL certificates -insecure = %RABBITMQ_INSECURE% - -# Path to SSL CA certificate or empty to allow self signed server certificate -ca_certs = '/etc/murano/certs/ca_certs' - -# RabbitMQ credentials. Fresh RabbitMQ installation has "guest" account with "guest" password. -login = %RABBITMQ_USER% -password = %RABBITMQ_PASSWORD% - -# RabbitMQ virtual host (vhost). Fresh RabbitMQ installation has "/" vhost preconfigured. -virtual_host = %RABBITMQ_VHOST% diff --git a/meta/io.murano/Resources/PutFile.template b/meta/io.murano/Resources/PutFile.template deleted file mode 100644 index 7f6f8988d..000000000 --- a/meta/io.murano/Resources/PutFile.template +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed 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. - -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: $planName - -Parameters: - path: $path - -Body: | - return putFile("'{0}'".format(args.path)).exitCode == 0 - -Files: - destinationFile: - BodyType: Base64 - Name: destinationFile - Body: $fileContent - -Scripts: - putFile: - Type: Application - Version: 1.0.0 - EntryPoint: putFile.sh - Files: - - destinationFile - Options: - captureStdout: false - captureStderr: true - verifyExitcode: $verifyExitcode \ No newline at end of file diff --git a/meta/io.murano/Resources/RunCommand.template b/meta/io.murano/Resources/RunCommand.template deleted file mode 100644 index 2bac76eef..000000000 --- a/meta/io.murano/Resources/RunCommand.template +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed 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. - -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: $planName - -Body: return runCommand() - -Files: - scriptFile: - BodyType: Text - Name: scriptFile.sh - Body: $command - -Scripts: - runCommand: - Type: Application - Version: 1.0.0 - EntryPoint: scriptFile.sh - Options: - captureStdout: $captureStdout - captureStderr: $captureStderr - verifyExitcode: $verifyExitcode diff --git a/meta/io.murano/Resources/conflang.conf b/meta/io.murano/Resources/conflang.conf deleted file mode 100644 index d60d1a3d0..000000000 --- a/meta/io.murano/Resources/conflang.conf +++ /dev/null @@ -1,28 +0,0 @@ -yum_repos: - chef: - baseurl: http://repositories.testbed.fi-ware.eu/repo/rpm/x86_64/ - enabled: true - failovermethod: priority - gpgcheck: false - name: Chef repo - - puppetlabs-products: - name: Puppet Labs Products El 6 - $basearch - baseurl: http://yum.puppetlabs.com/el/$releasever/products/$basearch - gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs - enabled: true - gpgcheck: false - - puppetlabs-deps: - name: Puppet Labs Dependencies El 6 - $basearch - baseurl: http://yum.puppetlabs.com/el/$releasever/dependencies/$basearch - gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs - enabled: true - gpgcheck: false - - -packages: - - chef - - puppet - - diff --git a/meta/io.murano/Resources/linux-init.sh b/meta/io.murano/Resources/linux-init.sh deleted file mode 100644 index d76c5624e..000000000 --- a/meta/io.murano/Resources/linux-init.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# Licensed 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. - -service murano-agent stop - -AgentConfigBase64='%AGENT_CONFIG_BASE64%' -RMQCaCertBase64='%CA_ROOT_CERT_BASE64%' - -if [ ! -d /etc/murano ]; then - mkdir /etc/murano -fi -echo $AgentConfigBase64 | base64 -d > /etc/murano/agent.conf -chmod 664 /etc/murano/agent.conf - -if [ ! -d /etc/murano/certs ]; then - mkdir /etc/murano/certs -fi -echo $RMQCaCertBase64 | base64 -d > /etc/murano/certs/ca_certs -chmod 664 /etc/murano/certs/ca_certs - -service murano-agent start diff --git a/meta/io.murano/Resources/murano-agent b/meta/io.murano/Resources/murano-agent deleted file mode 100644 index 41c11da11..000000000 --- a/meta/io.murano/Resources/murano-agent +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh -### BEGIN INIT INFO -# Provides: murano-agent -# Required-Start: $local_fs $network $named $time $syslog -# Required-Stop: $local_fs $network $named $time $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Description: murano-agent service -### END INIT INFO - -# NOTE(kzaitsev): not using fullpath to allow different locations -# of muranoagent binary on different OS images. muranoagent just needs -# to be in PATH. Up to image to decide where exactly. -SCRIPT="muranoagent --config-dir /etc/murano" -RUNAS=root - -PIDFILE=/var/run/murano.pid -LOGFILE=/var/log/murano.log - -start() { - if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")"; then - echo 'Service already running' >&2 - return 1 - fi - echo 'Starting service' >&2 - PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$PATH" - LOCAL_CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!" - su -c "$LOCAL_CMD" $RUNAS > "$PIDFILE" - echo 'Service started' >&2 -} - -stop() { - if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat "$PIDFILE")"; then - echo 'Service not running' >&2 - return 1 - fi - echo 'Stopping service' >&2 - kill -15 "$(cat "$PIDFILE")" && rm -f "$PIDFILE" - echo 'Service stopped' >&2 -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - restart) - stop - start - ;; - *) - echo "Usage: $0 {start|stop|restart|uninstall}" -esac diff --git a/meta/io.murano/Resources/murano-agent.conf b/meta/io.murano/Resources/murano-agent.conf deleted file mode 100644 index 398580ba0..000000000 --- a/meta/io.murano/Resources/murano-agent.conf +++ /dev/null @@ -1,14 +0,0 @@ -start on runlevel [2345] -stop on runlevel [016] - -respawn -# the default post-start of 1 second sleep delays respawning enough to -# not hit the default of 10 times in 5 seconds. Make it 2 times in 5s. -respawn limit 2 5 - -# We're logging to syslog -console none - -exec start-stop-daemon --start -c root --exec /usr/local/bin/muranoagent -- --config-dir /etc/murano 2>&1 | logger -t murano-agent - -post-start exec sleep 1 diff --git a/meta/io.murano/Resources/murano-agent.service b/meta/io.murano/Resources/murano-agent.service deleted file mode 100644 index 93a976889..000000000 --- a/meta/io.murano/Resources/murano-agent.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=OpenStack Murano Agent - -[Service] -Type=simple -ExecStart=/usr/local/bin/muranoagent --config-dir /etc/murano -Restart=on-failure - -[Install] -WantedBy=multi-user.target diff --git a/meta/io.murano/Resources/murano-init.conf b/meta/io.murano/Resources/murano-init.conf deleted file mode 100644 index a74dd1090..000000000 --- a/meta/io.murano/Resources/murano-init.conf +++ /dev/null @@ -1,19 +0,0 @@ - yum_repos: - epel-testing: - baseurl: http://download.fedoraproject.org/pub/epel/$releasever/$basearch - enabled: true - failovermethod: priority - gpgcheck: false - name: Extra Packages for Enterprise Linux - Testing - - packages: - - subversion - - git-core - - wget - - make - - gcc - - python-pip - - python-setuptools - - python-virtualenv - - hostname: $instanceHostname diff --git a/meta/io.murano/Resources/murano-init.sh b/meta/io.murano/Resources/murano-init.sh deleted file mode 100644 index 6adb5bc67..000000000 --- a/meta/io.murano/Resources/murano-init.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -# NOTE(kzaitsev): old dib elements installed murano-agent into a venv -# so if the image is an old one: symlink agent into /usr/local/bin -if [ -d /opt/stack/venvs/murano-agent ] && [ ! -f /usr/local/bin/muranoagent ]; then - ln -s /opt/stack/venvs/murano-agent/bin/muranoagent /usr/local/bin/muranoagent -fi - -# NOTE(kzaitsev): for example on debian by default PATH would be /sbin:/usr/sbin:/bin:/usr/bin -# when this script is run. Our default DIB elements install it in /usr/local/bin. -# Expand path to some of those locations. -PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:$PATH" -which muranoagent > /dev/null -if [ $? -eq 0 ]; then - echo "muranoagent binary is already installed" -else - # TODO(kzaitsev): use deb/rpm packages as soon as we can - echo "installing murano agent from pip" - echo "binary not found in PATH: $PATH" - pip install '%PIP_SOURCE%' -fi - -muranoAgentConf='%MURANO_AGENT_CONF%' -echo $muranoAgentConf | base64 -d > /etc/init/murano-agent.conf -muranoAgentService='%MURANO_AGENT_SERVICE%' -echo $muranoAgentService | base64 -d > /etc/systemd/system/murano-agent.service -muranoAgent='%MURANO_AGENT%' -echo $muranoAgent | base64 -d > /etc/init.d/murano-agent -chmod +x /etc/init.d/murano-agent diff --git a/meta/io.murano/Resources/scripts/putFile.sh b/meta/io.murano/Resources/scripts/putFile.sh deleted file mode 100644 index 32d06bebe..000000000 --- a/meta/io.murano/Resources/scripts/putFile.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# Licensed 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. - -TARGET_PATH=$1 - -mv "$(readlink destinationFile)" "$TARGET_PATH" diff --git a/meta/io.murano/Resources/windows-init.ps1 b/meta/io.murano/Resources/windows-init.ps1 deleted file mode 100644 index c12a0033f..000000000 --- a/meta/io.murano/Resources/windows-init.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -#ps1 - -# Licensed 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. - - -$WindowsAgentConfigBase64 = '%AGENT_CONFIG_BASE64%' -$WindowsAgentConfigFile = "C:\Murano\Agent\WindowsAgent.exe.config" -$WindowsAgentLogFile = "C:\Murano\Agent\log.txt" - -$CurrentComputerName = HOSTNAME -$NewComputerName = '%INTERNAL_HOSTNAME%' -$MuranoFileShare = '\\%MURANO_SERVER_ADDRESS%\share' - -$CaRootCertBase64 = "%CA_ROOT_CERT_BASE64%" -$CaRootCertFile = "C:\Murano\ca.cert" - -$RestartRequired = $false - -Import-Module CoreFunctions -Initialize-Logger 'CloudBase-Init' 'C:\Murano\PowerShell.log' - -$ErrorActionPreference = 'Stop' - -trap { - Write-LogError '' - Write-LogError $_ -EntireObject - Write-LogError '' - exit 1 -} - -Write-Log "Importing CA certificate ..." -if ($CaRootCertBase64 -eq '') { - Write-Log "Importing CA certificate ... skipped" -} -else { - ConvertFrom-Base64String -Base64String $CaRootCertBase64 -Path $CaRootCertFile - $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $CaRootCertFile - $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("AuthRoot","LocalMachine") - $store.Open("MaxAllowed") - $store.Add($cert) - $store.Close() - Write-Log "Importing CA certificate ... done" -} - -Write-Log "Updating Murano Windows Agent." -Stop-Service "Murano Agent" -Backup-File $WindowsAgentConfigFile -Remove-Item $WindowsAgentConfigFile -Force -ErrorAction 'SilentlyContinue' -Remove-Item $WindowsAgentLogFile -Force -ErrorAction 'SilentlyContinue' -ConvertFrom-Base64String -Base64String $WindowsAgentConfigBase64 -Path $WindowsAgentConfigFile -Exec sc.exe 'config','"Murano Agent"','start=','delayed-auto' -Write-Log "Service has been updated." - -Write-Log "Adding environment variable 'MuranoFileShare' = '$MuranoFileShare' ..." -[Environment]::SetEnvironmentVariable('MuranoFileShare', $MuranoFileShare, [EnvironmentVariableTarget]::Machine) -Write-Log "Environment variable added." -# (jose-phillips) If the Master Image or CloudBase-Init is already set the hostname this procedure fail. -if ($CurrentComputerName -ne $NewComputerName){ - Write-Log "Renaming computer to '$NewComputerName' ..." - $null = Rename-Computer -NewName $NewComputerName -Force - - Write-Log "New name assigned, restart required." - $RestartRequired = $true -} - -Write-Log 'All done!' -if ( $RestartRequired ) { - Write-Log "Restarting computer ..." - Restart-Computer -Force -} -else { - Start-Service 'Murano Agent' -} diff --git a/meta/io.murano/manifest.yaml b/meta/io.murano/manifest.yaml deleted file mode 100644 index 42f6e2187..000000000 --- a/meta/io.murano/manifest.yaml +++ /dev/null @@ -1,94 +0,0 @@ -# Licensed 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. - -Format: 1.4 - -Type: Library - -FullName: io.murano - -Name: Core library - -Description: | - Core MuranoPL library - -Author: 'murano.io' - -Tags: [MuranoPL] - -Classes: - io.murano.Object: Object.yaml - io.murano.Environment: Environment.yaml - io.murano.CloudRegion: CloudRegion.yaml - io.murano.CloudResource: CloudResource.yaml - io.murano.Application: Application.yaml - io.murano.Exception: Exception.yaml - io.murano.StackTrace: StackTrace.yaml - io.murano.SharedIp: SharedIp.yaml - io.murano.SharedIpRange: SharedIpRange.yaml - io.murano.File: File.yaml - io.murano.User: User.yaml - io.murano.Project: Project.yaml - - - io.murano.configuration.Linux: configuration/Linux.yaml - - io.murano.resources.Network: resources/Network.yaml - io.murano.resources.Instance: resources/Instance.yaml - io.murano.resources.LinuxInstance: resources/LinuxInstance.yaml - io.murano.resources.LinuxMuranoInstance: resources/LinuxMuranoInstance.yaml - io.murano.resources.ConfLangInstance: resources/ConfLangInstance.yaml - io.murano.resources.HeatSWConfigInstance: resources/HeatSWConfigInstance.yaml - io.murano.resources.HeatSWConfigLinuxInstance: resources/HeatSWConfigLinuxInstance.yaml - io.murano.resources.LinuxUDInstance: resources/LinuxUDInstance.yaml - io.murano.resources.WindowsInstance: resources/WindowsInstance.yaml - io.murano.resources.NeutronNetworkBase: resources/NeutronNetworkBase.yaml - io.murano.resources.NeutronNetwork: resources/NeutronNetwork.yaml - io.murano.resources.ExistingNeutronNetwork: resources/ExistingNeutronNetwork.yaml - io.murano.resources.NovaNetwork: resources/NovaNetwork.yaml - io.murano.resources.Volume: resources/Volume.yaml - io.murano.resources.CinderVolume: resources/CinderVolume.yaml - io.murano.resources.ExistingCinderVolume: resources/ExistingCinderVolume.yaml - io.murano.resources.CinderVolumeBackup: resources/CinderVolumeBackup.yaml - io.murano.resources.CinderVolumeSnapshot: resources/CinderVolumeSnapshot.yaml - io.murano.resources.MetadataAware: resources/MetadataAware.yaml - io.murano.resources.InstanceAffinityGroup: resources/InstanceAffinityGroup.yaml - - io.murano.system.Agent: system/Agent.yaml - io.murano.system.AgentListener: system/AgentListener.yaml - io.murano.system.HeatStack: system/HeatStack.yaml - io.murano.system.Resources: system/Resources.yaml - io.murano.system.InstanceNotifier: system/InstanceNotifier.yaml - io.murano.system.Logger: system/Logger.yaml - io.murano.system.StatusReporter: system/StatusReporter.yaml - io.murano.system.NetworkExplorer: system/NetworkExplorer.yaml - io.murano.system.SecurityGroupManager: system/SecurityGroupManager.yaml - io.murano.system.NeutronSecurityGroupManager: system/NeutronSecurityGroupManager.yaml - io.murano.system.AwsSecurityGroupManager: system/AwsSecurityGroupManager.yaml - io.murano.system.DummySecurityGroupManager: system/DummySecurityGroupManager.yaml - io.murano.system.MistralClient: system/MistralClient.yaml - io.murano.system.MetadefBrowser: system/MetadefBrowser.yaml - - io.murano.metadata.Description: metadata/Description.yaml - io.murano.metadata.HelpText: metadata/HelpText.yaml - io.murano.metadata.ModelBuilder: metadata/ModelBuilder.yaml - io.murano.metadata.Title: metadata/Title.yaml - io.murano.metadata.forms.Hidden: metadata/forms/Hidden.yaml - io.murano.metadata.forms.Position: metadata/forms/Position.yaml - io.murano.metadata.forms.Section: metadata/forms/Section.yaml - io.murano.metadata.engine.Serialize: metadata/engine/Serialize.yaml - io.murano.metadata.engine.Synchronize: metadata/engine/Synchronize.yaml - - - io.murano.test.TestFixture: test/TestFixture.yaml - io.murano.test.TestFixtureWithEnvironment: test/TestFixture.yaml - io.murano.test.DummyNetwork: test/TestFixture.yaml diff --git a/murano/__init__.py b/murano/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/api/__init__.py b/murano/api/__init__.py deleted file mode 100644 index a9bd720ac..000000000 --- a/murano/api/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -# Licensed 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 murano.monkey_patch # noqa diff --git a/murano/api/middleware/__init__.py b/murano/api/middleware/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/api/middleware/context.py b/murano/api/middleware/context.py deleted file mode 100644 index 29b87272d..000000000 --- a/murano/api/middleware/context.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_config import cfg -from oslo_middleware import request_id as oslo_request_id -from oslo_serialization import jsonutils - -from murano.common.i18n import _ -from murano.common import wsgi -import murano.context - -context_opts = [ - cfg.StrOpt('admin_role', default='admin', - help=_('Role used to identify an authenticated user as ' - 'administrator.'))] - -CONF = cfg.CONF -CONF.register_opts(context_opts) -CONF = cfg.CONF - - -class ContextMiddleware(wsgi.Middleware): - def process_request(self, req): - - """Convert authentication information into a request context - - Generate a murano.context.RequestContext object from the available - authentication headers and store on the 'context' attribute - of the req object. - - :param req: wsgi request object that will be given the context object - """ - roles = [r.strip() for r in req.headers.get('X-Roles').split(',')] - kwargs = { - 'user': req.headers.get('X-User-Id'), - 'project_id': req.headers.get('X-Tenant-Id'), - 'auth_token': req.headers.get('X-Auth-Token'), - 'session': req.headers.get('X-Configuration-Session'), - 'is_admin': CONF.admin_role in roles, - 'request_id': req.environ.get(oslo_request_id.ENV_REQUEST_ID), - 'roles': roles - } - sc_header = req.headers.get('X-Service-Catalog') - if sc_header: - kwargs['service_catalog'] = jsonutils.loads(sc_header) - req.context = murano.context.RequestContext(**kwargs) - - @classmethod - def factory(cls, global_conf, **local_conf): - def filter(app): - return cls(app) - return filter diff --git a/murano/api/middleware/ext_context.py b/murano/api/middleware/ext_context.py deleted file mode 100644 index 0cd7ff2d0..000000000 --- a/murano/api/middleware/ext_context.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 base64 - -from keystoneauth1 import exceptions -from keystoneauth1.identity import v3 -from keystoneauth1 import session as ks_session -from oslo_config import cfg -from oslo_log import log -from webob import exc - -from murano.common.i18n import _ -from murano.common import wsgi - -CONF = cfg.CONF -LOG = log.getLogger(__name__) - - -class ExternalContextMiddleware(wsgi.Middleware): - def get_keystone_token(self, user, password): - # TODO(starodubcevna): picking up project_name and auth_url from - # section related to Cloud Foundry service broker is probably a duct - # tape and should be rewritten as soon as we get more non-OpenStack - # services as murano recipients. The same is right for project and user - # domain names. - - auth_url = CONF.cfapi.auth_url - if not (auth_url.endswith('v2.0') or auth_url.endswith('v3')): - auth_url += '/v3' - elif auth_url.endswith('v2.0'): - auth_url = auth_url.replace('v2.0', 'v3') - elif auth_url.endswith('v3'): - pass - else: - LOG.warning('Wrong format for service broker auth url') - - kwargs = {'auth_url': auth_url, - 'username': user, - 'password': password, - 'project_name': CONF.cfapi.tenant, - 'user_domain_name': CONF.cfapi.user_domain_name, - 'project_domain_name': CONF.cfapi.project_domain_name} - password_auth = v3.Password(**kwargs) - session = ks_session.Session(auth=password_auth) - - self._query_endpoints(password_auth, session) - - return session.get_token() - - def _query_endpoints(self, auth, session): - if not hasattr(self, '_murano_endpoint'): - try: - self._murano_endpoint = auth.get_endpoint( - session, 'application-catalog') - except exceptions.EndpointNotFound: - pass - if not hasattr(self, '_glare_endpoint'): - try: - self._glare_endpoint = auth.get_endpoint( - session, 'artifact') - except exceptions.EndpointNotFound: - pass - - def get_endpoints(self): - return { - 'murano': getattr(self, '_murano_endpoint', None), - 'glare': getattr(self, '_glare_endpoint', None), - } - - def process_request(self, req): - - """Get keystone token for external request - - This middleware is used for external requests. It get credentials from - request and try to get keystone token for further authorization. - - :param req: wsgi request object that will be given the context object - """ - try: - credentials = base64.b64decode( - req.headers['Authorization'].split(' ')[1]) - user, password = credentials.decode('utf-8').split(':', 2) - req.headers['X-Auth-Token'] = self.get_keystone_token(user, - password) - req.endpoints = self.get_endpoints() - except KeyError: - msg = _("Authorization required") - LOG.warning(msg) - raise exc.HTTPUnauthorized(explanation=msg) - except exceptions.Unauthorized: - msg = _("Your credentials are wrong. Please try again") - LOG.warning(msg) - raise exc.HTTPUnauthorized(explanation=msg) - - @classmethod - def factory(cls, global_conf, **local_conf): - def filter(app): - return cls(app) - return filter diff --git a/murano/api/middleware/fault.py b/murano/api/middleware/fault.py deleted file mode 100644 index 4ffa90b22..000000000 --- a/murano/api/middleware/fault.py +++ /dev/null @@ -1,131 +0,0 @@ -# Licensed 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. - -"""A middleware that turns exceptions into parsable string. Inspired by -Cinder's faultwrapper -""" - -import sys -import traceback - -from oslo_config import cfg -import webob - -from murano.common import wsgi -from murano.packages import exceptions as pkg_exc - - -class HTTPExceptionDisguise(Exception): - """Disguises HTTP exceptions - - Disguises HTTP exceptions so they can be handled by the webob fault - application in the wsgi pipeline. - """ - - def __init__(self, exception): - self.exc = exception - self.tb = sys.exc_info()[2] - - -class Fault(object): - - def __init__(self, error): - self.error = error - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - if req.content_type == 'application/xml': - serializer = wsgi.XMLDictSerializer() - else: - serializer = wsgi.JSONDictSerializer() - resp = webob.Response(request=req) - default_webob_exc = webob.exc.HTTPInternalServerError() - resp.status_code = self.error.get('code', default_webob_exc.code) - serializer.default(resp, self.error) - return resp - - -class FaultWrapper(wsgi.Middleware): - """Replace error body with something the client can parse.""" - - @classmethod - def factory(cls, global_conf, **local_conf): - def filter(app): - return cls(app) - return filter - - error_map = { - 'ValueError': webob.exc.HTTPBadRequest, - 'LookupError': webob.exc.HTTPNotFound, - 'PackageClassLoadError': webob.exc.HTTPBadRequest, - 'PackageUILoadError': webob.exc.HTTPBadRequest, - 'PackageLoadError': webob.exc.HTTPBadRequest, - 'PackageFormatError': webob.exc.HTTPBadRequest, - } - - def _map_exception_to_error(self, class_exception): - if class_exception == Exception: - return webob.exc.HTTPInternalServerError - - if class_exception.__name__ not in self.error_map: - return self._map_exception_to_error(class_exception.__base__) - - return self.error_map[class_exception.__name__] - - def _error(self, ex): - - trace = None - webob_exc = None - if isinstance(ex, HTTPExceptionDisguise): - # An HTTP exception was disguised so it could make it here - # let's remove the disguise and set the original HTTP exception - if cfg.CONF.debug: - trace = ''.join(traceback.format_tb(ex.tb)) - ex = ex.exc - webob_exc = ex - - ex_type = ex.__class__.__name__ - - full_message = str(ex) - if full_message.find('\n') > -1: - message, msg_trace = full_message.split('\n', 1) - else: - msg_trace = traceback.format_exc() - message = full_message - - if isinstance(ex, pkg_exc.PackageException): - message = ex.message - - if cfg.CONF.debug and not trace: - trace = msg_trace - - if not webob_exc: - webob_exc = self._map_exception_to_error(ex.__class__) - - error = { - 'code': webob_exc.code, - 'title': webob_exc.title, - 'explanation': webob_exc.explanation, - 'error': { - 'message': message, - 'type': ex_type, - 'traceback': trace, - } - } - - return error - - def process_request(self, req): - try: - return req.get_response(self.application) - except Exception as exc: - return req.get_response(Fault(self._error(exc))) diff --git a/murano/api/middleware/version_negotiation.py b/murano/api/middleware/version_negotiation.py deleted file mode 100644 index 4e04c85d1..000000000 --- a/murano/api/middleware/version_negotiation.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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. - -""" -A filter middleware that inspects the requested URI for a version string -and/or Accept headers and attempts to negotiate an API controller to -return -""" - -from oslo_log import log as logging - -from murano.api import versions -from murano.common import wsgi - -LOG = logging.getLogger(__name__) - - -class VersionNegotiationFilter(wsgi.Middleware): - @classmethod - def factory(cls, global_conf, **local_conf): - def filter(app): - return cls(app) - return filter - - def __init__(self, app): - self.versions_app = versions.Controller() - super(VersionNegotiationFilter, self).__init__(app) - - def process_request(self, req): - """Try to find a version first in the accept header, then the URL.""" - LOG.debug(("Determining version of request:{method} {path} " - "Accept: {accept}").format(method=req.method, - path=req.path, - accept=req.accept)) - LOG.debug("Using url versioning") - # Remove version in url so it doesn't conflict later - req_version = self._pop_path_info(req) - - try: - version = self._match_version_string(req_version) - except ValueError: - LOG.warning("Unknown version. Returning version choices.") - return self.versions_app - - req.environ['api.version'] = version - req.path_info = ''.join(('/v', str(version), req.path_info)) - LOG.debug("Matched version: v{version}".format(version=version)) - LOG.debug('new path {path}'.format(path=req.path_info)) - return None - - def _match_version_string(self, subject): - """Tries to match major and/or minor version - - Given a string, tries to match a major and/or - minor version number. - - :param subject: The string to check - :returns version found in the subject - :raises ValueError if no acceptable version could be found - """ - if subject in ('v1',): - major_version = 1 - else: - raise ValueError() - return major_version - - def _pop_path_info(self, req): - """Returns the popped off next segment - - 'Pops' off the next segment of PATH_INFO, returns the popped - segment. Do NOT push it onto SCRIPT_NAME. - """ - path = req.path_info - if not path: - return None - while path.startswith('/'): - path = path[1:] - idx = path.find('/') - if idx == -1: - idx = len(path) - r = path[:idx] - req.path_info = path[idx:] - return r diff --git a/murano/api/v1/__init__.py b/murano/api/v1/__init__.py deleted file mode 100644 index da194a370..000000000 --- a/murano/api/v1/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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. - -stats = None - -SUPPORTED_PARAMS = {'id', 'order_by', 'category', 'marker', 'tag', - 'class_name', 'limit', 'type', 'fqn', 'category', 'owned', - 'search', 'include_disabled', 'sort_dir', 'name'} -LIST_PARAMS = {'id', 'category', 'tag', 'class', 'order_by'} -ORDER_VALUES = {'fqn', 'name', 'created'} -OPERATOR_VALUES = {'id', 'category', 'tag'} -PKG_PARAMS_MAP = {'display_name': 'name', - 'full_name': 'fully_qualified_name', - 'ui': 'ui_definition', - 'logo': 'logo', - 'package_type': 'type', - 'description': 'description', - 'author': 'author', - 'classes': 'class_definitions', - 'tags': 'tags', - 'supplier': 'supplier', - 'supplier_logo': 'supplier_logo'} diff --git a/murano/api/v1/actions.py b/murano/api/v1/actions.py deleted file mode 100644 index add71a971..000000000 --- a/murano/api/v1/actions.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from webob import exc - -from murano.common import policy -from murano.common import wsgi -from murano.db.services import environments as envs -from murano.db.services import sessions -from murano.db import session as db_session -from murano.services import actions -from murano.services import states -from murano.utils import verify_env - - -LOG = logging.getLogger(__name__) - - -class Controller(object): - - @verify_env - def execute(self, request, environment_id, action_id, body): - policy.check("execute_action", request.context, {}) - - LOG.debug('Action:Execute '.format(action_id)) - - unit = db_session.get_session() - - # no new session can be opened if environment has deploying status - env_status = envs.EnvironmentServices.get_status(environment_id) - if env_status in (states.EnvironmentStatus.DEPLOYING, - states.EnvironmentStatus.DELETING): - LOG.warning('Could not open session for environment ' - ', environment has deploying ' - 'or deleting status.'.format(id=environment_id)) - raise exc.HTTPForbidden() - - user_id = request.context.user - session = sessions.SessionServices.create(environment_id, user_id) - - if not sessions.SessionServices.validate(session): - LOG.error('Session ' - 'is invalid'.format(id=session.id)) - raise exc.HTTPForbidden() - - task_id = actions.ActionServices.execute( - action_id, session, unit, request.context, body or {}) - return {'task_id': task_id} - - @verify_env - def get_result(self, request, environment_id, task_id): - policy.check("execute_action", request.context, {}) - - LOG.debug('Action:GetResult '.format(id=task_id)) - - unit = db_session.get_session() - result = actions.ActionServices.get_result(environment_id, task_id, - unit) - - if result is not None: - return result - msg = ('Result for task with environment_id: {env_id} and task_id: ' - '{task_id} was not found.'.format(env_id=environment_id, - task_id=task_id)) - LOG.error(msg) - raise exc.HTTPNotFound(msg) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/catalog.py b/murano/api/v1/catalog.py deleted file mode 100644 index 9cc951d8a..000000000 --- a/murano/api/v1/catalog.py +++ /dev/null @@ -1,468 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# -# Licensed 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 cgi -import os -import tempfile - -import jsonschema -from keystoneclient import exceptions as keystone_ex -from keystoneclient import service_catalog -from oslo_config import cfg -from oslo_db import exception as db_exc -from oslo_log import log as logging -from webob import exc - -import murano.api.v1 -from murano.api.v1 import validation_schemas -from murano.common import exceptions -from murano.common.i18n import _ -from murano.common import policy -import murano.common.utils as murano_utils -from murano.common import wsgi -from murano.db.catalog import api as db_api -from murano.packages import exceptions as pkg_exc -from murano.packages import load_utils -from muranoclient.glance import client as glare_client - - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -SUPPORTED_PARAMS = murano.api.v1.SUPPORTED_PARAMS -LIST_PARAMS = murano.api.v1.LIST_PARAMS -ORDER_VALUES = murano.api.v1.ORDER_VALUES -PKG_PARAMS_MAP = murano.api.v1.PKG_PARAMS_MAP -OPERATOR_VALUES = murano.api.v1.OPERATOR_VALUES - - -def _check_content_type(req, content_type): - try: - req.get_content_type((content_type,)) - except exceptions.UnsupportedContentType: - msg = _("Content-Type must be '{type}'").format(type=content_type) - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - -def _get_filters(query_params): - filters = {} - for param_pair in query_params: - k, v = param_pair - if k not in SUPPORTED_PARAMS: - LOG.warning("Search by parameter '{name}' " - "is not supported. Skipping it.".format(name=k)) - continue - - if k in LIST_PARAMS: - if v.startswith('in:') and k in OPERATOR_VALUES: - in_value = v[len('in:'):] - try: - filters[k] = murano_utils.split_for_quotes(in_value) - except ValueError as err: - LOG.warning("Search by parameter '{name}' " - "caused an {message} error." - "Skipping it.".format(name=k, - message=err)) - else: - filters.setdefault(k, []).append(v) - else: - filters[k] = v - order_by = filters.get('order_by', []) - for i in order_by[:]: - if ORDER_VALUES and i not in ORDER_VALUES: - filters['order_by'].remove(i) - LOG.warning("Value of 'order_by' parameter is not valid. " - "Allowed values are: {values}. Skipping it." - .format(values=", ".join(ORDER_VALUES))) - return filters - - -def _validate_body(body): - """Check multipart/form-data has two parts - - Check multipart/form-data has two parts: text (which is json string and - should parsed into dictionary in serializer) and file, which stores as - cgi.FieldStorage instance. Also validate file size doesn't exceed - the limit: seek to the end of the file, get the position of EOF and - reset the file position to the beginning - """ - def check_file_size(f): - mb_limit = CONF.murano.package_size_limit - pkg_size_limit = mb_limit * 1024 * 1024 - f.seek(0, 2) - size = f.tell() - f.seek(0) - if size > pkg_size_limit: - raise exc.HTTPBadRequest(explanation=_( - 'Uploading file is too large. ' - 'The limit is {0} Mb').format(mb_limit)) - - if len(body) > 2: - msg = _("'multipart/form-data' request body should contain 1 or 2 " - "parts: json string and zip archive. Current body consists " - "of {amount} part(s)").format(amount=len(body.keys())) - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - file_obj = None - package_meta = None - for part in body.values(): - if isinstance(part, cgi.FieldStorage): - file_obj = part - check_file_size(file_obj.file) - - if isinstance(part, dict): - package_meta = part - if file_obj is None: - msg = _('There is no file package with application description') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - return file_obj, package_meta - - -class Controller(object): - """WSGI controller for application catalog resource in Murano v1 API.""" - - def _validate_limit(self, value): - if value is None: - return - try: - value = int(value) - except ValueError: - msg = _("Limit param must be an integer") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - if value <= 0: - msg = _("Limit param must be positive") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - return value - - def update(self, req, body, package_id): - """List of allowed changes - - List of allowed changes: - { "op": "add", "path": "/tags", "value": [ "foo", "bar" ] } - { "op": "add", "path": "/categories", "value": [ "foo", "bar" ] } - { "op": "remove", "path": "/tags" } - { "op": "remove", "path": "/categories" } - { "op": "replace", "path": "/tags", "value": ["foo", "bar"] } - { "op": "replace", "path": "/is_public", "value": true } - { "op": "replace", "path": "/description", - "value":"New description" } - { "op": "replace", "path": "/name", "value": "New name" } - """ - policy.check("modify_package", req.context, {'package_id': package_id}) - - pkg_to_update = db_api.package_get(package_id, req.context) - if pkg_to_update.is_public: - policy.check("manage_public_package", req.context) - - _check_content_type(req, 'application/murano-packages-json-patch') - if not isinstance(body, list): - msg = _('Request body must be a JSON array of operation objects.') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - for change in body: - if 'is_public' in change['path']: - if change['value'] is True and not pkg_to_update.is_public: - policy.check('publicize_package', req.context) - if 'name' in change['path']: - if len(change['value']) > 80: - msg = _('Package name should be 80 characters maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - package = db_api.package_update(package_id, body, req.context) - return package.to_dict() - - def get(self, req, package_id): - policy.check("get_package", req.context, {'package_id': package_id}) - - package = db_api.package_get(package_id, req.context) - return package.to_dict() - - def search(self, req): - policy.check("get_package", req.context) - manage_public = True - try: - policy.check("manage_public_package", req.context) - except exc.HTTPForbidden: - manage_public = False - - filters = _get_filters(req.GET.items()) - - limit = self._validate_limit(filters.get('limit')) - if limit is None: - limit = CONF.murano.limit_param_default - limit = min(CONF.murano.api_limit_max, limit) - - result = {} - - catalog = req.GET.pop('catalog', '').lower() == 'true' - packages = db_api.package_search( - filters, req.context, manage_public, limit, catalog=catalog) - if len(packages) == limit: - result['next_marker'] = packages[-1].id - result['packages'] = [package.to_dict() for package in packages] - return result - - def upload(self, req, body=None): - """Upload new file archive - - Upload new file archive for the new package - together with package metadata. - """ - policy.check("upload_package", req.context) - - _check_content_type(req, 'multipart/form-data') - file_obj, package_meta = _validate_body(body) - if package_meta: - try: - jsonschema.validate(package_meta, - validation_schemas.PKG_UPLOAD_SCHEMA) - except jsonschema.ValidationError as e: - msg = _("Package schema is not valid: {reason}").format( - reason=e) - LOG.exception(msg) - raise exc.HTTPBadRequest(explanation=msg) - else: - package_meta = {} - - if package_meta.get('is_public'): - policy.check('publicize_package', req.context) - - with tempfile.NamedTemporaryFile(delete=False) as tempf: - LOG.debug("Storing package archive in a temporary file") - content = file_obj.file.read() - if not content: - msg = _("Uploading file can't be empty") - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - tempf.write(content) - package_meta['archive'] = content - try: - with load_utils.load_from_file( - tempf.name, target_dir=None, - drop_dir=True) as pkg_to_upload: - # extend dictionary for update db - for k, v in PKG_PARAMS_MAP.items(): - if hasattr(pkg_to_upload, k): - if k == "tags" and package_meta.get(k): - package_meta[v] = list(set( - package_meta[v] + getattr(pkg_to_upload, k))) - else: - package_meta[v] = getattr(pkg_to_upload, k) - if len(package_meta['name']) > 80: - msg = _('Package name should be 80 characters maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - try: - package = db_api.package_upload( - package_meta, req.context.project_id) - except db_exc.DBDuplicateEntry: - msg = _('Package with specified full ' - 'name is already registered') - LOG.exception(msg) - raise exc.HTTPConflict(msg) - return package.to_dict() - except pkg_exc.PackageLoadError as e: - msg = _("Couldn't load package from file: {reason}").format( - reason=e) - LOG.exception(msg) - raise exc.HTTPBadRequest(explanation=msg) - finally: - LOG.debug("Deleting package archive temporary file") - os.remove(tempf.name) - - def get_ui(self, req, package_id): - if CONF.engine.packages_service == 'murano': - target = {'package_id': package_id} - policy.check("get_package", req.context, target) - - package = db_api.package_get(package_id, req.context) - return package.ui_definition - else: - g_client = self._get_glare_client(req) - blob_data = g_client.artifacts.download_blob(package_id, 'archive') - with tempfile.NamedTemporaryFile() as tempf: - for chunk in blob_data: - tempf.write(chunk) - tempf.file.flush() - os.fsync(tempf.file.fileno()) - with load_utils.load_from_file(tempf.name, target_dir=None, - drop_dir=True) as pkg: - return pkg.ui - - def get_logo(self, req, package_id): - target = {'package_id': package_id} - policy.check("get_package", req.context, target) - - package = db_api.package_get(package_id, req.context) - return package.logo - - def get_supplier_logo(self, req, package_id): - package = db_api.package_get(package_id, req.context) - return package.supplier_logo - - def download(self, req, package_id): - target = {'package_id': package_id} - policy.check("download_package", req.context, target) - - package = db_api.package_get(package_id, req.context) - return package.archive - - def delete(self, req, package_id): - target = {'package_id': package_id} - policy.check("delete_package", req.context, target) - - package = db_api.package_get(package_id, req.context) - if package.is_public: - policy.check("manage_public_package", req.context, target) - db_api.package_delete(package_id, req.context) - - def get_category(self, req, category_id): - policy.check("get_category", req.context) - category = db_api.category_get(category_id, packages=True) - return category.to_dict() - - def list_categories(self, req): - """List all categories - - List all categories with pagination and sorting - Acceptable filter params: - :param sort_keys: an array of fields used to sort the list - :param sort_dir: the direction of the sort ('asc' or 'desc') - :param limit: the number of categories to list - :param marker: the ID of the last item in the previous page - """ - def _get_category_filters(req): - query_params = {} - valid_query_params = ['sort_keys', 'sort_dir', 'limit', 'marker'] - for key, value in req.GET.items(): - if key not in valid_query_params: - raise exc.HTTPBadRequest( - _('Bad value passed to filter. ' - 'Got {key}, expected:{valid}').format( - key=key, valid=', '.join(valid_query_params))) - if key == 'sort_keys': - available_sort_keys = ['name', 'created', - 'updated', 'package_count', 'id'] - value = [v.strip() for v in value.split(',')] - for sort_key in value: - if sort_key not in available_sort_keys: - raise exc.HTTPBadRequest( - explanation=_('Invalid sort key: {sort_key}. ' - 'Must be one of the following: ' - '{available}').format( - sort_key=sort_key, - available=', '.join(available_sort_keys))) - if key == 'sort_dir': - if value not in ['asc', 'desc']: - msg = _('Invalid sort direction: {0}').format(value) - raise exc.HTTPBadRequest(explanation=msg) - query_params[key] = value - return query_params - - policy.check("get_category", req.context) - - filters = _get_category_filters(req) - - marker = filters.get('marker') - limit = self._validate_limit(filters.get('limit')) - - result = {} - categories = db_api.categories_list(filters, - limit=limit, - marker=marker) - if len(categories) == limit: - result['next_marker'] = categories[-1].id - - result['categories'] = [category.to_dict() for category in categories] - return result - - def add_category(self, req, body=None): - policy.check("add_category", req.context) - category_name = body.get('name') - if not category_name: - raise exc.HTTPBadRequest( - explanation='Please, specify a name of the category to create') - if len(category_name) > 80: - msg = _('Category name should be 80 characters maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - try: - category = db_api.category_add(category_name) - except db_exc.DBDuplicateEntry: - msg = _('Category with specified name is already exist') - LOG.error(msg) - raise exc.HTTPConflict(explanation=msg) - return category.to_dict() - - def delete_category(self, req, category_id): - target = {'category_id': category_id} - policy.check("delete_category", req.context, target) - category = db_api.category_get(category_id, packages=True) - if category.packages: - msg = _("It's impossible to delete categories assigned " - "to the package, uploaded to the catalog") - raise exc.HTTPForbidden(explanation=msg) - db_api.category_delete(category_id) - - def _get_glare_client(self, request): - glare_settings = CONF.glare - token = request.context.auth_token - url = glare_settings.url - if not url: - url = self._get_glare_url(request) - # TODO(gyurco): use auth_utils.get_session_client_parameters - client = glare_client.Client( - endpoint=url, token=token, insecure=glare_settings.insecure, - key_file=glare_settings.keyfile or None, - ca_file=glare_settings.cafile or None, - cert_file=glare_settings.certfile or None, - type_name='murano', - type_version=1) - return client - - def _get_glare_url(self, request): - sc = request.context.service_catalog - token = request.context.auth_token - try: - return service_catalog.ServiceCatalogV2( - {'serviceCatalog': sc}).url_for( - service_type='artifact', - endpoint_type=CONF.glare.endpoint_type, - region_name=CONF.home_region) - except keystone_ex.EndpointNotFound: - return service_catalog.ServiceCatalogV3( - token, - {'catalog': sc}).url_for( - service_type='artifact', - endpoint_type=CONF.glare.endpoint_type, - region_name=CONF.home_region) - - -def create_resource(): - specific_content_types = { - 'get_ui': ['text/plain'], - 'download': ['application/octet-stream'], - 'get_logo': ['application/octet-stream'], - 'get_supplier_logo': ['application/octet-stream']} - deserializer = wsgi.RequestDeserializer( - specific_content_types=specific_content_types) - return wsgi.Resource(Controller(), deserializer=deserializer) diff --git a/murano/api/v1/deployments.py b/murano/api/v1/deployments.py deleted file mode 100644 index 45dfb30fe..000000000 --- a/murano/api/v1/deployments.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from sqlalchemy import desc -from sqlalchemy.orm import load_only -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common.helpers import token_sanitizer -from murano.common import policy -from murano.common import utils -from murano.common import wsgi -from murano.db import models -from murano.db import session as db_session -from murano.utils import check_env - -LOG = logging.getLogger(__name__) - -API_NAME = 'Deployments' - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'Index') - def index(self, request, environment_id=None): - all_environments = environment_id is None - LOG.debug('Deployments:List ' - .format(all_environments)) - - if all_environments: - policy.check("list_deployments_all_environments", request.context) - else: - check_env(request, environment_id) - target = {"environment_id": environment_id} - policy.check("list_deployments", request.context, target) - - unit = db_session.get_session() - - if all_environments: - query = unit.query(models.Environment) \ - .options(load_only('tenant_id')) \ - .filter_by(tenant_id=request.context.project_id) \ - .join(models.Task) \ - .order_by(desc(models.Task.created)) - result = query.all() - # The join statement above fetches the deployments into - # Environment.tasks. Iterate over that to get the deployments. - deployments = [] - for row in result: - for deployment in row.tasks: - deployment = set_dep_state(deployment, unit).to_dict() - deployments.append(deployment) - else: - query = unit.query(models.Task) \ - .filter_by(environment_id=environment_id) \ - .order_by(desc(models.Task.created)) - result = query.all() - deployments = [set_dep_state(deployment, unit).to_dict() - for deployment in result] - return {'deployments': deployments} - - @request_statistics.stats_count(API_NAME, 'Statuses') - def statuses(self, request, environment_id, deployment_id): - target = {"environment_id": environment_id, - "deployment_id": deployment_id} - policy.check("statuses_deployments", request.context, target) - - unit = db_session.get_session() - query = unit.query(models.Status) \ - .filter_by(task_id=deployment_id) \ - .order_by(models.Status.created) - deployment = verify_and_get_deployment(unit, environment_id, - deployment_id) - - if 'service_id' in request.GET: - service_id_set = set(request.GET.getall('service_id')) - environment = deployment.description - entity_ids = [] - for service in environment.get('services', []): - if service['?']['id'] in service_id_set: - id_map = utils.build_entity_map(service) - entity_ids = entity_ids + id_map.keys() - if entity_ids: - query = query.filter(models.Status.entity_id.in_(entity_ids)) - else: - return {'reports': []} - - result = query.all() - return {'reports': [status.to_dict() for status in result]} - - -def _patch_description(description): - if not description: - description = {} - description['services'] = description.pop('applications', []) - return token_sanitizer.TokenSanitizer().sanitize(description) - - -def verify_and_get_deployment(db_session, environment_id, deployment_id): - deployment = db_session.query(models.Task).get(deployment_id) - if not deployment: - LOG.error('Deployment with id {id} not found' - .format(id=deployment_id)) - raise exc.HTTPNotFound - if deployment.environment_id != environment_id: - LOG.error('Deployment with id {d_id} not found in environment ' - '{env_id}'.format(d_id=deployment_id, - env_id=environment_id)) - raise exc.HTTPBadRequest - - deployment.description = _patch_description(deployment.description) - return deployment - - -def create_resource(): - return wsgi.Resource(Controller()) - - -def set_dep_state(deployment, unit): - num_errors = unit.query(models.Status).filter_by( - level='error', - task_id=deployment.id).count() - - num_warnings = unit.query(models.Status).filter_by( - level='warning', - task_id=deployment.id).count() - - if deployment.finished: - if num_errors: - deployment.state = 'completed_w_errors' - elif num_warnings: - deployment.state = 'completed_w_warnings' - else: - deployment.state = 'success' - else: - if num_errors: - deployment.state = 'running_w_errors' - elif num_warnings: - deployment.state = 'running_w_warnings' - else: - deployment.state = 'running' - - deployment.description = _patch_description(deployment.description) - return deployment diff --git a/murano/api/v1/environments.py b/murano/api/v1/environments.py deleted file mode 100644 index b9b18c503..000000000 --- a/murano/api/v1/environments.py +++ /dev/null @@ -1,273 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 datetime - -import jsonpatch -from oslo_db import exception as db_exc -from oslo_log import log as logging -from sqlalchemy import desc -from webob import exc - -from murano.api.v1 import request_statistics -from murano.api.v1 import sessions -from murano.common.i18n import _ -from murano.common import policy -from murano.common import utils -from murano.common import wsgi -from murano.db import models -from murano.db.services import core_services -from murano.db.services import environments as envs -from murano.db.services import sessions as session_services -from murano.db import session as db_session -from murano.engine.system import status_reporter -from murano.services import states -from murano.utils import check_env -from murano.utils import check_session -from murano.utils import verify_env - -LOG = logging.getLogger(__name__) - -API_NAME = 'Environments' - - -class Controller(object): - - def __init__(self, *args, **kwargs): - super(Controller, self).__init__(*args, **kwargs) - self._notifier = status_reporter.Notification() - - @request_statistics.stats_count(API_NAME, 'Index') - def index(self, request): - all_tenants = request.GET.get('all_tenants', 'false').lower() == 'true' - tenant = request.GET.get('tenant', None) - LOG.debug('Environments:List '.format(tenants=all_tenants, - tenant=tenant)) - - if all_tenants: - policy.check('list_environments_all_tenants', request.context) - filters = {} - elif tenant: - policy.check('list_environments_all_tenants', request.context) - filters = {'tenant_id': tenant} - else: - policy.check('list_environments', request.context) - # Only environments from same tenant as user should be returned - filters = {'tenant_id': request.context.project_id} - - environments = envs.EnvironmentServices.get_environments_by(filters) - environments = [env.to_dict() for env in environments] - - return {"environments": environments} - - @request_statistics.stats_count(API_NAME, 'Create') - def create(self, request, body): - LOG.debug('Environments:Create '.format(body=body)) - policy.check('create_environment', request.context) - - if not('name' in body and body['name'].strip()): - msg = _('Please, specify a name of the environment to create') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - name = str(body['name']) - if len(name) > 255: - msg = _('Environment name should be 255 characters maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - try: - environment = envs.EnvironmentServices.create( - body.copy(), - request.context) - except db_exc.DBDuplicateEntry: - msg = _('Environment with specified name already exists') - LOG.error(msg) - raise exc.HTTPConflict(explanation=msg) - - return environment.to_dict() - - @request_statistics.stats_count(API_NAME, 'Show') - @verify_env - def show(self, request, environment_id): - LOG.debug('Environments:Show '.format(id=environment_id)) - target = {"environment_id": environment_id} - policy.check('show_environment', request.context, target) - - session = db_session.get_session() - environment = session.query(models.Environment).get(environment_id) - env = environment.to_dict() - env['status'] = envs.EnvironmentServices.get_status(env['id']) - - # if env is currently being deployed we can provide information about - # the session right away - env['acquired_by'] = None - if env['status'] == states.EnvironmentStatus.DEPLOYING: - session_list = session_services.SessionServices.get_sessions( - environment_id, state=states.SessionState.DEPLOYING) - if session_list: - env['acquired_by'] = session_list[0].id - - session_id = None - if hasattr(request, 'context') and request.context.session: - session_id = request.context.session - if session_id: - env_session = session.query(models.Session).get(session_id) - check_session(request, environment_id, env_session, session_id) - - # add services to env - get_data = core_services.CoreServices.get_data - env['services'] = get_data(environment_id, '/services', session_id) - - return env - - @request_statistics.stats_count(API_NAME, 'Update') - @verify_env - def update(self, request, environment_id, body): - """"Rename an environment.""" - LOG.debug('Environments:Update '.format(id=environment_id, body=body)) - target = {"environment_id": environment_id} - policy.check('update_environment', request.context, target) - - session = db_session.get_session() - environment = session.query(models.Environment).get(environment_id) - new_name = str(body['name']) - if new_name.strip(): - if len(new_name) > 255: - msg = _('Environment name should be 255 characters maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - try: - environment.update({'name': new_name}) - environment.save(session) - except db_exc.DBDuplicateEntry: - msg = _('Environment with specified name already exists') - LOG.error(msg) - raise exc.HTTPConflict(explanation=msg) - else: - msg = _('Environment name must contain at least one ' - 'non-white space symbol') - LOG.error(msg) - raise exc.HTTPClientError(explanation=msg) - - return environment.to_dict() - - @request_statistics.stats_count(API_NAME, 'Delete') - def delete(self, request, environment_id): - target = {"environment_id": environment_id} - policy.check('delete_environment', request.context, target) - environment = check_env(request, environment_id) - - if request.GET.get('abandon', '').lower() == 'true': - LOG.debug( - 'Environments:Abandon '.format(id=environment_id)) - envs.EnvironmentServices.remove(environment_id) - else: - LOG.debug( - 'Environments:Delete '.format(id=environment_id)) - sessions_controller = sessions.Controller() - session = sessions_controller.configure( - request, environment_id) - session_id = session['id'] - envs.EnvironmentServices.delete(environment_id, session_id) - sessions_controller.deploy(request, environment_id, session_id) - - env = environment.to_dict() - env['deleted'] = datetime.datetime.utcnow() - self._notifier.report('environment.delete.end', env) - - @request_statistics.stats_count(API_NAME, 'LastStatus') - @verify_env - def last(self, request, environment_id): - session_id = None - if hasattr(request, 'context') and request.context.session: - session_id = request.context.session - services = core_services.CoreServices.get_data(environment_id, - '/services', - session_id) - session = db_session.get_session() - result = {} - for service in services or []: - service_id = service['?']['id'] - entity_ids = utils.build_entity_map(service).keys() - last_status = session.query(models.Status). \ - filter(models.Status.entity_id.in_(entity_ids)). \ - order_by(desc(models.Status.created)). \ - first() - if last_status: - result[service_id] = last_status.to_dict() - else: - result[service_id] = None - return {'lastStatuses': result} - - @request_statistics.stats_count(API_NAME, 'GetModel') - @verify_env - def get_model(self, request, environment_id, path): - LOG.debug('Environments:GetModel , Path: %(path)s', - {'env_id': environment_id, 'path': path}) - target = {"environment_id": environment_id} - policy.check('show_environment', request.context, target) - - session_id = None - if hasattr(request, 'context') and request.context.session: - session_id = request.context.session - - get_description = envs.EnvironmentServices.get_environment_description - env_model = get_description(environment_id, session_id) - try: - result = utils.TraverseHelper.get(path, env_model) - except (KeyError, ValueError): - raise exc.HTTPNotFound - - return result - - @request_statistics.stats_count(API_NAME, 'UpdateModel') - @verify_env - def update_model(self, request, environment_id, body=None): - if not body: - msg = _('Request body is empty: please, provide ' - 'environment object model patch') - LOG.error(msg) - raise exc.HTTPBadRequest(msg) - LOG.debug('Environments:UpdateModel ', - {'env_id': environment_id, 'body': body}) - target = {"environment_id": environment_id} - policy.check('update_environment', request.context, target) - - session_id = None - if hasattr(request, 'context') and request.context.session: - session_id = request.context.session - - get_description = envs.EnvironmentServices.get_environment_description - env_model = get_description(environment_id, session_id) - - for change in body: - change['path'] = '/' + '/'.join(change['path']) - - patch = jsonpatch.JsonPatch(body) - try: - patch.apply(env_model, in_place=True) - except jsonpatch.JsonPatchException as e: - raise exc.HTTPNotFound(str(e)) - - save_description = envs.EnvironmentServices. \ - save_environment_description - save_description(session_id, env_model) - - return env_model - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/instance_statistics.py b/murano/api/v1/instance_statistics.py deleted file mode 100644 index 778925c71..000000000 --- a/murano/api/v1/instance_statistics.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging - -from murano.api.v1 import request_statistics -from murano.common import policy -from murano.common import wsgi -from murano.db.services import instances - - -LOG = logging.getLogger(__name__) -API_NAME = 'EnvironmentStatistics' - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'GetAggregated') - def get_aggregated(self, request, environment_id): - LOG.debug('EnvironmentStatistics:GetAggregated') - target = {"environment_id": environment_id} - policy.check("get_aggregated_statistics", request.context, target) - - # TODO(stanlagun): Check that caller is authorized to access - # tenant's statistics - - return instances.InstanceStatsServices.get_aggregated_stats( - environment_id) - - @request_statistics.stats_count(API_NAME, 'GetForInstance') - def get_for_instance(self, request, environment_id, instance_id): - LOG.debug('EnvironmentStatistics:GetForInstance') - target = {"environment_id": environment_id, "instance_id": instance_id} - policy.check("get_instance_statistics", request.context, target) - - # TODO(stanlagun): Check that caller is authorized to access - # tenant's statistics - - return instances.InstanceStatsServices.get_raw_environment_stats( - environment_id, instance_id) - - @request_statistics.stats_count(API_NAME, 'GetForEnvironment') - def get_for_environment(self, request, environment_id): - LOG.debug('EnvironmentStatistics:GetForEnvironment') - target = {"environment_id": environment_id} - policy.check("get_statistics", request.context, target) - - # TODO(stanlagun): Check that caller is authorized to access - # tenant's statistics - - return instances.InstanceStatsServices.get_raw_environment_stats( - environment_id) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/request_statistics.py b/murano/api/v1/request_statistics.py deleted file mode 100644 index 9da2caa0e..000000000 --- a/murano/api/v1/request_statistics.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 time - -from oslo_log import log as logging - -from murano.api import v1 -from murano.common import wsgi -from murano.db.services import stats - -LOG = logging.getLogger(__name__) - - -class RequestStatisticsCollection(object): - request_count = 0 - error_count = 0 - average_time = 0.0 - - requests_per_tenant = {} - errors_per_tenant = {} - - def add_api_request(self, tenant, ex_time): - self.average_time = (self.average_time * self.request_count + - ex_time) / (self.request_count + 1) - if tenant: - tenant_count = self.requests_per_tenant.get(tenant, 0) # nosec - tenant_count += 1 - self.requests_per_tenant[tenant] = tenant_count - - def add_api_error(self, tenant, ex_time): - self.average_time = (self.average_time * self.request_count + - ex_time) / (self.request_count + 1) - if tenant: - tenant_count = self.errors_per_tenant.get(tenant, 0) - tenant_count += 1 - self.errors_per_tenant[tenant] = tenant_count - - -def stats_count(api, method): - def wrapper(func): - def wrap(*args, **kwargs): - try: - ts = time.time() - result = func(*args, **kwargs) - te = time.time() - tenant = args[1].context.project_id - update_count(api, method, te - ts, - tenant) - return result - except Exception: - te = time.time() - tenant = args[1].context.project_id - LOG.exception('API {api} method {method} raised an ' - 'exception'.format(api=api, method=method)) - update_error_count(api, method, te - te, tenant) - raise - return wrap - - return wrapper - - -def update_count(api, method, ex_time, tenant=None): - LOG.debug("Updating count stats for {api}, {method} on object {object}" - .format(api=api, method=method, object=v1.stats)) - v1.stats.add_api_request(tenant, ex_time) - v1.stats.request_count += 1 - - -def update_error_count(api, method, ex_time, tenant=None): - LOG.debug("Updating count stats for {api}, {method} on object " - "{object}".format(api=api, method=method, object=v1.stats)) - v1.stats.add_api_error(tenant, ex_time) - v1.stats.error_count += 1 - v1.stats.request_count += 1 - - -def init_stats(): - if not v1.stats: - v1.stats = RequestStatisticsCollection() - - -class Controller(object): - def get(self, request): - model = stats.Statistics() - entries = model.get_all() - ent_list = [] - for entry in entries: - ent_list.append(entry.to_dict()) - return ent_list - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/router.py b/murano/api/v1/router.py deleted file mode 100644 index ef35faa37..000000000 --- a/murano/api/v1/router.py +++ /dev/null @@ -1,297 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 routes - -from murano.api.v1 import actions -from murano.api.v1 import catalog -from murano.api.v1 import deployments -from murano.api.v1 import environments -from murano.api.v1 import instance_statistics -from murano.api.v1 import request_statistics -from murano.api.v1 import schemas -from murano.api.v1 import services -from murano.api.v1 import sessions -from murano.api.v1 import static_actions -from murano.api.v1 import template_applications -from murano.api.v1 import templates -from murano.common import wsgi - - -class API(wsgi.Router): - @classmethod - def factory(cls, global_conf, **local_conf): - return cls(routes.Mapper()) - - def __init__(self, mapper): - services_resource = services.create_resource() - mapper.connect('/environments/{environment_id}/services', - controller=services_resource, - action='get', - conditions={'method': ['GET']}, path='') - mapper.connect('/environments/{environment_id}/services/{path:.*?}', - controller=services_resource, - action='get', - conditions={'method': ['GET']}, path='') - - mapper.connect('/environments/{environment_id}/services', - controller=services_resource, - action='post', - conditions={'method': ['POST']}, path='') - mapper.connect('/environments/{environment_id}/services/{path:.*?}', - controller=services_resource, - action='post', - conditions={'method': ['POST']}, path='') - - mapper.connect('/environments/{environment_id}/services', - controller=services_resource, - action='put', - conditions={'method': ['PUT']}, path='') - mapper.connect('/environments/{environment_id}/services/{path:.*?}', - controller=services_resource, - action='put', - conditions={'method': ['PUT']}, path='') - - mapper.connect('/environments/{environment_id}/services', - controller=services_resource, - action='delete', - conditions={'method': ['DELETE']}, path='') - mapper.connect('/environments/{environment_id}/services/{path:.*?}', - controller=services_resource, - action='delete', - conditions={'method': ['DELETE']}, path='') - - environments_resource = environments.create_resource() - mapper.connect('/environments', - controller=environments_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments', - controller=environments_resource, - action='create', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='update', - conditions={'method': ['PUT']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='show', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}', - controller=environments_resource, - action='delete', - conditions={'method': ['DELETE']}) - mapper.connect('/environments/{environment_id}/lastStatus', - controller=environments_resource, - action='last', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/model/{path:.*?}', - controller=environments_resource, - action='get_model', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/model/', - controller=environments_resource, - action='update_model', - conditions={'method': ['PATCH']}) - - templates_resource = templates.create_resource() - mapper.connect('/templates', - controller=templates_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/templates', - controller=templates_resource, - action='create', - conditions={'method': ['POST']}) - mapper.connect('/templates/{env_template_id}', - controller=templates_resource, - action='update', - conditions={'method': ['PUT']}) - mapper.connect('/templates/{env_template_id}', - controller=templates_resource, - action='show', - conditions={'method': ['GET']}) - mapper.connect('/templates/{env_template_id}', - controller=templates_resource, - action='delete', - conditions={'method': ['DELETE']}) - mapper.connect('/templates/{env_template_id}/create-environment', - controller=templates_resource, - action='create_environment', - conditions={'method': ['POST']}) - mapper.connect('/templates/{env_template_id}/clone', - controller=templates_resource, - action='clone', - conditions={'method': ['POST']}) - - applications_resource = template_applications.create_resource() - mapper.connect('/templates/{env_template_id}/services', - controller=applications_resource, - action='index', - conditions={'method': ['GET']}, path='') - mapper.connect('/templates/{env_template_id}/services/{path:.*?}', - controller=applications_resource, - action='show', - conditions={'method': ['GET']}, path='') - mapper.connect('/templates/{env_template_id}/services', - controller=applications_resource, - action='post', - conditions={'method': ['POST']}, path='') - mapper.connect('/templates/{env_template_id}/services/{path:.*?}', - controller=applications_resource, - action='put', - conditions={'method': ['PUT']}, path='') - mapper.connect('/templates/{env_template_id}/services/{path:.*?}', - controller=applications_resource, - action='delete', - conditions={'method': ['DELETE']}, path='') - - deployments_resource = deployments.create_resource() - mapper.connect('/environments/{environment_id}/deployments', - controller=deployments_resource, - action='index', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/deployments/' - '{deployment_id}', - controller=deployments_resource, - action='statuses', - conditions={'method': ['GET']}) - mapper.connect('/deployments', - controller=deployments_resource, - action='index', - conditions={'method': ['GET']}) - - sessions_resource = sessions.create_resource() - mapper.connect('/environments/{environment_id}/configure', - controller=sessions_resource, - action='configure', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}', - controller=sessions_resource, - action='show', - conditions={'method': ['GET']}) - mapper.connect('/environments/{environment_id}/sessions/{session_id}', - controller=sessions_resource, - action='delete', - conditions={'method': ['DELETE']}) - mapper.connect('/environments/{environment_id}/sessions/' - '{session_id}/deploy', - controller=sessions_resource, - action='deploy', - conditions={'method': ['POST']}) - - statistics_resource = instance_statistics.create_resource() - mapper.connect( - '/environments/{environment_id}/instance-statistics/raw/' - '{instance_id}', - controller=statistics_resource, - action='get_for_instance', - conditions={'method': ['GET']}) - mapper.connect( - '/environments/{environment_id}/instance-statistics/raw', - controller=statistics_resource, - action='get_for_environment', - conditions={'method': ['GET']}) - mapper.connect( - '/environments/{environment_id}/instance-statistics/aggregated', - controller=statistics_resource, - action='get_aggregated', - conditions={'method': ['GET']}) - - actions_resource = actions.create_resource() - mapper.connect('/environments/{environment_id}/actions/{action_id}', - controller=actions_resource, - action='execute', - conditions={'method': ['POST']}) - mapper.connect('/environments/{environment_id}/actions/{task_id}', - controller=actions_resource, - action='get_result', - conditions={'method': ['GET']}) - - static_actions_resource = static_actions.create_resource() - mapper.connect('/actions', - controller=static_actions_resource, - action='execute', - conditions={'method': ['POST']}) - - catalog_resource = catalog.create_resource() - mapper.connect('/catalog/packages/{package_id}', - controller=catalog_resource, - action='get', - conditions={'method': ['GET']}) - mapper.connect('/catalog/packages/{package_id}', - controller=catalog_resource, - action='delete', - conditions={'method': ['DELETE']}) - mapper.connect('/catalog/packages/{package_id}', - controller=catalog_resource, - action='update', - conditions={'method': ['PATCH']}) - mapper.connect('/catalog/packages', - controller=catalog_resource, - action='search', - conditions={'method': ['GET']}) - mapper.connect('/catalog/packages', - controller=catalog_resource, - action='upload', - conditions={'method': ['POST']}) - mapper.connect('/catalog/packages/{package_id}/ui', - controller=catalog_resource, - action='get_ui', - conditions={'method': ['GET']}) - mapper.connect('/catalog/packages/{package_id}/logo', - controller=catalog_resource, - action='get_logo', - conditions={'method': ['GET']}) - mapper.connect('/catalog/packages/{package_id}/supplier_logo', - controller=catalog_resource, - action='get_supplier_logo', - conditions={'method': ['GET']}) - mapper.connect('/catalog/packages/{package_id}/download', - controller=catalog_resource, - action='download', - conditions={'method': ['GET']}) - mapper.connect('/catalog/categories', - controller=catalog_resource, - action='list_categories', - conditions={'method': ['GET']}) - mapper.connect('/catalog/categories/{category_id}', - controller=catalog_resource, - action='get_category', - conditions={'method': ['GET']}) - mapper.connect('/catalog/categories', - controller=catalog_resource, - action='add_category', - conditions={'method': ['POST']}) - mapper.connect('/catalog/categories/{category_id}', - controller=catalog_resource, - action='delete_category', - conditions={'method': ['DELETE']}) - req_stats_resource = request_statistics.create_resource() - mapper.connect('/stats', - controller=req_stats_resource, - action='get', - conditions={'method': ['GET']}) - schemas_resource = schemas.create_resource() - mapper.connect('/schemas/{class_name}/{method_names}', - controller=schemas_resource, - action='get_schema', - conditions={'method': ['GET']}) - mapper.connect('/schemas/{class_name}', - controller=schemas_resource, - action='get_schema', - conditions={'method': ['GET']}) - - super(API, self).__init__(mapper) diff --git a/murano/api/v1/schemas.py b/murano/api/v1/schemas.py deleted file mode 100644 index ecb59c62a..000000000 --- a/murano/api/v1/schemas.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from oslo_messaging.rpc import client -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common import policy -from murano.common import rpc -from murano.common import wsgi - - -LOG = logging.getLogger(__name__) -API_NAME = 'Schemas' - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'GetSchema') - def get_schema(self, request, class_name, method_names=None): - LOG.debug('GetSchema:GetSchema') - target = {"class_name": class_name} - policy.check("get_schema", request.context, target) - class_version = request.GET.get('classVersion') - package_name = request.GET.get('packageName') - credentials = { - 'token': request.context.auth_token, - 'project_id': request.context.project_id - } - - try: - methods = (list( - map(str.strip, method_names.split(','))) - if method_names else []) - return rpc.engine().generate_schema( - credentials, class_name, methods, - class_version, package_name) - except client.RemoteError as e: - if e.exc_type in ('NoClassFound', - 'NoPackageForClassFound', - 'NoPackageFound'): - raise exc.HTTPNotFound(e.value) - raise - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/services.py b/murano/api/v1/services.py deleted file mode 100644 index 7089e1e54..000000000 --- a/murano/api/v1/services.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 functools as func - -from oslo_log import log as logging -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common.helpers import token_sanitizer -from murano.common.i18n import _ -from murano.common import wsgi -from murano.db.services import core_services -from murano import utils - - -LOG = logging.getLogger(__name__) - -API_NAME = 'Services' - - -def normalize_path(f): - @func.wraps(f) - def f_normalize_path(*args, **kwargs): - if 'path' in kwargs: - if kwargs['path']: - kwargs['path'] = '/services/' + kwargs['path'] - else: - kwargs['path'] = '/services' - return f(*args, **kwargs) - - return f_normalize_path - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'Index') - @utils.verify_env - @normalize_path - def get(self, request, environment_id, path): - LOG.debug('Services:Get '.format(env_id=environment_id, path=path)) - - session_id = None - if hasattr(request, 'context') and request.context.session: - session_id = request.context.session - - try: - result = core_services.CoreServices.get_data(environment_id, - path, - session_id) - except (KeyError, ValueError, AttributeError): - raise exc.HTTPNotFound - return result - - @request_statistics.stats_count(API_NAME, 'Create') - @utils.verify_session - @utils.verify_env - @normalize_path - def post(self, request, environment_id, path, body=None): - if not body: - msg = _('Request body is empty: please, provide ' - 'application object model') - LOG.error(msg) - raise exc.HTTPBadRequest(msg) - secure_data = token_sanitizer.TokenSanitizer().sanitize(body) - LOG.debug('Services:Post '.format(env_id=environment_id, - body=secure_data, path=path)) - - post_data = core_services.CoreServices.post_data - session_id = request.context.session - try: - result = post_data(environment_id, session_id, body, path) - except (KeyError, ValueError): - raise exc.HTTPNotFound - return result - - @request_statistics.stats_count(API_NAME, 'Update') - @utils.verify_session - @utils.verify_env - @normalize_path - def put(self, request, environment_id, path, body=None): - if not body: - body = [] - LOG.debug('Services:Put '.format(environment_id, body, path)) - - put_data = core_services.CoreServices.put_data - session_id = request.context.session - - try: - result = put_data(environment_id, session_id, body, path) - except (KeyError, ValueError): - raise exc.HTTPNotFound - return result - - @request_statistics.stats_count(API_NAME, 'Delete') - @utils.verify_session - @utils.verify_env - @normalize_path - def delete(self, request, environment_id, path): - LOG.debug('Services:Delete '.format(environment_id, path)) - - delete_data = core_services.CoreServices.delete_data - session_id = request.context.session - - try: - delete_data(environment_id, session_id, path) - except (KeyError, ValueError): - raise exc.HTTPNotFound - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/sessions.py b/murano/api/v1/sessions.py deleted file mode 100644 index 356271ed7..000000000 --- a/murano/api/v1/sessions.py +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common.i18n import _ -from murano.common import wsgi -from murano.db import models -from murano.db.services import environments as envs -from murano.db.services import sessions -from murano.db import session as db_session -from murano.services import states -from murano.utils import check_env -from murano.utils import check_session - -LOG = logging.getLogger(__name__) -API_NAME = 'Sessions' - - -class Controller(object): - - @request_statistics.stats_count(API_NAME, 'Create') - def configure(self, request, environment_id): - LOG.debug('Session:Configure ' - .format(env_id=environment_id)) - - check_env(request, environment_id) - - # No new session can be opened if environment has deploying or - # deleting status. - env_status = envs.EnvironmentServices.get_status(environment_id) - if env_status in (states.EnvironmentStatus.DEPLOYING, - states.EnvironmentStatus.DELETING): - msg = _('Could not open session for environment , environment has deploying or ' - 'deleting status.').format(env_id=environment_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - user_id = request.context.user - session = sessions.SessionServices.create(environment_id, user_id) - - return session.to_dict() - - @request_statistics.stats_count(API_NAME, 'Index') - def show(self, request, environment_id, session_id): - LOG.debug('Session:Show '.format(id=session_id)) - - unit = db_session.get_session() - session = unit.query(models.Session).get(session_id) - - check_session(request, environment_id, session, session_id) - - user_id = request.context.user - - if session.user_id != user_id: - msg = _('User is not authorized to access ' - 'session .').format(usr_id=user_id, - s_id=session_id) - LOG.error(msg) - raise exc.HTTPUnauthorized(explanation=msg) - - if not sessions.SessionServices.validate(session): - msg = _('Session is invalid: environment has been ' - 'updated or updating right now with other session' - ).format(session_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - return session.to_dict() - - @request_statistics.stats_count(API_NAME, 'Delete') - def delete(self, request, environment_id, session_id): - LOG.debug('Session:Delete '.format(s_id=session_id)) - - unit = db_session.get_session() - session = unit.query(models.Session).get(session_id) - - check_session(request, environment_id, session, session_id) - - user_id = request.context.user - if session.user_id != user_id: - msg = _('User is not authorized to access ' - 'session .').format(usr_id=user_id, - s_id=session_id) - LOG.error(msg) - raise exc.HTTPUnauthorized(explanation=msg) - - if session.state == states.SessionState.DEPLOYING: - msg = _('Session is in deploying state ' - 'and could not be deleted').format(s_id=session_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - with unit.begin(): - unit.delete(session) - - return None - - @request_statistics.stats_count(API_NAME, 'Deploy') - def deploy(self, request, environment_id, session_id): - LOG.debug('Session:Deploy '.format(s_id=session_id)) - - unit = db_session.get_session() - session = unit.query(models.Session).get(session_id) - - check_session(request, environment_id, session, session_id) - - if not sessions.SessionServices.validate(session): - msg = _('Session is invalid: environment has been ' - 'updated or updating right now with other session' - ).format(session_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - if session.state != states.SessionState.OPENED: - msg = _('Session is already deployed or ' - 'deployment is in progress').format(s_id=session_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - envs.EnvironmentServices.deploy(session, - unit, - request.context) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/static_actions.py b/murano/api/v1/static_actions.py deleted file mode 100644 index 8bfe42a2f..000000000 --- a/murano/api/v1/static_actions.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from oslo_messaging.rpc import client -from webob import exc - -from murano.common.i18n import _ -from murano.common import policy -from murano.common import wsgi -from murano.services import static_actions - - -LOG = logging.getLogger(__name__) - - -class Controller(object): - - def execute(self, request, body): - policy.check("execute_action", request.context, {}) - - class_name = body.get('className') - method_name = body.get('methodName') - if not class_name or not method_name: - msg = _('Class name and method name must be specified for ' - 'static action') - LOG.error(msg) - raise exc.HTTPBadRequest(msg) - - args = body.get('parameters') - pkg_name = body.get('packageName') - class_version = body.get('classVersion', '=0') - - LOG.debug('StaticAction:Execute '.format(method_name, class_name)) - - credentials = { - 'token': request.context.auth_token, - 'project_id': request.context.project_id, - 'user_id': request.context.user - } - - try: - return static_actions.StaticActionServices.execute( - method_name, class_name, pkg_name, class_version, args, - credentials) - except client.RemoteError as e: - LOG.error('Exception during call of the method {method_name}: ' - '{exc}'.format(method_name=method_name, exc=str(e))) - if e.exc_type in ( - 'NoClassFound', 'NoMethodFound', 'NoPackageFound', - 'NoPackageForClassFound', 'MethodNotExposed', - 'NoMatchingMethodException'): - raise exc.HTTPNotFound(e.value) - elif e.exc_type == 'ContractViolationException': - raise exc.HTTPBadRequest(e.value) - raise exc.HTTPServiceUnavailable(e.value) - except ValueError as e: - LOG.error('Exception during call of the method {method_name}: ' - '{exc}'.format(method_name=method_name, - exc=str(e))) - raise exc.HTTPBadRequest(str(e)) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/template_applications.py b/murano/api/v1/template_applications.py deleted file mode 100644 index c75036dc3..000000000 --- a/murano/api/v1/template_applications.py +++ /dev/null @@ -1,191 +0,0 @@ - -# Copyright (c) 2015 Telefonica I+D. -# -# Licensed 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 functools as func - -from oslo_log import log as logging -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common.helpers import token_sanitizer -from murano.common.i18n import _ -from murano.common import policy -from murano.common import wsgi -from murano.db.services import core_services -from murano import utils - - -LOG = logging.getLogger(__name__) - -API_NAME = 'Services' - - -def normalize_path(f): - """Normalizes request path - - It normalizes the path obtaining in the requests. - It is used in all the operations in the controller - """ - @func.wraps(f) - def f_normalize_path(*args, **kwargs): - if 'path' in kwargs: - if kwargs['path']: - kwargs['path'] = '/services/' + kwargs['path'] - else: - kwargs['path'] = '/services' - return f(*args, **kwargs) - - return f_normalize_path - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'Index') - @utils.verify_env_template - @normalize_path - def index(self, request, env_template_id, path): - """Obtains services/applications for a template - - It obtains all the services/applications associated to - a template - :param request: The operation request. - :param env_template_id: The environment template id with - contains the services - :param path: The operation path - """ - LOG.debug('Applications:Get '.format(templ_id=env_template_id, path=path)) - - try: - get_data = core_services.CoreServices.get_template_data - result = get_data(env_template_id, path) - except (KeyError, ValueError, AttributeError): - msg = _('The environment template {templ_id} does not ' - 'exist').format(templ_id=env_template_id) - LOG.exception(msg) - raise exc.HTTPNotFound(msg) - return result - - @request_statistics.stats_count(API_NAME, 'Show') - @utils.verify_env_template - @normalize_path - def show(self, request, env_template_id, path): - """It shows the service description - - :param request: The operation request. - :param env_template_id: the env template ID where the service - belongs to. - :param path: The path include the service id - :return: the service description. - """ - LOG.debug('Applications:Get '.format(templ_id=env_template_id, path=path)) - - try: - get_data = core_services.CoreServices.get_template_data - result = get_data(env_template_id, path) - except (KeyError, ValueError, AttributeError): - msg = _('The template does not exist {templ_id}').format( - templ_id=env_template_id) - LOG.exception(msg) - raise exc.HTTPNotFound(msg) - return result - - @request_statistics.stats_count(API_NAME, 'Create') - @utils.verify_env_template - @normalize_path - def post(self, request, env_template_id, path, body): - """It adds a service into a template - - :param request: The operation request. - :param env_template_id: the env template ID where the - service belongs to. - :param path: The path - :param body: the information about the service - :return: the service description. - """ - secure_data = token_sanitizer.TokenSanitizer().sanitize(body) - LOG.debug('Applications:Post '.format(env_id=env_template_id, - body=secure_data, - path=path)) - - post_data = core_services.CoreServices.post_application_data - try: - result = post_data(env_template_id, body, path) - except (KeyError, ValueError): - msg = _('The template does not exist {templ_id}').format( - templ_id=env_template_id) - LOG.exception(msg) - raise exc.HTTPNotFound(msg) - return result - - @request_statistics.stats_count(API_NAME, 'Update') - @utils.verify_env_template - @normalize_path - def put(self, request, env_template_id, path, body): - - """It updates a service into a template. - - :param request: The operation request. - :param env_template_id: the env template ID where the service - belongs to. - :param path: The path - :param body: the information about the service - :return: the service description updated. - """ - policy.check('update_service_env_template', request.context) - LOG.debug('Applications:Put '.format(templ_id=env_template_id, - body=body, - path=path)) - - put_data = core_services.CoreServices.put_application_data - - try: - result = put_data(env_template_id, body, path) - except (KeyError, ValueError): - msg = _('The template does not exist {templ_id}').format( - templ_id=env_template_id) - LOG.exception(msg) - raise exc.HTTPNotFound(msg) - return result - - @request_statistics.stats_count(API_NAME, 'Delete') - @utils.verify_env_template - @normalize_path - def delete(self, request, env_template_id, path): - """It deletes a service into a template. - - :param request: The operation request. - :param env_template_id: the env template ID where - the service belongs to. - :param path: The path contains the service id - """ - LOG.debug('Applications:Put '.format(templ_id=env_template_id, - path=path)) - delete_data = core_services.CoreServices.delete_env_template_data - try: - result = delete_data(env_template_id, path) - except (KeyError, ValueError): - msg = _('The template does not exist {templ_id}').format( - templ_id=env_template_id) - LOG.exception(msg) - raise exc.HTTPNotFound(msg) - return result - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/templates.py b/murano/api/v1/templates.py deleted file mode 100644 index 6c9f859e4..000000000 --- a/murano/api/v1/templates.py +++ /dev/null @@ -1,292 +0,0 @@ -# Copyright (c) 2015, Telefonica I+D. -# -# Licensed 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 oslo_db import exception as db_exc -from oslo_log import log as logging -from webob import exc - -from murano.api.v1 import request_statistics -from murano.common.i18n import _ -from murano.common import policy -from murano.common import utils -from murano.common import wsgi -from murano.db import models -from murano.db.services import core_services -from murano.db.services import environment_templates as env_temps -from murano.db.services import environments as envs -from murano.db.services import sessions - -LOG = logging.getLogger(__name__) - -API_NAME = 'Templates' - - -class Controller(object): - @request_statistics.stats_count(API_NAME, 'Index') - def index(self, request): - """Lists the env templates associated to an tenant-id - - It lists the env templates associated to an tenant-id. - :param request: The operation request. - :return: the env template description list. - """ - LOG.debug('EnvTemplates:List') - policy.check('list_env_templates', request.context) - tenant_id = request.context.project_id - filters = {} - if request.GET.get('is_public'): - is_public = request.GET.get('is_public', 'false').lower() == 'true' - if not is_public: - - filters['is_public'] = False - filters['tenant_id'] = tenant_id - elif is_public: - filters['is_public'] = True - - list_templates = env_temps.EnvTemplateServices.\ - get_env_templates_by(filters) - - else: - filters = (models.EnvironmentTemplate.is_public, - models.EnvironmentTemplate.tenant_id == tenant_id) - list_templates = env_temps.EnvTemplateServices.\ - get_env_templates_or_by(filters) - - list_templates = [temp.to_dict() for temp in list_templates] - return {"templates": list_templates} - - @request_statistics.stats_count(API_NAME, 'Create') - def create(self, request, body): - """Creates the env template from the payload - - This payload can contain just the template name, or include - also service information. - :param request: the operation request. - :param body: the env template description - :return: the description of the created template. - """ - LOG.debug('EnvTemplates:Create '.format(body=body)) - policy.check('create_env_template', request.context) - - self._validate_body_name(body) - try: - LOG.debug('ENV TEMP NAME: {templ_name}>'. - format(templ_name=body['name'])) - template = env_temps.EnvTemplateServices.create( - body.copy(), request.context.project_id) - return template.to_dict() - except db_exc.DBDuplicateEntry: - msg = _('Env Template with specified name already exists') - LOG.exception(msg) - raise exc.HTTPConflict(msg) - - @request_statistics.stats_count(API_NAME, 'Show') - def show(self, request, env_template_id): - """It shows the description of a template - - :param request: the operation request. - :param env_template_id: the env template ID. - :return: the description of the env template. - """ - LOG.debug('Templates:Show '.format( - templ_id=env_template_id)) - target = {"env_template_id": env_template_id} - policy.check('show_env_template', request.context, target) - - self._validate_request(request, env_template_id) - - template = env_temps.EnvTemplateServices.\ - get_env_template(env_template_id) - temp = template.to_dict() - - get_data = core_services.CoreServices.get_template_data - temp['services'] = get_data(env_template_id, '/services') - return temp - - @request_statistics.stats_count(API_NAME, 'Update') - def update(self, request, env_template_id, body): - """It updates the description template - - :param request: the operation request. - :param env_template_id: the env template ID. - :param body: the description to be updated - :return: the updated template description. - """ - LOG.debug('Templates:Update '.format(templ_id=env_template_id, body=body)) - target = {"env_template_id": env_template_id} - policy.check('update_env_template', request.context, target) - - self._validate_request(request, env_template_id) - try: - LOG.debug('ENV TEMP NAME: {temp_name}>'.format( - temp_name=body['name'])) - if not str(body['name']).strip(): - msg = _('Environment Template must contain at least one ' - 'non-white space symbol') - LOG.exception(msg) - raise exc.HTTPBadRequest(msg) - except Exception: - msg = _('EnvTemplate body is incorrect') - LOG.exception(msg) - raise exc.HTTPBadRequest(msg) - - template = env_temps.EnvTemplateServices.update(env_template_id, body) - return template.to_dict() - - @request_statistics.stats_count(API_NAME, 'Delete') - def delete(self, request, env_template_id): - """It deletes the env template - - :param request: the operation request. - :param env_template_id: the template ID. - """ - LOG.debug('EnvTemplates:Delete '.format( - templ_id=env_template_id)) - target = {"env_template_id": env_template_id} - policy.check('delete_env_template', request.context, target) - self._validate_request(request, env_template_id) - env_temps.EnvTemplateServices.delete(env_template_id) - env_temps.EnvTemplateServices.remove(env_template_id) - return - - def has_services(self, template): - """"It checks if the template has services - - :param template: the template to check. - :return: True or False - """ - if not template.description: - return False - - if (template.description.get('services')): - return True - return False - - @request_statistics.stats_count(API_NAME, 'Create_environment') - def create_environment(self, request, env_template_id, body): - """Creates environment and session from template - - :param request: operation request - :param env_template_id: environment template ID - :param body: the environment name - :return: session_id and environment_id - """ - target = {"env_template_id": env_template_id} - policy.check('create_environment', request.context, target) - - self._validate_request(request, env_template_id) - LOG.debug('Templates:Create environment '. - format(templ_id=env_template_id)) - template = env_temps.EnvTemplateServices.\ - get_env_template(env_template_id) - self._validate_body_name(body) - try: - environment = envs.EnvironmentServices.create( - body.copy(), request.context) - except db_exc.DBDuplicateEntry: - msg = _('Environment with specified name already exists') - LOG.exception(msg) - raise exc.HTTPConflict(explanation=msg) - - user_id = request.context.user - session = sessions.SessionServices.create(environment.id, user_id) - - if self.has_services(template): - services_node = utils.TraverseHelper.get("services", - template.description) - utils.TraverseHelper.update("/Objects/services", - services_node, - environment.description) - - envs.EnvironmentServices.save_environment_description( - session.id, - environment.description, - inner=False - ) - return {"session_id": session.id, "environment_id": environment.id} - - @request_statistics.stats_count(API_NAME, 'Clone') - def clone(self, request, env_template_id, body): - """Clones env template from another tenant - - It clones the env template from another env template - from other tenant. - :param request: the operation request. - :param env_template_id: the env template ID. - :param body: the request body. - :return: the description of the created template. - """ - - LOG.debug('EnvTemplates:Clone '. - format(body, env_template_id)) - policy.check('clone_env_template', request.context) - - old_env_template = self._validate_exists(env_template_id) - - if not old_env_template.get('is_public'): - msg = _('User has no access to these resources.') - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - self._validate_body_name(body) - LOG.debug('ENV TEMP NAME: {0}'.format(body['name'])) - - try: - is_public = body.get('is_public', False) - template = env_temps.EnvTemplateServices.clone( - env_template_id, request.context.project_id, body['name'], - is_public) - except db_exc.DBDuplicateEntry: - msg = _('Env template with specified name already exists') - LOG.error(msg) - raise exc.HTTPConflict(explanation=msg) - - return template.to_dict() - - def _validate_request(self, request, env_template_id): - env_template = self._validate_exists(env_template_id) - if env_template.is_public or request.context.is_admin: - return - if env_template.tenant_id != request.context.project_id: - msg = _('User has no access to these resources.') - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - def _validate_exists(self, env_template_id): - env_template_exists = env_temps.EnvTemplateServices.env_template_exist - if not env_template_exists(env_template_id): - msg = _('EnvTemplate is not found').format( - temp_id=env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - get_env_template = env_temps.EnvTemplateServices.get_env_template - return get_env_template(env_template_id) - - def _validate_body_name(self, body): - - if not('name' in body and body['name'].strip()): - msg = _('Please, specify a name of the environment template.') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - name = str(body['name']) - if len(name) > 255: - msg = _('Environment template name should be 255 characters ' - 'maximum') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - -def create_resource(): - return wsgi.Resource(Controller()) diff --git a/murano/api/v1/validation_schemas.py b/murano/api/v1/validation_schemas.py deleted file mode 100644 index 88cd11820..000000000 --- a/murano/api/v1/validation_schemas.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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. - -ENV_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - - "type": "object", - "properties": { - "?": { - "type": "object", - "properties": { - "id": {"type": "string"}, - "name": {"type": "string"}, - "type": {"type": "string"}, - "_actions": {"type": "object"} - }, - "required": ["id", "type"] - }, - "name": {"type": "string"}, - "region": {"type": ["string", "null"]}, - "regions": {"type": "object"}, - "defaultNetworks": { - "type": "object", - "properties": { - "environment": { - "type": "object", - "properties": { - "name": {"type": "string"}, - "?": { - "type": "object", - "properties": { - "type": {"type": "string"}, - "id": {"type": "string"}, - "name": {"type": "string"} - }, - }, - "autoUplink": {"type": "boolean"}, - "externalRouterId": {"type": "string"}, - "dnsNameServers": {"type": "array"}, - "autogenerateSubnet": {"type": "boolean"}, - "subnetCidr": {"type": "string"}, - "openstackId": {"type": "string"}, - "regionName": {"type": "string"} - }, - "required": ["name", "?"] - }, - "flat": {"type": ["boolean", "null"]} - }, - "required": ["environment", "flat"] - }, - "services": { - "type": "array", - "minItems": 0, - "items": {"type": "object"} - } - }, - "required": ["?", "name", "region", "defaultNetworks"] -} - -PKG_UPLOAD_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - - "type": "object", - "properties": { - "tags": { - "type": "array", - "minItems": 0, - "items": {"type": "string"}, - "uniqueItems": True - }, - "categories": { - "type": "array", - "minItems": 0, - "items": {"type": "string"}, - "uniqueItems": True - }, - "description": {"type": "string"}, - "name": {"type": "string"}, - "is_public": {"type": "boolean"}, - "enabled": {"type": "boolean"} - }, - "additionalProperties": False -} - -PKG_UPDATE_SCHEMA = { - "$schema": "http://json-schema.org/draft-04/schema#", - - "type": "object", - "properties": { - "tags": { - "type": "array", - "items": {"type": "string"}, - "uniqueItems": True - }, - "categories": { - "type": "array", - "items": {"type": "string"}, - "uniqueItems": True - }, - "description": {"type": "string"}, - "name": {"type": "string"}, - "is_public": {"type": "boolean"}, - "enabled": {"type": "boolean"} - }, - "additionalProperties": False, - "minProperties": 1, -} diff --git a/murano/api/versions.py b/murano/api/versions.py deleted file mode 100644 index 3a47e3974..000000000 --- a/murano/api/versions.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 http import client as http_client -from oslo_serialization import jsonutils -import webob.dec - -from murano.common import wsgi - - -class Controller(object): - - """A wsgi controller that reports which API versions are supported.""" - - def index(self, req): - """Respond to a request for all OpenStack API versions.""" - def build_version_object(version, path, status): - return { - 'id': 'v%s' % version, - 'status': status, - 'links': [ - { - 'rel': 'self', - 'href': '%s/%s/' % (req.host_url, path), - }, - ], - } - - version_objs = [] - version_objs.extend([ - build_version_object(1.0, 'v1', 'CURRENT'), - ]) - - response = webob.Response(request=req, - status=http_client.MULTIPLE_CHOICES, - content_type='application/json') - response.text = jsonutils.dumps(dict(versions=version_objs)) - return response - - @webob.dec.wsgify(RequestClass=wsgi.Request) - def __call__(self, req): - return self.index(req) - - -def create_resource(conf): - return wsgi.Resource(Controller()) diff --git a/murano/cfapi/__init__.py b/murano/cfapi/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/cfapi/cfapi.py b/murano/cfapi/cfapi.py deleted file mode 100644 index 37c22733f..000000000 --- a/murano/cfapi/cfapi.py +++ /dev/null @@ -1,341 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 json -import uuid - -from oslo_config import cfg -from oslo_log import log as logging -import tenacity -from webob import response - -from murano.common import auth_utils # noqa -from murano.common import wsgi -from murano.db.services import cf_connections as db_cf -import muranoclient.client as muranoclient -from muranoclient.common import exceptions -from muranoclient.glance import client as glare_client - - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class Controller(object): - """WSGI controller for application catalog resource in Murano v1 API""" - - def _package_to_service(self, package): - srv = {} - srv['id'] = package.id - srv['name'] = package.name - if len(package.description) > 256: - srv['description'] = u"{0} ...".format(package.description[:253]) - else: - srv['description'] = package.description - srv['bindable'] = True - srv['tags'] = [] - for tag in package.tags: - srv['tags'].append(tag) - plan = {'id': package.id + '-1', - 'name': 'default', - 'description': 'Default plan for the service {name}'.format( - name=package.name)} - srv['plans'] = [plan] - return srv - - def _make_service(self, name, package, plan_id): - id = uuid.uuid4().hex - - return {"name": name, - "?": {plan_id: {"name": package.name}, - "type": package.fully_qualified_name, - "id": id}} - - def _get_service(self, env, service_id): - for service in env.services: - if service['?']['id'] == service_id: - return service - return None - - def list(self, req): - - token = req.headers['X-Auth-Token'] - m_cli = _get_muranoclient(token, req) - kwargs = {'type': 'Application'} - packages = m_cli.packages.filter(**kwargs) - services = [] - for package in packages: - services.append(self._package_to_service(package)) - - resp = {'services': services} - - return resp - - def provision(self, req, body, instance_id): - """Here is the example of request body given us from Cloud Foundry: - - { - "service_id": "service-guid-here", - "plan_id": "plan-guid-here", - "organization_guid": "org-guid-here", - "space_guid": "space-guid-here", - "parameters": {"param1": "value1", - "param2": "value2"} - } - """ - data = json.loads(req.body) - space_guid = data['space_guid'] - org_guid = data['organization_guid'] - plan_id = data['plan_id'] - service_id = data['service_id'] - parameters = data['parameters'] - self.current_session = None - - # Here we'll take an entry for CF org and space from db. If we - # don't have any entries we will create it from scratch. - try: - tenant = db_cf.get_tenant_for_org(org_guid) - except AttributeError: - tenant = req.headers['X-Project-Id'] - db_cf.set_tenant_for_org(org_guid, tenant) - LOG.info("Cloud Foundry {org_id} mapped to tenant " - "{tenant_name}".format(org_id=org_guid, - tenant_name=tenant)) - - token = req.headers['X-Auth-Token'] - m_cli = _get_muranoclient(token, req) - - def _set_new_environment_for_space(space_guid, log_msg): - body = {'name': 'my_{uuid}'.format(uuid=uuid.uuid4().hex)} - env = m_cli.environments.create(body) - db_cf.set_environment_for_space(space_guid, env.id) - LOG.info(log_msg.format(space_id=space_guid, - environment_id=env.id)) - return env.id - - try: - environment_id = db_cf.get_environment_for_space(space_guid) - # NOTE: Check that environment which was previously linked with - # CF space still exist, reset a new environment for space. - try: - env = m_cli.environments.get(environment_id) - except exceptions.HTTPNotFound: - msg = ("Can not find environment_id {environment_id}, " - "will create a new one." - .format(environment_id=environment_id)) - LOG.info(msg) - env = {} - if not env: - log_msg = ("Cloud Foundry {space_id} remapped to " - "{environment_id}") - environment_id = _set_new_environment_for_space( - space_guid, log_msg) - except AttributeError: - log_msg = ("Cloud Foundry {space_id} mapped to " - "{environment_id}") - environment_id = _set_new_environment_for_space( - space_guid, log_msg) - - package = m_cli.packages.get(service_id) - LOG.debug('Adding service {name}'.format(name=package.name)) - - service = self._make_service(space_guid, package, plan_id) - db_cf.set_instance_for_service(instance_id, service['?']['id'], - environment_id, tenant) - - # NOTE(Kezar): Here we are going through JSON and add ids where - # it's necessary. Before that we need to drop '?' key from parameters - # dictionary as far it contains murano package related info which is - # necessary in our scenario - if '?' in parameters.keys(): - parameters.pop('?', None) - LOG.warning("Incorrect input parameters. Package related " - "parameters shouldn't be passed through Cloud " - "Foundry") - params = [parameters] - while params: - a = params.pop() - for k, v in a.items(): - if isinstance(v, dict): - params.append(v) - if k == '?': - v['id'] = uuid.uuid4().hex - service.update(parameters) - # Now we need to obtain session to modify the env - session_id = create_session(m_cli, environment_id) - m_cli.services.post(environment_id, - path='/', - data=service, - session_id=session_id) - m_cli.sessions.deploy(environment_id, session_id) - self.current_session = session_id - return response.Response(status=202, json_body={}) - - def deprovision(self, req, instance_id): - service = db_cf.get_service_for_instance(instance_id) - if not service: - return {} - - service_id = service.service_id - environment_id = service.environment_id - token = req.headers['X-Auth-Token'] - m_cli = _get_muranoclient(token, req) - - session_id = create_session(m_cli, environment_id) - m_cli.services.delete(environment_id, '/' + service_id, session_id) - m_cli.sessions.deploy(environment_id, session_id) - return response.Response(status=202, json_body={}) - - def bind(self, req, body, instance_id, app_id): - db_service = db_cf.get_service_for_instance(instance_id) - if not db_service: - return {} - - service_id = db_service.service_id - environment_id = db_service.environment_id - token = req.headers['X-Auth-Token'] - m_cli = _get_muranoclient(token, req) - - session_id = create_session(m_cli, environment_id) - env = m_cli.environments.get(environment_id, session_id) - LOG.debug('Got environment {0}'.format(env)) - service = self._get_service(env, service_id) - LOG.debug('Got service {0}'.format(service)) - - # NOTE(starodubcevna): Here we need to find an action which will return - # us needed credentials. By default we will look for getCredentials - # action. - result = {} - try: - actions = service['?']['_actions'] - for action_id in list(actions): - if 'getCredentials' in action_id: - - @tenacity.retry( - retry=tenacity.retry_if_exception_type(TypeError), - wait=tenacity.wait_random(min=1, max=10), - stop=tenacity.stop_after_delay(30), - reraise=True) - def _get_creds(client, task_id, environment_id): - result = m_cli.actions.get_result(environment_id, - task_id)['result'] - return result - - task_id = m_cli.actions.call(environment_id, action_id) - result = _get_creds(m_cli, task_id, environment_id) - - if not result: - LOG.warning("This application doesn't have action " - "getCredentials") - return response.Response(status=500) - except KeyError: - # NOTE(starodubcevna): In CF service broker API spec return - # code for failed bind is not present, so we will return 500. - LOG.warning("This application doesn't have actions at all") - return response.Response(status=500) - - if 'credentials' in list(result): - return result - else: - return {'credentials': result} - - def unbind(self, req, instance_id, app_id): - """Unsupported functionality - - murano doesn't support this kind of functionality, so we just need - to create a stub where the call will come. We can't raise something - like NotImplementedError because we will have problems on Cloud Foundry - side. The best way now it to return empty dict which will be correct - answer for Cloud Foundry. - """ - - return {} - - def get_last_operation(self, req, instance_id): - service = db_cf.get_service_for_instance(instance_id) - # NOTE(freerunner): Prevent code 500 if requested environment - # already doesn't exist. - if not service: - LOG.warning('Requested service for instance {0} is not ' - 'found'.format(instance_id)) - body = {} - resp = response.Response(status=410, json_body=body) - return resp - env_id = service.environment_id - token = req.headers["X-Auth-Token"] - m_cli = _get_muranoclient(token, req) - - # NOTE(starodubcevna): we can track only environment status. it's - # murano API limitation. - m_environment = m_cli.environments.get(env_id) - body = {'state': 'unknown', 'description': 'operation unknown'} - resp = response.Response(status=500, json_body=body) - if m_environment.status == 'ready': - body = {'state': 'succeeded', - 'description': 'operation succeed'} - resp = response.Response(status=200, json_body=body) - elif m_environment.status in ['pending', 'deleting', 'deploying']: - body = {'state': 'in progress', - 'description': 'operation in progress'} - resp = response.Response(status=202, json_body=body) - elif m_environment.status in ['deploy failure', 'delete failure']: - body = {'state': 'failed', - 'description': '{0}. Please correct it manually'.format( - m_environment.status)} - resp = response.Response(status=200, json_body=body) - return resp - - -def _get_muranoclient(token_id, req): - - artifacts_client = None - if CONF.engine.packages_service in ['glance', 'glare']: - artifacts_client = _get_glareclient(token_id, req) - - murano_url = CONF.murano.url or req.endpoints.get('murano') - if not murano_url: - LOG.error('No murano url is specified and no ' - '"application-catalog" ' - 'service is registered in keystone.') - - return muranoclient.Client(1, murano_url, token=token_id, - artifacts_client=artifacts_client) - - -def _get_glareclient(token_id, req): - glare_settings = CONF.glare - - url = glare_settings.url or req.endpoints.get('glare') - if not url: - LOG.error('No glare url is specified and no "artifact" ' - 'service is registered in keystone.') - # TODO(gyurco): use auth_utils.get_session_client_parameters - return glare_client.Client( - endpoint=url, token=token_id, - insecure=glare_settings.insecure, - key_file=glare_settings.keyfile or None, - ca_file=glare_settings.cafile or None, - cert_file=glare_settings.certfile or None, - type_name='murano', - type_version=1) - - -def create_session(client, environment_id): - id = client.sessions.configure(environment_id).id - return id - - -def create_resource(): - return wsgi.Resource(Controller(), - serializer=wsgi.ServiceBrokerResponseSerializer()) diff --git a/murano/cfapi/router.py b/murano/cfapi/router.py deleted file mode 100644 index 29bfaa70f..000000000 --- a/murano/cfapi/router.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 routes - -from murano.cfapi import cfapi -from murano.common import wsgi - - -class API(wsgi.Router): - @classmethod - def factory(cls, global_conf, **local_conf): - return cls(routes.Mapper()) - - def __init__(self, mapper): - services_resource = cfapi.create_resource() - mapper.connect('/v2/catalog', - controller=services_resource, - action='list', - conditions={'method': ['GET']}) - mapper.connect(('/v2/service_instances/{instance_id}'), - controller=services_resource, - action='provision', - conditions={'method': ['PUT']}) - mapper.connect(('/v2/service_instances/{instance_id}'), - controller=services_resource, - action='deprovision', - conditions={'method': ['DELETE']}) - - mapper.connect(('/v2/service_instances/{instance_id}/service_bindings/' - '{app_id}'), - controller=services_resource, - action='bind', - conditions={'method': ['PUT']}) - - mapper.connect(('/v2/service_instances/{instance_id}/service_bindings/' - '{app_id}'), - controller=services_resource, - action='unbind', - conditions={'method': ['DELETE']}) - - mapper.connect(('/v2/service_instances/{instance_id}/last_operation'), - controller=services_resource, - action='get_last_operation', - conditions={'method': ['GET']}) - - super(API, self).__init__(mapper) diff --git a/murano/cmd/__init__.py b/murano/cmd/__init__.py deleted file mode 100644 index a9bd720ac..000000000 --- a/murano/cmd/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# -# Licensed 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 murano.monkey_patch # noqa diff --git a/murano/cmd/api.py b/murano/cmd/api.py deleted file mode 100644 index 6383c97cc..000000000 --- a/murano/cmd/api.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 os -import sys - -from oslo_concurrency import processutils -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import service - -from murano.api.v1 import request_statistics -from murano.common import app_loader -from murano.common import config -from murano.common import policy -from murano.common import server -from murano.common import statservice as stats -from murano.common import wsgi - -CONF = cfg.CONF - - -# If ../murano/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -root = os.path.join(os.path.abspath(__file__), os.pardir, os.pardir, os.pardir) -if os.path.exists(os.path.join(root, 'murano', '__init__.py')): - sys.path.insert(0, root) - - -def main(): - try: - config.parse_args() - config.set_middleware_defaults() - request_statistics.init_stats() - policy.init() - - logging.setup(CONF, 'murano') - workers = CONF.murano.api_workers - if not workers: - workers = processutils.get_worker_count() - launcher = service.launch( - CONF, server.ApiService(), - workers=workers, restart_method='mutate') - - app = app_loader.load_paste_app('murano') - port, host = (CONF.bind_port, CONF.bind_host) - launcher.launch_service(wsgi.Service(app, port, host)) - launcher.launch_service(server.NotificationService()) - launcher.launch_service(stats.StatsCollectingService()) - - launcher.wait() - except RuntimeError as e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/murano/cmd/cfapi.py b/murano/cmd/cfapi.py deleted file mode 100644 index 29504cdbb..000000000 --- a/murano/cmd/cfapi.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 os -import sys - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import service - -from murano.api.v1 import request_statistics -from murano.common import app_loader -from murano.common import cf_config as config -from murano.common import policy -from murano.common import wsgi - -CONF = cfg.CONF - - -# If ../murano/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -root = os.path.join(os.path.abspath(__file__), os.pardir, os.pardir, os.pardir) -if os.path.exists(os.path.join(root, 'murano', '__init__.py')): - sys.path.insert(0, root) - - -def main(): - try: - config.parse_args() - logging.setup(CONF, 'murano-cfapi') - request_statistics.init_stats() - policy.init() - - launcher = service.ServiceLauncher(CONF) - - cfapp = app_loader.load_paste_app('cloudfoundry') - cfport, cfhost = (config.CONF.cfapi.bind_port, - config.CONF.cfapi.bind_host) - - launcher.launch_service(wsgi.Service(cfapp, cfport, cfhost)) - - launcher.wait() - except RuntimeError as e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/murano/cmd/cfapi_db_manage.py b/murano/cmd/cfapi_db_manage.py deleted file mode 100644 index 27f5e9f17..000000000 --- a/murano/cmd/cfapi_db_manage.py +++ /dev/null @@ -1,80 +0,0 @@ -# Licensed 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 oslo_config import cfg -from oslo_db import options - -from murano.db.cfapi_migration import migration - -CONF = cfg.CONF -options.set_defaults(CONF) - - -class ApiDBCommand(object): - - def upgrade(self, config): - migration.upgrade(CONF.command.revision, config=config) - - def downgrade(self, config): - migration.downgrade(CONF.command.revision, config=config) - - def revision(self, config): - migration.revision(CONF.command.message, - CONF.command.autogenerate, - config=config) - - def stamp(self, config): - migration.stamp(CONF.command.revision, config=config) - - def version(self, config): - print(migration.version()) - - -def add_command_parsers(subparsers): - command_object = ApiDBCommand() - - parser = subparsers.add_parser('upgrade') - parser.set_defaults(func=command_object.upgrade) - parser.add_argument('--revision', nargs='?') - - parser = subparsers.add_parser('downgrade') - parser.set_defaults(func=command_object.downgrade) - parser.add_argument('--revision', nargs='?') - - parser = subparsers.add_parser('stamp') - parser.add_argument('--revision', nargs='?') - parser.set_defaults(func=command_object.stamp) - - parser = subparsers.add_parser('revision') - parser.add_argument('-m', '--message') - parser.add_argument('--autogenerate', action='store_true') - parser.set_defaults(func=command_object.revision) - - parser = subparsers.add_parser('version') - parser.set_defaults(func=command_object.version) - - -command_opt = cfg.SubCommandOpt('command', - title='Command', - help='Available commands', - handler=add_command_parsers) - -CONF.register_cli_opt(command_opt) - - -def main(): - config = migration.get_alembic_config() - # attach the Murano conf to the Alembic conf - config.murano_config = CONF - - CONF(project='murano') - CONF.command.func(config) diff --git a/murano/cmd/db_manage.py b/murano/cmd/db_manage.py deleted file mode 100644 index e4c8f374f..000000000 --- a/murano/cmd/db_manage.py +++ /dev/null @@ -1,80 +0,0 @@ -# Licensed 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 oslo_config import cfg -from oslo_db import options - -from murano.db.migration import migration - -CONF = cfg.CONF -options.set_defaults(CONF) - - -class DBCommand(object): - - def upgrade(self, config): - migration.upgrade(CONF.command.revision, config=config) - - def downgrade(self, config): - migration.downgrade(CONF.command.revision, config=config) - - def revision(self, config): - migration.revision(CONF.command.message, - CONF.command.autogenerate, - config=config) - - def stamp(self, config): - migration.stamp(CONF.command.revision, config=config) - - def version(self, config): - print(migration.version()) - - -def add_command_parsers(subparsers): - command_object = DBCommand() - - parser = subparsers.add_parser('upgrade') - parser.set_defaults(func=command_object.upgrade) - parser.add_argument('--revision', nargs='?') - - parser = subparsers.add_parser('downgrade') - parser.set_defaults(func=command_object.downgrade) - parser.add_argument('--revision', nargs='?') - - parser = subparsers.add_parser('stamp') - parser.add_argument('--revision', nargs='?') - parser.set_defaults(func=command_object.stamp) - - parser = subparsers.add_parser('revision') - parser.add_argument('-m', '--message') - parser.add_argument('--autogenerate', action='store_true') - parser.set_defaults(func=command_object.revision) - - parser = subparsers.add_parser('version') - parser.set_defaults(func=command_object.version) - - -command_opt = cfg.SubCommandOpt('command', - title='Command', - help='Available commands', - handler=add_command_parsers) - -CONF.register_cli_opt(command_opt) - - -def main(): - config = migration.get_alembic_config() - # attach the Murano conf to the Alembic conf - config.murano_config = CONF - - CONF(project='murano') - CONF.command.func(config) diff --git a/murano/cmd/engine.py b/murano/cmd/engine.py deleted file mode 100644 index 6a8592be3..000000000 --- a/murano/cmd/engine.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 os -import sys - -from oslo_concurrency import processutils -from oslo_log import log as logging -from oslo_service import service - -from murano.common import config -from murano.common import engine - -CONF = config.CONF - - -# If ../murano/__init__.py exists, add ../ to Python search path, so that -# it will override what happens to be installed in /usr/(local/)lib/python... -root = os.path.join(os.path.abspath(__file__), os.pardir, os.pardir, os.pardir) -if os.path.exists(os.path.join(root, 'murano', '__init__.py')): - sys.path.insert(0, root) - - -def main(): - try: - config.parse_args() - - logging.setup(CONF, 'murano') - workers = CONF.engine.engine_workers - if not workers: - workers = processutils.get_worker_count() - launcher = service.launch( - CONF, engine.EngineService(), - workers=workers, restart_method='mutate') - launcher.wait() - except RuntimeError as e: - sys.stderr.write("ERROR: %s\n" % e) - sys.exit(1) - - -if __name__ == '__main__': - main() diff --git a/murano/cmd/manage.py b/murano/cmd/manage.py deleted file mode 100644 index c46b7478b..000000000 --- a/murano/cmd/manage.py +++ /dev/null @@ -1,171 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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. - -""" - *** Deprecation warning *** - This file is about to be deprecated, please use python-muranoclient. - *** Deprecation warning *** -""" -import sys -import traceback - -from oslo_config import cfg -from oslo_db import exception as db_exception -from oslo_log import log as logging - -from murano.common import consts -from murano.db.catalog import api as db_catalog_api -from murano.packages import load_utils -from murano import version - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class AdminContext(object): - def __init__(self): - self.is_admin = True - - -def _do_import_package(_dir, categories, update=False): - LOG.debug("Going to import Murano package from {source}".format( - source=_dir)) - pkg = load_utils.load_from_dir(_dir) - - LOG.debug("Checking for existing packages") - existing = db_catalog_api.package_search( - {'fqn': pkg.full_name}, - AdminContext()) - if existing: - existing_pkg = existing[0] - if update: - LOG.debug('Deleting existing package {exst_pkg_id}').format( - exst_pkg_id=existing_pkg.id) - db_catalog_api.package_delete(existing_pkg.id, AdminContext()) - else: - LOG.error("Package '{name}' exists ({pkg_id}). Use --update." - .format(name=pkg.full_name, pkg_id=existing_pkg.id)) - return - - package = { - 'fully_qualified_name': pkg.full_name, - 'type': pkg.package_type, - 'author': pkg.author, - 'supplier': pkg.supplier, - 'name': pkg.display_name, - 'description': pkg.description, - # note: we explicitly mark all the imported packages as public, - # until a parameter added to control visibility scope of a package - 'is_public': True, - 'tags': pkg.tags, - 'logo': pkg.logo, - 'supplier_logo': pkg.supplier_logo, - 'ui_definition': pkg.ui, - 'class_definitions': pkg.classes, - 'archive': pkg.blob, - 'categories': categories or [] - } - - # note(ruhe): the second parameter is tenant_id - # it is a required field in the DB, that's why we pass an empty string - result = db_catalog_api.package_upload(package, '') - - LOG.info("Finished import of package {res_id}".format(res_id=result.id)) - - -# TODO(ruhe): proper error handling -def do_import_package(): - """Import Murano package from local directory.""" - _do_import_package( - CONF.command.directory, - CONF.command.categories, - CONF.command.update) - - -def do_list_categories(): - categories = db_catalog_api.category_get_names() - - if categories: - print(">> Murano package categories:") - for c in categories: - print("* {0}".format(c)) - else: - print("No categories were found") - - -def do_add_category(): - category_name = CONF.command.category_name - - try: - db_catalog_api.category_add(category_name) - print(">> Successfully added category {0}".format(category_name)) - except db_exception.DBDuplicateEntry: - print(">> ERROR: Category '{0}' already exists".format(category_name)) - - -def add_command_parsers(subparsers): - parser = subparsers.add_parser('import-package') - parser.set_defaults(func=do_import_package) - parser.add_argument('directory', - help='A directory with Murano package.') - parser.add_argument('-u', '--update', - action="store_true", - default=False, - help='If a package already exists, delete and update') - parser.add_argument('-c', '--categories', - choices=consts.CATEGORIES, - nargs='*', - help='An optional list of categories this package ' - 'to be assigned to.') - - parser = subparsers.add_parser('category-list') - parser.set_defaults(func=do_list_categories) - - parser = subparsers.add_parser('category-add') - parser.set_defaults(func=do_add_category) - parser.add_argument('category_name', - help='Name of the new category.') - - -command_opt = cfg.SubCommandOpt('command', - title='Commands', - help='Show available commands.', - handler=add_command_parsers) - - -def main(): - CONF.register_cli_opt(command_opt) - - try: - default_config_files = cfg.find_config_files('murano', 'murano') - CONF(sys.argv[1:], project='murano', prog='murano-manage', - version=version.version_string, - default_config_files=default_config_files) - except RuntimeError as e: - LOG.error("failed to initialize murano-manage: {error}".format( - error=e)) - sys.exit("ERROR: %s" % e) - - try: - CONF.command.func() - except Exception as e: - tb = traceback.format_exc() - err_msg = ("murano-manage command failed: {error}\n" - "{traceback}").format(error=e, traceback=tb) - LOG.error(err_msg) - sys.exit(err_msg) - - -if __name__ == '__main__': - main() diff --git a/murano/cmd/status.py b/murano/cmd/status.py deleted file mode 100644 index 5c48b3896..000000000 --- a/murano/cmd/status.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2018 NEC, Corp. -# -# Licensed 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 sys - -from oslo_config import cfg -from oslo_upgradecheck import common_checks -from oslo_upgradecheck import upgradecheck - -from murano.common.i18n import _ - -CONF = cfg.CONF - - -class Checks(upgradecheck.UpgradeCommands): - - """Contains upgrade checks - - Various upgrade checks should be added as separate methods in this class - and added to _upgrade_checks tuple. - """ - - _upgrade_checks = ( - (_("Policy File JSON to YAML Migration"), - (common_checks.check_policy_json, {'conf': CONF})), - ) - - -def main(): - return upgradecheck.main( - CONF, project='murano', upgrade_command=Checks()) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/murano/cmd/test_runner.py b/murano/cmd/test_runner.py deleted file mode 100644 index 813e0333e..000000000 --- a/murano/cmd/test_runner.py +++ /dev/null @@ -1,383 +0,0 @@ -# Licensed 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 argparse -import os -import re -import sys -import traceback - -import eventlet -from keystoneclient.v3 import client as ks_client -from muranoclient.common import exceptions as exc -from muranoclient.common import utils -from oslo_config import cfg -from oslo_db import options -from oslo_log import log as logging -from oslo_utils import timeutils - -from murano.common import config -from murano.common import engine -from murano.common.i18n import _ -from murano.dsl import dsl_exception -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import executor -from murano.dsl import helpers -from murano.engine import execution_session -from murano.engine import mock_context_manager -from murano.engine import package_loader -from murano import version - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) -options.set_defaults(CONF) - -BASE_CLASS = 'io.murano.test.TestFixture' -TEST_CASE_NAME = re.compile('^test(?![a-z])') - -OK_COLOR = '\033[92m' -FAIL_COLOR = '\033[91m' -END_COLOR = '\033[0m' - - -if os.name == 'nt': - # eventlet monkey patching causes subprocess.Popen to fail on Windows - # when using pipes due to missing non blocking I/O support - eventlet.monkey_patch(os=False) -else: - eventlet.monkey_patch() - - -class MuranoTestRunner(object): - def __init__(self): - self.parser = self.get_parser() - self.args = self.parser.parse_args() - - if self.args.verbose: - LOG.logger.setLevel(logging.DEBUG) - - def _load_package(self, pkg_loader, name): - try: - parts = name.rsplit('/') - if len(parts) == 2: - name, pkg_version = parts - version_spec = helpers.parse_version_spec(pkg_version) - else: - version_spec = helpers.parse_version_spec('*') - package = pkg_loader.load_package(name, version_spec) - except exceptions.NoPackageFound: - if not CONF.engine.load_packages_from: - msg = _('Local package is not found since "load-packages-from"' - ' engine parameter is not provided and specified ' - 'packages is not loaded to murano-api') - else: - msg = _('Specified package is not found: {0} were scanned ' - 'together with murano database' - ).format(','.join( - CONF.engine.load_packages_from)) - LOG.error(msg) - self.error(msg, show_help=False) - except exc.CommunicationError: - msg = ('Murano API is not running. ' - 'Check configuration parameters.') - LOG.error(msg) - self.error(msg, show_help=False) - return package - - def _get_methods_to_run(self, package, tests_to_run, class_to_methods): - if not tests_to_run: - return class_to_methods - - methods_to_run = {} - - for item in tests_to_run: - # Check for method name occurrence in all methods. - # if there is no dot in provided item - it is a method name - if '.' not in item: - for class_name, methods in class_to_methods.items(): - methods_to_run[class_name] = [] - if item in methods: - methods_to_run[class_name].append(item) - continue - # Check match for the whole class name - if item in package.classes: - # Add all test cases from specified package - if item in class_to_methods: - methods_to_run[item] = class_to_methods[item] - continue - - # Check match for the class together with method specified - class_to_test, test_method = item.rsplit('.', 1) - if class_to_test in package.classes: - methods_to_run[class_to_test] = [ - m for m in class_to_methods[class_to_test] - if m == test_method] - continue - methods_count = sum(len(v) for v in methods_to_run.values()) - methods = [k + '.' + method - for k, v in methods_to_run.items() for method in v] - LOG.debug('{0} method(s) is(are) going to be executed: ' - '\n{1}'.format(methods_count, '\n'.join(methods))) - return methods_to_run - - def _get_test_cases_by_classes(self, package): - """Build valid test cases list for each class in the provided package. - - Check, if test class and test case name are valid. - Return class mappings to test cases. - """ - class_to_methods = {} - for pkg_class_name in package.classes: - class_obj = package.find_class(pkg_class_name, False) - base_class = package.find_class(BASE_CLASS) - if not base_class.is_compatible(class_obj): - LOG.debug('Class {0} is not inherited from {1}. ' - 'Skipping it.'.format(pkg_class_name, BASE_CLASS)) - continue - - # Exclude methods, that are not test cases. - tests = [] - valid_test_name = TEST_CASE_NAME - - for m in class_obj.methods: - if valid_test_name.match(m): - tests.append(m) - class_to_methods[pkg_class_name] = tests - return class_to_methods - - def _call_service_method(self, name, exc, obj): - if name in obj.type.all_method_names: - method = obj.type.find_single_method(name) - method.scope = dsl_types.MethodScopes.Public - LOG.debug('Executing: {0}.{1}'.format(obj.type.name, name)) - exc.run(obj.type, name, obj, (), {}) - - def _validate_keystone_opts(self, args): - ks_opts_to_config = { - 'auth_url': 'www_authenticate_uri', - 'username': 'admin_user', - 'password': 'admin_password', - 'project_name': 'admin_project_name'} - - ks_opts = {'auth_url': getattr(args, 'os_auth_url', None), - 'username': getattr(args, 'os_username', None), - 'password': getattr(args, 'os_password', None), - 'project_name': getattr(args, 'os_project_name', None)} - - if None in ks_opts.values() and not CONF.default_config_files: - msg = ('Please provide murano config file or credentials for ' - 'authorization: {0}').format( - ', '.join(['--os-auth-url', '--os-username', '--os-password', - '--os-project-name', '--os-tenant-id'])) - LOG.error(msg) - self.error(msg) - - for param, value in ks_opts.items(): - if not value: - ks_opts[param] = getattr(CONF.murano_auth, - ks_opts_to_config[param]) - if param == 'auth_url': - ks_opts[param] = ks_opts[param].replace('v2.0', 'v3') - return ks_opts - - def error(self, msg, show_help=True): - sys.stderr.write("ERROR: {msg}\n\n".format(msg=msg)) - if show_help: - self.parser.print_help() - sys.exit(1) - - def message(self, msg): - sys.stdout.write('{0}\n'.format(msg)) - - def run_tests(self): - exit_code = 0 - provided_pkg_name = self.args.package - load_packages_from = self.args.load_packages_from - tests_to_run = self.args.tests - - ks_opts = self._validate_keystone_opts(self.args) - - client = ks_client.Client(**ks_opts) - test_session = execution_session.ExecutionSession() - test_session.token = client.auth_token - test_session.project_id = client.project_id - - # Replace location of loading packages with provided from command line. - if load_packages_from: - cfg.CONF.engine.load_packages_from = load_packages_from - with package_loader.CombinedPackageLoader(test_session) as pkg_loader: - engine.get_plugin_loader().register_in_loader(pkg_loader) - - package = self._load_package(pkg_loader, provided_pkg_name) - class_to_methods = self._get_test_cases_by_classes(package) - run_set = self._get_methods_to_run(package, - tests_to_run, - class_to_methods) - max_length = 0 - num_tests = 0 - for pkg_class, test_cases in run_set.items(): - for m in test_cases: - max_length = max(max_length, len(pkg_class) + len(m) + 1) - num_tests += len(test_cases) - max_length += 3 - - if run_set: - LOG.debug('Starting test execution.') - self.message('About to execute {0} tests(s)'.format(num_tests)) - else: - msg = _('No tests found for execution.') - LOG.error(msg) - self.error(msg) - - run_count = 0 - error_count = 0 - started = timeutils.utcnow() - for pkg_class, test_cases in run_set.items(): - for m in test_cases: - # Create new executor for each test case to provide - # pure test environment - dsl_executor = executor.MuranoDslExecutor( - pkg_loader, - mock_context_manager.MockContextManager(), - test_session) - obj = dsl_executor.object_store.load( - {}, None, - default_type=package.find_class(pkg_class, False)) - - test_name = "{0}.{1}".format(obj.type.name, m) - dots_number = max_length - len(test_name) - msg = "{0} {1} ".format(test_name, '.' * dots_number) - sys.stdout.write(msg) - sys.stdout.flush() - self._call_service_method('setUp', dsl_executor, obj) - obj.type.methods[m].usage = 'Action' - test_session.start() - try: - run_count += 1 - dsl_executor.run(obj.type, m, obj, (), {}) - self._call_service_method( - 'tearDown', dsl_executor, obj) - msg = '{0}{1}{2}\n'.format(OK_COLOR, 'OK', END_COLOR) - LOG.debug('Test {0} successful'.format(test_name)) - sys.stdout.write(msg) - sys.stdout.flush() - except Exception as e: - error_count += 1 - msg = ''.join(( - FAIL_COLOR, 'FAIL!', END_COLOR, '\n')) - sys.stdout.write(msg) - if isinstance(e, dsl_exception.MuranoPlException): - tb = e.format() - else: - tb = traceback.format_exc() - - sys.stdout.write(''.join(( - FAIL_COLOR, - tb, - END_COLOR, - '\n' - ))) - sys.stdout.flush() - - LOG.exception('Test {0} failed'.format(test_name)) - exit_code = 1 - finally: - test_session.finish() - completed = timeutils.utcnow() - self.message('Executed {0} tests in {1} seconds: ' - '{2} passed, ' - '{3} failed'.format(run_count, - timeutils.delta_seconds( - started, completed), - run_count - error_count, - error_count)) - return exit_code - - def get_parser(self): - parser = argparse.ArgumentParser(prog='murano-test-runner') - parser.set_defaults(func=self.run_tests) - parser.add_argument('--config-file', - help='Path to the murano config') - - parser.add_argument('--os-auth-url', - default=utils.env('OS_AUTH_URL'), - help='Defaults to env[OS_AUTH_URL]') - parser.add_argument('--os-username', - default=utils.env('OS_USERNAME'), - help='Defaults to env[OS_USERNAME]') - - parser.add_argument('--os-password', - default=utils.env('OS_PASSWORD'), - help='Defaults to env[OS_PASSWORD]') - - parser.add_argument('--os-project-name', - default=utils.env('OS_PROJECT_NAME'), - help='Defaults to env[OS_PROJECT_NAME]') - parser.add_argument('-l', '--load_packages_from', - nargs='*', metavar='path', - help='Directory to search packages from. ' - 'We be added to the list of current directory' - ' list, provided in a config file.') - parser.add_argument("-v", "--verbose", action="store_true", - help="increase output verbosity") - parser.add_argument('--version', action='version', - version=version.version_string) - parser.add_argument('package', - metavar='', - help='Full name of application package that is ' - 'going to be tested') - parser.add_argument('tests', nargs='*', - metavar='className.testMethod', - help='List of method names to be tested') - return parser - - -def main(): - test_runner = MuranoTestRunner() - try: - if test_runner.args.config_file: - default_config_files = [test_runner.args.config_file] - else: - default_config_files = cfg.find_config_files('murano') - if not default_config_files: - murano_conf = os.path.join(os.path.dirname(__file__), - os.pardir, - os.pardir, - 'etc', 'murano', - 'murano.conf') - if os.path.exists(murano_conf): - default_config_files = [murano_conf] - sys.argv = [sys.argv[0]] - config.parse_args(default_config_files=default_config_files) - CONF.set_default('use_stderr', False) - logging.setup(CONF, 'murano') - except RuntimeError as e: - LOG.exception("Failed to initialize murano-test-runner: %s", e) - sys.exit("ERROR: %s" % e) - - try: - exit_code = test_runner.run_tests() - sys.exit(exit_code) - except Exception as e: - if isinstance(e, dsl_exception.MuranoPlException): - tb = e.format() - else: - tb = traceback.format_exc() - err_msg = "Command failed: {0}\n{1}".format(e, tb) - LOG.error(err_msg) - sys.exit(err_msg) - - -if __name__ == '__main__': - main() diff --git a/murano/common/__init__.py b/murano/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/common/app_loader.py b/murano/common/app_loader.py deleted file mode 100644 index ce30f5380..000000000 --- a/murano/common/app_loader.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 os - -from oslo_config import cfg -from oslo_log import log as logging -from paste import deploy - -from murano.common.i18n import _ - -CONF = cfg.CONF - - -def _get_deployment_flavor(): - """Retrieve the paste_deploy.flavor config item - - Retrieve the paste_deploy.flavor config item, formatted appropriately - for appending to the application name. - """ - flavor = CONF.paste_deploy.flavor - return '' if not flavor else ('-' + flavor) - - -def _get_paste_config_path(): - paste_suffix = '-paste.ini' - conf_suffix = '.conf' - if CONF.config_file: - # Assume paste config is in a paste.ini file corresponding - # to the last config file - path = CONF.config_file[-1].replace(conf_suffix, paste_suffix) - else: - path = CONF.prog + '-paste.ini' - return CONF.find_file(os.path.basename(path)) - - -def _get_deployment_config_file(): - """Retrieve the deployment_config_file config item - - Retrieve the deployment_config_file config item, formatted as an - absolute pathname. - """ - path = CONF.paste_deploy.config_file - if not path: - path = _get_paste_config_path() - if not path: - msg = _("Unable to locate paste config file for %s.") % CONF.prog - raise RuntimeError(msg) - return os.path.abspath(path) - - -def load_paste_app(app_name=None): - """Builds and returns a WSGI app from a paste config file. - - We assume the last config file specified in the supplied ConfigOpts - object is the paste config file. - - :param app_name: name of the application to load - - :raises RuntimeError when config file cannot be located or application - cannot be loaded from config file - """ - if app_name is None: - app_name = CONF.prog - - # append the deployment flavor to the application name, - # in order to identify the appropriate paste pipeline - app_name += _get_deployment_flavor() - - conf_file = _get_deployment_config_file() - - try: - logger = logging.getLogger(__name__) - logger.debug("Loading {app_name} from {conf_file}".format( - conf_file=conf_file, app_name=app_name)) - - app = deploy.loadapp("config:%s" % conf_file, name=app_name) - - return app - except (LookupError, ImportError) as e: - msg = _("Unable to load %(app_name)s from configuration file" - " %(conf_file)s. \nGot: %(e)r") % {'conf_file': conf_file, - 'app_name': app_name, - 'e': e} - logger.error(msg) - raise RuntimeError(msg) diff --git a/murano/common/auth_utils.py b/murano/common/auth_utils.py deleted file mode 100644 index 78279336b..000000000 --- a/murano/common/auth_utils.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 os - -from keystoneauth1 import identity -from keystoneauth1 import loading as ka_loading -from keystoneclient.v3 import client as ks_client -from oslo_config import cfg -from oslo_log import log as logging -from oslo_log import versionutils - -from murano.dsl import helpers - - -CFG_KEYSTONE_GROUP = 'keystone_authtoken' -CFG_MURANO_AUTH_GROUP = 'murano_auth' -LOG = logging.getLogger(__name__) - -cfg.CONF.import_group(CFG_KEYSTONE_GROUP, 'keystonemiddleware.auth_token') - - -def _get_keystone_auth(trust_id=None): - kwargs = {} - if trust_id: - # Remove project_name and project_id, since we need a trust scoped - # auth object - kwargs['project_name'] = None - kwargs['project_domain_name'] = None - kwargs['project_id'] = None - kwargs['trust_id'] = trust_id - auth = ka_loading.load_auth_from_conf_options( - cfg.CONF, - CFG_MURANO_AUTH_GROUP, - **kwargs) - return auth - - -def _create_keystone_admin_client(): - auth = _get_keystone_auth() - session = _get_session(auth=auth) - return ks_client.Client(session=session) - - -def get_client_session(execution_session=None, conf=None): - if not execution_session: - execution_session = helpers.get_execution_session() - trust_id = execution_session.trust_id - if trust_id is None: - return get_token_client_session( - token=execution_session.token, - project_id=execution_session.project_id) - auth = _get_keystone_auth(trust_id) - session = _get_session(auth=auth, conf_section=conf) - return session - - -def get_token_client_session(token=None, project_id=None, conf=None): - www_authenticate_uri = \ - cfg.CONF[CFG_MURANO_AUTH_GROUP].www_authenticate_uri - if not www_authenticate_uri: - versionutils.report_deprecated_feature( - LOG, 'Please configure www_authenticate_uri in ' + - CFG_MURANO_AUTH_GROUP + 'group') - www_authenticate_uri = \ - cfg.CONF[CFG_KEYSTONE_GROUP].www_authenticate_uri - if not (www_authenticate_uri.endswith('v2.0') or - www_authenticate_uri.endswith('v3')): - auth_url = os.path.join(www_authenticate_uri, 'v3') - elif www_authenticate_uri.endswith('v2.0'): - auth_url = www_authenticate_uri.replace('v2.0', 'v3') - else: - auth_url = www_authenticate_uri - - if token is None or project_id is None: - execution_session = helpers.get_execution_session() - token = execution_session.token - project_id = execution_session.project_id - token_auth = identity.Token( - auth_url, - token=token, - project_id=project_id) - session = _get_session(auth=token_auth, conf_section=conf) - return session - - -def create_keystone_client(token=None, project_id=None, conf=None): - return ks_client.Client(session=get_token_client_session( - token=token, project_id=project_id, conf=conf)) - - -def create_trust(trustee_token=None, trustee_project_id=None): - admin_client = _create_keystone_admin_client() - user_client = create_keystone_client( - token=trustee_token, project_id=trustee_project_id) - trustee_user = admin_client.session.auth.get_user_id(admin_client.session) - auth_ref = user_client.session.auth.get_access(user_client.session) - trustor_user = auth_ref.user_id - project = auth_ref.project_id - roles = auth_ref.role_names - trust = user_client.trusts.create( - trustor_user=trustor_user, - trustee_user=trustee_user, - impersonation=True, - role_names=roles, - project=project) - return trust.id - - -def delete_trust(session): - user_client = create_keystone_client( - token=session.token, project_id=session.project_id) - user_client.trusts.delete(session.trust_id) - - -def _get_config_option(conf_section, option_name, default=None): - if conf_section and hasattr(cfg.CONF[conf_section], option_name): - return getattr(cfg.CONF[conf_section], option_name) - return default - - -def _get_session(auth, conf_section=None): - # Fallback to murano_auth section for TLS parameters - # if no other conf_section supplied - if not conf_section: - conf_section = CFG_MURANO_AUTH_GROUP - session = ka_loading.load_session_from_conf_options( - auth=auth, - conf=cfg.CONF, - group=conf_section) - return session - - -def get_session_client_parameters(service_type=None, - region='', - interface=None, - service_name=None, - conf=None, - session=None, - execution_session=None): - if region == '': - region = cfg.CONF.home_region - result = { - 'session': session or get_client_session( - execution_session=execution_session, conf=conf) - } - - url = _get_config_option(conf, 'url') - if url: - result['endpoint_override'] = url - else: - if not interface: - interface = _get_config_option(conf, 'endpoint_type') - result.update({ - 'service_type': service_type, - 'service_name': service_name, - 'interface': interface, - 'region_name': region - }) - return result - - -def get_user(uid): - client = _create_keystone_admin_client() - return client.users.get(uid).to_dict() - - -def get_project(pid): - client = _create_keystone_admin_client() - return client.projects.get(pid).to_dict() diff --git a/murano/common/cf_config.py b/murano/common/cf_config.py deleted file mode 100644 index 7b7490892..000000000 --- a/murano/common/cf_config.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 oslo_config import cfg -from oslo_log import log as logging -from oslo_middleware import cors - -from murano.common.i18n import _ -from murano import version - - -cfapi_opts = [ - cfg.StrOpt('tenant', default='admin', - help=_('Project for service broker')), - cfg.HostAddressOpt('bind_host', default='localhost', - help=_('Host for service broker')), - cfg.StrOpt('bind_port', default='8083', - help=_('Port for service broker')), - cfg.StrOpt('auth_url', default='localhost:5000', - help=_('Authentication URL')), - cfg.StrOpt('user_domain_name', default='default', - help=_('Domain name of the user')), - cfg.StrOpt('project_domain_name', default='default', - help=_('Domain name of the project')), - cfg.StrOpt('packages_service', default='murano', - help=_('Package service which should be used by service' - ' broker'))] - -CONF = cfg.CONF -CONF.register_opts(cfapi_opts, group='cfapi') - - -def parse_args(args=None, usage=None, default_config_files=None): - logging.register_options(CONF) - CONF(args=args, - project='murano', - version=version.version_string, - usage=usage, - default_config_files=default_config_files) - - -def set_middleware_defaults(): - """Update default configuration options for oslo.middleware.""" - cors.set_defaults( - allow_headers=['X-Auth-Token', - 'X-Openstack-Request-Id', - 'X-Configuration-Session', - 'X-Roles', - 'X-User-Id', - 'X-Tenant-Id'], - expose_headers=['X-Auth-Token', - 'X-Openstack-Request-Id', - 'X-Configuration-Session', - 'X-Roles', - 'X-User-Id', - 'X-Tenant-Id'], - allow_methods=['GET', - 'PUT', - 'POST', - 'DELETE', - 'PATCH'] - ) diff --git a/murano/common/config.py b/murano/common/config.py deleted file mode 100644 index 14dfa1326..000000000 --- a/murano/common/config.py +++ /dev/null @@ -1,352 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed 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 keystoneauth1 import loading as ks_loading -from oslo_config import cfg -from oslo_log import log as logging -from oslo_middleware import cors -from oslo_policy import opts - -from murano.common.i18n import _ -from murano import version - -paste_deploy_opts = [ - cfg.StrOpt('flavor', help='Paste flavor'), - cfg.StrOpt('config_file', help='Path to Paste config file'), -] - -bind_opts = [ - cfg.HostAddressOpt('bind-host', default='0.0.0.0', - help='Address to bind the Murano API server to.'), - cfg.PortOpt('bind-port', - default=8082, - help='Port the bind the Murano API server to.'), -] - -rabbit_opts = [ - cfg.HostAddressOpt('host', default='localhost', - help='The RabbitMQ broker address which used for ' - 'communication with Murano guest agents.'), - - cfg.PortOpt('port', - default=5672, - help='The RabbitMQ broker port.'), - - cfg.StrOpt('login', default='guest', - help='The RabbitMQ login.'), - - cfg.StrOpt('password', default='guest', secret=True, - help='The RabbitMQ password.'), - - cfg.StrOpt('virtual_host', default='/', - help='The RabbitMQ virtual host.'), - - cfg.BoolOpt('ssl', default=False, - help='Boolean flag to enable SSL communication through the ' - 'RabbitMQ broker between murano-engine and guest agents.'), - - cfg.StrOpt('ssl_version', - default='', - help='SSL version to use (valid only if SSL enabled). ' - 'Valid values are TLSv1 and SSLv23. SSLv2, SSLv3, ' - 'TLSv1_1, and TLSv1_2 may be available on some ' - 'distributions.'), - - cfg.StrOpt('ca_certs', default='', - help='SSL cert file (valid only if SSL enabled).'), - - cfg.BoolOpt('insecure', default=False, - help='This option explicitly allows Murano to perform ' - '"insecure" SSL connections to RabbitMQ'), -] - -heat_opts = [ - cfg.StrOpt('url', help='Optional heat endpoint override'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Heat endpoint type.'), - - cfg.ListOpt('stack_tags', default=['murano'], - help='List of tags to be assigned to heat stacks created ' - 'during environment deployment.') -] - -mistral_opts = [ - cfg.StrOpt('url', help='Optional mistral endpoint override'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Mistral endpoint type.'), - - cfg.StrOpt('service_type', default='workflowv2', - help='Mistral service type.') -] - -neutron_opts = [ - cfg.StrOpt('url', help='Optional neutron endpoint override'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Neutron endpoint type.') -] - -murano_opts = [ - cfg.StrOpt('url', help='Optional murano url in format ' - 'like http://0.0.0.0:8082 used by Murano engine'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Murano endpoint type used by Murano engine.'), - - cfg.ListOpt('enabled_plugins', - help="List of enabled Extension Plugins. " - "Remove or leave commented to enable all installed " - "plugins."), - - cfg.IntOpt('package_size_limit', default=5, - help='Maximum application package size, Mb', - deprecated_group='packages_opts'), - - cfg.IntOpt('limit_param_default', default=20, - help='Default value for package pagination in API.', - deprecated_group='packages_opts'), - - cfg.IntOpt('api_limit_max', default=100, - help='Maximum number of packages to be returned in a single ' - 'pagination request', - deprecated_group='packages_opts'), - - cfg.IntOpt('api_workers', - help=_('Number of API workers')), - - cfg.IntOpt('dsl_iterators_limit', default=2000, - help=_('Maximum number of elements that can be iterated per ' - 'object type.')), - -] - -networking_opts = [ - cfg.IntOpt('max_environments', default=250, - help='Maximum number of environments that use a single router ' - 'per tenant'), - - cfg.IntOpt('max_hosts', default=250, - help='Maximum number of VMs per environment'), - - cfg.HostAddressOpt('env_ip_template', default='10.0.0.0', - help='Template IP address for generating environment ' - 'subnet cidrs'), - - cfg.ListOpt('default_dns', default=[], - help='List of default DNS nameservers to be assigned to ' - 'created Networks'), - - cfg.StrOpt('external_network', default='ext-net', - help='ID or name of the external network for routers ' - 'to connect to'), - - cfg.StrOpt('router_name', default='murano-default-router', - help='Name of the router that going to be used in order to ' - 'join all networks created by Murano'), - - cfg.BoolOpt('create_router', default=True, - help='This option will create a router when one with ' - '"router_name" does not exist'), - - cfg.StrOpt('network_config_file', default='netconfig.yaml', - help='If provided networking configuration will be taken ' - 'from this file'), - - cfg.StrOpt('driver', choices=['neutron', 'nova'], - help='Network driver to use. Options are neutron or nova.' - 'If not provided, the driver will be detected.'), -] - -stats_opts = [ - cfg.IntOpt('period', default=5, - help=_('Statistics collection interval in minutes.' - 'Default value is 5 minutes.')), - cfg.IntOpt('env_audit_period', default=60, - help=_('Environment audit interval in minutes. ' - 'Default value is 60 minutes.')), - cfg.BoolOpt('env_audit_enabled', default=True, - help=_('Whether environment audit events enabled')) -] - -engine_opts = [ - cfg.BoolOpt('disable_murano_agent', default=False, - help=_('Disallow the use of murano-agent')), - cfg.StrOpt('class_configs', default='/etc/murano/class-configs', - help=_('Path to class configuration files')), - cfg.BoolOpt('use_trusts', default=True, - help=_("Create resources using trust token rather " - "than user's token")), - cfg.BoolOpt('enable_model_policy_enforcer', default=False, - help=_('Enable model policy enforcer using Congress')), - cfg.IntOpt('agent_timeout', default=3600, - help=_('Time for waiting for a response from murano agent ' - 'during the deployment')), - cfg.IntOpt('engine_workers', - deprecated_opts=[cfg.DeprecatedOpt('workers', - group='engine')], - help=_('Number of engine workers')), - - cfg.ListOpt('load_packages_from', default=[], - help=_('List of directories to load local packages from. ' - 'If not provided, packages will be loaded only API'), - deprecated_group='packages_opts'), - - cfg.StrOpt('packages_cache', - help='Location (directory) for Murano package cache.', - deprecated_group='packages_opts'), - - cfg.BoolOpt('enable_packages_cache', default=True, - help=_('Enables murano-engine to persist on disk ' - 'packages downloaded during deployments. ' - 'The packages would be re-used for consequent ' - 'deployments.'), - deprecated_group='packages_opts'), - - cfg.StrOpt('packages_service', default='murano', - help=_('The service to store murano packages: murano (stands ' - 'for legacy behavior using murano-api) or glance ' - '(stands for glance-glare artifact service)'), - deprecated_group='packages_opts'), - - cfg.StrOpt('signing_key', default='~/.ssh/id_rsa', - help=_('Path to RSA key for agent message signing')), - - cfg.StrOpt('agent_source', default='murano-agent', - help=_('pip URL/package spec for murano-agent')), -] - -# TODO(sjmc7): move into engine opts? -metadata_dir = [ - cfg.StrOpt('metadata-dir', default='./meta', - help='Metadata dir') -] - -glare_opts = [ - cfg.StrOpt('url', help='Optional glare url in format ' - 'like http://0.0.0.0:9494 used by Glare API', - deprecated_group='glance'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Glare endpoint type.', - deprecated_group='glance') -] - -glance_opts = [ - cfg.StrOpt('url', help='Optional glance endpoint override'), - - cfg.StrOpt('endpoint_type', default='publicURL', - help='Glance endpoint type.') -] - -file_server = [ - cfg.StrOpt('file_server', default='', - help='Set a file server.') -] - -home_region = cfg.StrOpt( - 'home_region', - help="Default region name used to get services endpoints.") - -# Unfortuntely we cannot use murano_auth.auth_url, since it -# is private to the actual authentication plugin used. -murano_auth_opts = [ - cfg.StrOpt('www_authenticate_uri', - help='Identity API endpoint for authenticating with tokens.')] - - -CONF = cfg.CONF -CONF.register_opts(paste_deploy_opts, group='paste_deploy') -CONF.register_cli_opts(bind_opts) -CONF.register_opts(rabbit_opts, group='rabbitmq') -CONF.register_opts(heat_opts, group='heat') -CONF.register_opts(mistral_opts, group='mistral') -CONF.register_opts(neutron_opts, group='neutron') -CONF.register_opts(murano_opts, group='murano') -CONF.register_opts(engine_opts, group='engine') -CONF.register_opts(file_server) -CONF.register_opt(home_region) -CONF.register_cli_opts(metadata_dir) -CONF.register_opts(stats_opts, group='stats') -CONF.register_opts(networking_opts, group='networking') -CONF.register_opts(glare_opts, group='glare') -CONF.register_opts(glance_opts, group='glance') -CONF.register_opts(murano_auth_opts, group='murano_auth') -ks_loading.register_auth_conf_options(CONF, group='murano_auth') - - -for group in ('heat', 'mistral', 'neutron', 'glance', 'glare', - 'murano', 'murano_auth'): - ks_loading.register_session_conf_options( - CONF, - group=group, - deprecated_opts={ - 'cafile': [cfg.DeprecatedOpt('cacert', group), - cfg.DeprecatedOpt('ca_file', group)], - 'certfile': [cfg.DeprecatedOpt('cert_file', group)], - 'keyfile': [cfg.DeprecatedOpt('key_file', group)] - - }) - - -def parse_args(args=None, usage=None, default_config_files=None): - logging.register_options(CONF) - CONF(args=args, - project='murano', - version=version.version_string, - usage=usage, - default_config_files=default_config_files) - - -def set_lib_defaults(): - """Update default value for configuration options from other namespace. - - Example, oslo lib config options. This is needed for - config generator tool to pick these default value changes. - https://docs.openstack.org/oslo.config/latest/cli/ - generator.html#modifying-defaults-from-other-namespaces - """ - - set_middleware_defaults() - - # TODO(gmann): Remove setting the default value of config policy_file - # once oslo_policy change the default value to 'policy.yaml'. - # https://github.com/openstack/oslo.policy/blob/a626ad12fe5a3abd49d70e3e5b95589d279ab578/oslo_policy/opts.py#L49 - opts.set_defaults(CONF, 'policy.yaml') - - -def set_middleware_defaults(): - """Update default configuration options for oslo.middleware.""" - - cors.set_defaults( - allow_headers=['X-Auth-Token', - 'X-Openstack-Request-Id', - 'X-Configuration-Session', - 'X-Roles', - 'X-User-Id', - 'X-Tenant-Id'], - expose_headers=['X-Auth-Token', - 'X-Openstack-Request-Id', - 'X-Configuration-Session', - 'X-Roles', - 'X-User-Id', - 'X-Tenant-Id'], - allow_methods=['GET', - 'PUT', - 'POST', - 'DELETE', - 'PATCH'] - ) diff --git a/murano/common/consts.py b/murano/common/consts.py deleted file mode 100644 index b82789485..000000000 --- a/murano/common/consts.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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. - -CATEGORIES = ['Web', 'Databases', 'Message Queue', 'Application Servers', - 'Microsoft Services', 'Load Balancers', 'Big Data', - 'Key-Value Storage', 'SAP'] diff --git a/murano/common/engine.py b/murano/common/engine.py deleted file mode 100644 index 9344f2880..000000000 --- a/murano/common/engine.py +++ /dev/null @@ -1,371 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 copy -import traceback -import uuid - -import eventlet.debug -from oslo_config import cfg -from oslo_log import log as logging -from oslo_messaging import target -from oslo_serialization import jsonutils -from oslo_service import service - -from murano.common import auth_utils -from murano.common.helpers import token_sanitizer -from murano.common.plugins import extensions_loader -from murano.common import rpc -from murano.dsl import context_manager -from murano.dsl import dsl_exception -from murano.dsl import executor as dsl_executor -from murano.dsl import helpers -from murano.dsl import schema_generator -from murano.dsl import serializer -from murano.engine import execution_session -from murano.engine import package_loader -from murano.engine.system import status_reporter -from murano.engine.system import yaql_functions -from murano.policy import model_policy_enforcer as enforcer - -CONF = cfg.CONF - -PLUGIN_LOADER = None - -LOG = logging.getLogger(__name__) - -eventlet.debug.hub_exceptions(False) - - -# noinspection PyAbstractClass -class EngineService(service.Service): - def __init__(self): - super(EngineService, self).__init__() - self.server = None - - def start(self): - if not rpc.initialized(): - rpc.init() - endpoints = [ - TaskProcessingEndpoint(), - StaticActionEndpoint(), - SchemaEndpoint() - ] - - s_target = target.Target('murano', 'tasks', server=str(uuid.uuid4())) - self.server = rpc.get_server(s_target, endpoints, executor='eventlet') - self.server.start() - super(EngineService, self).start() - - def stop(self, graceful=False): - if self.server: - self.server.stop() - if graceful: - self.server.wait() - super(EngineService, self).stop() - - def reset(self): - if self.server: - self.server.reset() - super(EngineService, self).reset() - - -def get_plugin_loader(): - global PLUGIN_LOADER - - if PLUGIN_LOADER is None: - PLUGIN_LOADER = extensions_loader.PluginLoader() - return PLUGIN_LOADER - - -class ContextManager(context_manager.ContextManager): - def create_root_context(self, runtime_version): - root_context = super(ContextManager, self).create_root_context( - runtime_version) - return helpers.link_contexts( - root_context, yaql_functions.get_context(runtime_version)) - - def create_package_context(self, package): - context = super(ContextManager, self).create_package_context( - package) - if package.name == 'io.murano': - context = helpers.link_contexts( - context, yaql_functions.get_restricted_context()) - return context - - -class SchemaEndpoint(object): - @classmethod - def generate_schema(cls, context, *args, **kwargs): - session = execution_session.ExecutionSession() - session.token = context['token'] - session.project_id = context['project_id'] - with package_loader.CombinedPackageLoader(session) as pkg_loader: - return schema_generator.generate_schema( - pkg_loader, ContextManager(), *args, **kwargs) - - -class TaskProcessingEndpoint(object): - @classmethod - def handle_task(cls, context, task): - result = cls.execute(task) - rpc.api().process_result(result, task['id']) - - @staticmethod - def execute(task): - s_task = token_sanitizer.TokenSanitizer().sanitize(task) - LOG.info('Starting processing task: {task_desc}'.format( - task_desc=jsonutils.dumps(s_task))) - - result = None - reporter = status_reporter.StatusReporter(task['id']) - - try: - task_executor = TaskExecutor(task, reporter) - result = task_executor.execute() - return result - finally: - s_result = token_sanitizer.TokenSanitizer().sanitize(result) - LOG.info('Finished processing task: {task_desc}'.format( - task_desc=jsonutils.dumps(s_result))) - - -class StaticActionEndpoint(object): - @classmethod - def call_static_action(cls, context, task): - s_task = token_sanitizer.TokenSanitizer().sanitize(task) - LOG.info('Starting execution of static action: ' - '{task_desc}'.format(task_desc=jsonutils.dumps(s_task))) - - result = None - reporter = status_reporter.StatusReporter(task['id']) - - try: - task_executor = StaticActionExecutor(task, reporter) - result = task_executor.execute() - return result - finally: - LOG.info('Finished execution of static action: ' - '{task_desc}'.format(task_desc=jsonutils.dumps(result))) - - -class TaskExecutor(object): - @property - def action(self): - return self._action - - @property - def session(self): - return self._session - - @property - def model(self): - return self._model - - def __init__(self, task, reporter=None): - if reporter is None: - reporter = status_reporter.StatusReporter(task['id']) - self._action = task.get('action') - self._model = task['model'] - self._session = execution_session.ExecutionSession() - self._session.token = task['token'] - self._session.project_id = task['project_id'] - self._session.user_id = task['user_id'] - self._session.environment_owner_project_id = self._model['project_id'] - self._session.environment_owner_user_id = self._model['user_id'] - self._session.system_attributes = self._model.get('SystemData', {}) - self._reporter = reporter - - self._model_policy_enforcer = enforcer.ModelPolicyEnforcer( - self._session) - - def execute(self): - try: - self._create_trust() - except Exception as e: - return self.exception_result(e, None, '') - - with package_loader.CombinedPackageLoader(self._session) as pkg_loader: - pkg_loader.import_fixation_table( - self._session.system_attributes.get('Packages', {})) - result = self._execute(pkg_loader) - self._session.system_attributes[ - 'Packages'] = pkg_loader.export_fixation_table() - self._model['SystemData'] = self._session.system_attributes - self._model['project_id'] = self._session.environment_owner_project_id - self._model['user_id'] = self._session.environment_owner_user_id - result['model'] = self._model - - if (not self._model.get('Objects') and - not self._model.get('ObjectsCopy')): - try: - self._delete_trust() - except Exception: - LOG.warning('Cannot delete trust', exc_info=True) - - return result - - def _execute(self, pkg_loader): - - get_plugin_loader().register_in_loader(pkg_loader) - with dsl_executor.MuranoDslExecutor( - pkg_loader, ContextManager(), self.session) as executor: - try: - obj = executor.load(self.model) - except Exception as e: - return self.exception_result(e, None, '') - - if obj is not None: - try: - self._validate_model(obj.object, pkg_loader, executor) - except Exception as e: - return self.exception_result(e, obj, '') - try: - LOG.debug('Invoking pre-cleanup hooks') - self.session.start() - executor.object_store.cleanup() - except Exception as e: - return self.exception_result(e, obj, '') - finally: - LOG.debug('Invoking post-cleanup hooks') - self.session.finish() - self._model['ObjectsCopy'] = \ - copy.deepcopy(self._model.get('Objects')) - - action_result = None - if self.action: - try: - LOG.debug('Invoking pre-execution hooks') - self.session.start() - action_result = self._invoke(executor) - except Exception as e: - return self.exception_result(e, obj, self.action['method']) - finally: - LOG.debug('Invoking post-execution hooks') - self.session.finish() - self._model = executor.finalize(obj) - try: - action_result = serializer.serialize(action_result, executor) - except Exception as e: - return self.exception_result(e, None, '') - - pkg_loader.compact_fixation_table() - return { - 'action': { - 'result': action_result, - 'isException': False - } - } - - def exception_result(self, exception, root, method_name): - if isinstance(exception, dsl_exception.MuranoPlException): - LOG.error('\n' + exception.format(prefix=' ')) - exception_traceback = exception.format() - else: - exception_traceback = traceback.format_exc() - LOG.exception( - ("Exception %(exc)s occurred" - " during invocation of %(method)s"), - {'exc': exception, 'method': method_name}) - self._reporter.report_error(root, str(exception)) - - return { - 'action': { - 'isException': True, - 'result': { - 'message': str(exception), - 'details': exception_traceback - } - } - } - - def _validate_model(self, obj, pkg_loader, executor): - if CONF.engine.enable_model_policy_enforcer: - if obj is not None: - with helpers.with_object_store(executor.object_store): - self._model_policy_enforcer.modify(obj, pkg_loader) - self._model_policy_enforcer.validate(obj.to_dictionary(), - pkg_loader) - - def _invoke(self, mpl_executor): - obj = mpl_executor.object_store.get(self.action['object_id']) - method_name, kwargs = self.action['method'], self.action['args'] - - if obj is not None: - return mpl_executor.run(obj.type, method_name, obj, (), kwargs) - - def _create_trust(self): - if not CONF.engine.use_trusts: - return - trust_id = self._session.system_attributes.get('TrustId') - if not trust_id: - trust_id = auth_utils.create_trust( - self._session.token, self._session.project_id) - self._session.system_attributes['TrustId'] = trust_id - self._session.trust_id = trust_id - - def _delete_trust(self): - trust_id = self._session.trust_id - if trust_id: - auth_utils.delete_trust(self._session) - self._session.system_attributes['TrustId'] = None - self._session.trust_id = None - - -class StaticActionExecutor(object): - @property - def action(self): - return self._action - - @property - def session(self): - return self._session - - def __init__(self, task, reporter=None): - if reporter is None: - reporter = status_reporter.StatusReporter(task['id']) - self._action = task['action'] - self._session = execution_session.ExecutionSession() - self._session.token = task['token'] - self._session.project_id = task['project_id'] - self._session.user_id = task['user_id'] - self._reporter = reporter - self._model_policy_enforcer = enforcer.ModelPolicyEnforcer( - self._session) - - def execute(self): - with package_loader.CombinedPackageLoader(self._session) as pkg_loader: - get_plugin_loader().register_in_loader(pkg_loader) - executor = dsl_executor.MuranoDslExecutor(pkg_loader, - ContextManager()) - action_result = self._invoke(executor) - action_result = serializer.serialize(action_result, executor) - return action_result - - def _invoke(self, mpl_executor): - class_name = self.action['class_name'] - pkg_name = self.action['pkg_name'] - class_version = self.action['class_version'] - version_spec = helpers.parse_version_spec(class_version) - if pkg_name: - package = mpl_executor.package_loader.load_package( - pkg_name, version_spec) - else: - package = mpl_executor.package_loader.load_class_package( - class_name, version_spec) - cls = package.find_class(class_name, search_requirements=False) - method_name, kwargs = self.action['method'], self.action['args'] - - return mpl_executor.run(cls, method_name, None, (), kwargs) diff --git a/murano/common/exceptions.py b/murano/common/exceptions.py deleted file mode 100644 index cafa8b180..000000000 --- a/murano/common/exceptions.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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. - -_FATAL_EXCEPTION_FORMAT_ERRORS = False - -# Exceptions from openstack-common - - -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) - - -class OpenstackException(Exception): - """Base Exception class. - - To correctly use this class, inherit from it and define - a 'msg_fmt' property. That message will get printf'd - with the keyword arguments provided to the constructor. - """ - msg_fmt = "An unknown exception occurred" - - def __init__(self, **kwargs): - try: - self._error_string = self.msg_fmt % kwargs - - except Exception: - if _FATAL_EXCEPTION_FORMAT_ERRORS: - raise - else: - # at least get the io.murano message out if something happened - self._error_string = self.msg_fmt - - def __str__(self): - return self._error_string - - -class UnsupportedContentType(OpenstackException): - msg_fmt = "Unsupported content type %(content_type)s" - - -class NotAcceptableContentType(OpenstackException): - msg_fmt = ("Response with content type %(content_type)s " - "expected but can not be provided") - - -class MalformedRequestBody(OpenstackException): - msg_fmt = "Malformed message body: %(reason)s" - -# Murano exceptions - - -class TimeoutException(Exception): - pass - - -class PolicyViolationException(Exception): - pass - - -class RouterInfoException(Exception): - pass diff --git a/murano/common/helpers/__init__.py b/murano/common/helpers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/common/helpers/path.py b/murano/common/helpers/path.py deleted file mode 100644 index 927c34343..000000000 --- a/murano/common/helpers/path.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2017 Mirantis Inc. -# -# Licensed 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 os.path - - -def secure_join(*parts): - """Secure version of os.path.join(*parts) - - Joins pathname components and ensures that with each join the result - is a subdirectory of the previous join - """ - new = prev = "" - for part in parts: - new = os.path.normpath(os.path.join(prev, part)) - if len(new) <= len(prev) or prev != "" and not new.startswith( - prev + os.path.sep): - raise ValueError('path {0} is not allowed {1}'.format( - os.path.join(*parts), parts)) - prev = new - return new diff --git a/murano/common/helpers/token_sanitizer.py b/murano/common/helpers/token_sanitizer.py deleted file mode 100644 index 0e1fb39c0..000000000 --- a/murano/common/helpers/token_sanitizer.py +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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. - - -class TokenSanitizer(object): - """Helper class for cleaning some object from different passwords/tokens - - Simply searches attribute with `look a like` name as one of - the token and replace it value with message. - """ - def __init__(self, tokens=('token', 'pass', 'trustid'), - message='*** SANITIZED ***'): - """Init method of TokenSanitizer - - :param tokens: iterable with tokens - :param message: string by which each token going to be replaced - """ - self._tokens = tokens - self._message = message - - @property - def tokens(self): - """Iterable with tokens that should be sanitized.""" - return self._tokens - - @property - def message(self): - """String by which each token going to be replaced.""" - return self._message - - def _contains_token(self, value): - for token in self.tokens: - if token in value.lower(): - return True - return False - - def sanitize(self, obj): - """Replaces each token found in object by message. - - :param obj: dict, list, tuple, object - :return: Sanitized object - """ - if isinstance(obj, dict): - return dict([self.sanitize(item) for item in obj.items()]) - elif isinstance(obj, list): - return [self.sanitize(item) for item in obj] - elif isinstance(obj, tuple): - k, v = obj - if self._contains_token(k) and isinstance(v, str): - return k, self.message - return k, self.sanitize(v) - else: - return obj diff --git a/murano/common/i18n.py b/murano/common/i18n.py deleted file mode 100644 index 226354ceb..000000000 --- a/murano/common/i18n.py +++ /dev/null @@ -1,24 +0,0 @@ -# Licensed 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. - -"""oslo.i18n integration module. - -See https://docs.openstack.org/oslo.i18n/latest/user/usage.html - -""" - -import oslo_i18n - -_translators = oslo_i18n.TranslatorFactory(domain='murano') - -# The primary translation function using the well-known name "_" -_ = _translators.primary diff --git a/murano/common/messaging/__init__.py b/murano/common/messaging/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/common/messaging/message.py b/murano/common/messaging/message.py deleted file mode 100644 index 7ab929300..000000000 --- a/murano/common/messaging/message.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 oslo_log import log as logging -from oslo_serialization import jsonutils - -LOG = logging.getLogger("murano-common.messaging") - - -class Message(object): - def __init__(self, connection=None, message_handle=None): - self._body = None - self._connection = connection - self._message_handle = message_handle - self.id = None if message_handle is None else \ - message_handle.properties.get('message_id') - try: - self.body = None if message_handle is None else \ - jsonutils.loads(message_handle.body) - except ValueError as e: - self.body = None - LOG.exception(e) - - @property - def body(self): - return self._body - - @body.setter - def body(self, value): - self._body = value - - @property - def id(self): - return self._id - - @id.setter - def id(self, value): - self._id = value or '' - - def ack(self): - self._message_handle.ack() diff --git a/murano/common/messaging/mqclient.py b/murano/common/messaging/mqclient.py deleted file mode 100644 index 48d15a036..000000000 --- a/murano/common/messaging/mqclient.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 ssl as ssl_module - -from eventlet import patcher -from oslo_serialization import jsonutils -from oslo_service import sslutils - -from murano.common.i18n import _ -from murano.common.messaging import subscription - -kombu = patcher.import_patched('kombu') - - -class MqClient(object): - def __init__(self, login, password, host, port, virtual_host, - ssl=False, ssl_version=None, ca_certs=None, insecure=False): - ssl_params = None - - if ssl: - cert_reqs = ssl_module.CERT_REQUIRED - if insecure: - if ca_certs: - cert_reqs = ssl_module.CERT_OPTIONAL - else: - cert_reqs = ssl_module.CERT_NONE - - ssl_params = { - 'ca_certs': ca_certs, - 'cert_reqs': cert_reqs - } - - if ssl_version: - key = ssl_version.lower() - try: - ssl_params['ssl_version'] = sslutils._SSL_PROTOCOLS[key] - except KeyError: - raise RuntimeError( - _("Invalid SSL version: %s") % ssl_version) - - self._connection = kombu.Connection( - 'amqp://{0}:{1}@{2}:{3}/{4}'.format( - login, - password, - host, - port, - virtual_host - ), ssl=ssl_params - ) - self._channel = None - self._connected = False - - def __enter__(self): - self.connect() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - return False - - def connect(self): - self._connection.connect() - self._channel = self._connection.channel() - self._connected = True - - def close(self): - self._connection.close() - self._connected = False - - def declare(self, queue, exchange='', enable_ha=False, ttl=0): - if not self._connected: - raise RuntimeError('Not connected to RabbitMQ') - - queue_arguments = {} - if enable_ha is True: - # To use mirrored queues feature in RabbitMQ 2.x - # we need to declare this policy on the queue itself. - # - # Warning: this option has no effect on RabbitMQ 3.X, - # to enable mirrored queues feature in RabbitMQ 3.X, please - # configure RabbitMQ. - queue_arguments['x-ha-policy'] = 'all' - if ttl > 0: - queue_arguments['x-expires'] = ttl - - exchange = kombu.Exchange(exchange, type='direct', durable=True) - queue = kombu.Queue(queue, exchange, queue, durable=True, - queue_arguments=queue_arguments) - bound_queue = queue(self._connection) - bound_queue.declare() - - def send(self, message, key, exchange='', signing_func=None): - if not self._connected: - raise RuntimeError('Not connected to RabbitMQ') - - producer = kombu.Producer(self._connection) - data = jsonutils.dumps(message.body) - headers = None - if signing_func: - headers = {'signature': signing_func(data)} - - producer.publish( - exchange=str(exchange), - routing_key=str(key), - body=data, - message_id=str(message.id), - headers=headers - ) - - def open(self, queue, prefetch_count=1): - if not self._connected: - raise RuntimeError('Not connected to RabbitMQ') - - return subscription.Subscription( - self._connection, queue, prefetch_count) diff --git a/murano/common/messaging/subscription.py b/murano/common/messaging/subscription.py deleted file mode 100644 index c7503f6a2..000000000 --- a/murano/common/messaging/subscription.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 collections -import socket -import time - -from eventlet import patcher - -from murano.common.messaging import message - -kombu = patcher.import_patched('kombu') - - -class Subscription(object): - def __init__(self, connection, queue, prefetch_count=1): - self._buffer = collections.deque() - self._connection = connection - self._queue = kombu.Queue(name=queue, exchange=None) - self._consumer = kombu.Consumer(self._connection, auto_declare=False) - self._consumer.register_callback(self._receive) - self._consumer.qos(prefetch_count=prefetch_count) - - def __enter__(self): - self._consumer.add_queue(self._queue) - self._consumer.consume() - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - if self._consumer is not None: - self._consumer.cancel() - return False - - def get_message(self, timeout=None): - msg_handle = self._get(timeout=timeout) - if msg_handle is None: - return None - return message.Message(self._connection, msg_handle) - - def _get(self, timeout=None): - elapsed = 0.0 - remaining = timeout - while True: - time_start = time.time() - if self._buffer: - return self._buffer.pop() - try: - self._connection.drain_events(timeout=timeout and remaining) - except socket.timeout: - return None - elapsed += time.time() - time_start - remaining = timeout and timeout - elapsed or None - - def _receive(self, message_data, message): - self._buffer.append(message) diff --git a/murano/common/plugins/__init__.py b/murano/common/plugins/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/common/plugins/extensions_loader.py b/murano/common/plugins/extensions_loader.py deleted file mode 100644 index d2c391ad2..000000000 --- a/murano/common/plugins/extensions_loader.py +++ /dev/null @@ -1,140 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 inspect -import re - -from oslo_config import cfg -from oslo_log import log as logging -from stevedore import dispatch - -from murano.dsl import murano_package - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - -# regexp validator to ensure that the entry-point name is a valid MuranoPL -# class name with an optional namespace name -NAME_RE = re.compile(r'^[a-zA-Z]\w*(\.[a-zA-Z]\w*)*$') - - -class PluginLoader(object): - def __init__(self, namespace="io.murano.extensions"): - LOG.info('Loading extension plugins') - self.namespace = namespace - extension_manager = dispatch.EnabledExtensionManager( - self.namespace, - PluginLoader.is_plugin_enabled, - on_load_failure_callback=PluginLoader._on_load_failure) - self.packages = {} - name_map = {} - for ext in extension_manager.extensions: - self.load_extension(ext, name_map) - self.cleanup_duplicates(name_map) - - def load_extension(self, extension, name_map): - dist_name = str(extension.entry_point.dist) - name = extension.entry_point.name - if not NAME_RE.match(name): - LOG.warning("Entry-point 'name' {name} is invalid".format( - name=name)) - return - name_map.setdefault(name, []).append(dist_name) - if dist_name in self.packages: - package = self.packages[dist_name] - else: - package = PackageDefinition(extension.entry_point.dist) - self.packages[dist_name] = package - - plugin = extension.plugin - try: - package.classes[name] = initialize_plugin(plugin) - except Exception: - LOG.exception("Unable to initialize plugin for {name}".format( - name=name)) - return - LOG.info("Loaded class {class_name} from {dist}".format( - class_name=name, dist=dist_name)) - - def cleanup_duplicates(self, name_map): - for class_name, package_names in name_map.items(): - if len(package_names) >= 2: - LOG.warning("Class is defined in multiple packages!") - for package_name in package_names: - LOG.warning( - "Disabling class {class_name} in {dist} due to " - "conflict".format( - class_name=class_name, dist=package_name)) - self.packages[package_name].classes.pop(class_name) - - @staticmethod - def is_plugin_enabled(extension): - if CONF.murano.enabled_plugins is None: - # assume all plugins are enabled until manually specified otherwise - return True - else: - return (extension.entry_point.dist.project_name in - CONF.murano.enabled_plugins) - - @staticmethod - def _on_load_failure(manager, ep, exc): - LOG.warning("Error loading entry-point {ep} from package {dist}: " - "{err}".format(ep=ep.name, dist=ep.dist, err=exc)) - - def register_in_loader(self, package_loader): - for package in self.packages.values(): - package_loader.register_package( - MuranoPackage(package_loader, package)) - - -def initialize_plugin(plugin): - if hasattr(plugin, "init_plugin"): - initializer = getattr(plugin, "init_plugin") - if inspect.ismethod(initializer) and initializer.__self__ is plugin: - LOG.debug("Initializing plugin class {name}".format(name=plugin)) - initializer() - return plugin - - -class PackageDefinition(object): - def __init__(self, distribution): - self.name = distribution.project_name - self.version = distribution.version - if distribution.has_metadata(distribution.PKG_INFO): - # This has all the package metadata, including Author, - # description, License etc - self.info = distribution.get_metadata(distribution.PKG_INFO) - else: - self.info = None - self.classes = {} - - -class MuranoPackage(murano_package.MuranoPackage): - def __init__(self, pkg_loader, package_definition): - super(MuranoPackage, self).__init__( - pkg_loader, package_definition.name, runtime_version='1.0') - for class_name, clazz in package_definition.classes.items(): - if hasattr(clazz, "_murano_class_name"): - LOG.warning("Class '%(class_name)s' has a MuranoPL " - "name '%(name)s' defined which will be " - "ignored" % - dict(class_name=class_name, - name=getattr(clazz, "_murano_class_name"))) - LOG.debug("Registering '%s' from '%s' in class loader", - class_name, package_definition.name) - self.register_class(clazz, class_name) - - def get_resource(self, name): - raise NotImplementedError() diff --git a/murano/common/plugins/package_types_loader.py b/murano/common/plugins/package_types_loader.py deleted file mode 100644 index 1233266bd..000000000 --- a/murano/common/plugins/package_types_loader.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 semantic_version - -from oslo_config import cfg -from oslo_log import log as logging -from stevedore import dispatch - - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) -NAMESPACE = 'io.murano.plugins.packages' - - -class PluginLoader(object): - def __init__(self): - LOG.info('Loading package type plugins') - extension_manager = dispatch.EnabledExtensionManager( - NAMESPACE, - self._is_plugin_enabled, - on_load_failure_callback=self._on_load_failure) - self.formats = {} - for ext in extension_manager.extensions: - self._load_plugin(ext) - - def _load_plugin(self, extension): - format_name = extension.entry_point.name - self.register_format(format_name, extension.plugin) - - @staticmethod - def _is_plugin_enabled(extension): - if CONF.murano.enabled_plugins is None: - # assume all plugins are enabled until manually specified otherwise - return True - else: - return (extension.entry_point.dist.project_name in - CONF.murano.enabled_plugins) - - @staticmethod - def _on_load_failure(manager, ep, exc): - LOG.warning("Error loading entry-point {ep} from package {dist}: " - "{err}".format(ep=ep.name, dist=ep.dist, err=exc)) - - @staticmethod - def _parse_format_string(format_string): - parts = format_string.rsplit('/', 1) - if len(parts) != 2: - LOG.error("Incorrect format name {name}".format( - name=format_string)) - raise ValueError(format_string) - return ( - parts[0].strip(), - semantic_version.Version.coerce(parts[1]) - ) - - def register_format(self, format_name, package_class): - try: - name, version = self._parse_format_string(format_name) - except ValueError: - return - else: - self._initialize_plugin(package_class) - self.formats.setdefault(name, {})[version] = package_class - LOG.info('Plugin for "{0}" package type was loaded'.format( - format_name)) - - def get_package_handler(self, format_name): - format_name, runtime_version = self._parse_format_string(format_name) - - package_class = self.formats.get(format_name, {}).get( - runtime_version) - if package_class is None: - return None - return lambda *args, **kwargs: package_class( - format_name, runtime_version, *args, **kwargs) - - @staticmethod - def _initialize_plugin(plugin): - if hasattr(plugin, "init_plugin"): - initializer = getattr(plugin, "init_plugin") - LOG.debug("Initializing plugin class {name}".format(name=plugin)) - initializer() diff --git a/murano/common/policies/__init__.py b/murano/common/policies/__init__.py deleted file mode 100644 index 3694bd048..000000000 --- a/murano/common/policies/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 - -from murano.common.policies import action -from murano.common.policies import base -from murano.common.policies import category -from murano.common.policies import deployment -from murano.common.policies import env_template -from murano.common.policies import environment -from murano.common.policies import package - - -def list_rules(): - return itertools.chain( - base.list_rules(), - action.list_rules(), - category.list_rules(), - deployment.list_rules(), - environment.list_rules(), - env_template.list_rules(), - package.list_rules() - ) diff --git a/murano/common/policies/action.py b/murano/common/policies/action.py deleted file mode 100644 index 5262204a2..000000000 --- a/murano/common/policies/action.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -action_policies = [ - policy.DocumentedRuleDefault( - name='execute_action', - check_str=base.RULE_DEFAULT, - description="""Excute an available action on a deployed environment, -retrieve the task status of an executed action, or retrieve the result of -an executed static action.""", - operations=[ - {'path': 'v1/environments/{environment_id}/actions/{action_id}', - 'method': 'POST'}, - {'path': 'v1/environments/{environment_id}/actions/{task_id}', - 'method': 'GET'}, - {'path': 'v1/actions', - 'method': 'POST'}]) -] - - -def list_rules(): - return action_policies diff --git a/murano/common/policies/base.py b/murano/common/policies/base.py deleted file mode 100644 index f5e9ff641..000000000 --- a/murano/common/policies/base.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -RULE_ADMIN_API = 'rule:admin_api' -RULE_DEFAULT = 'rule:default' - - -rules = [ - policy.RuleDefault( - name='context_is_admin', - check_str='role:admin'), - policy.RuleDefault( - name='admin_api', - check_str='is_admin:True'), - policy.RuleDefault( - name='default', - check_str='') -] - - -def list_rules(): - return rules diff --git a/murano/common/policies/category.py b/murano/common/policies/category.py deleted file mode 100644 index 54dc33ec1..000000000 --- a/murano/common/policies/category.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -category_policies = [ - policy.DocumentedRuleDefault( - name='get_category', - check_str=base.RULE_DEFAULT, - description="""Show category details or list all categories in the -application catalog.""", - operations=[{'path': '/v1/catalog/categories/{category_id}', - 'method': 'GET'}, - {'path': '/v1/catalog/categories', 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='delete_category', - check_str=base.RULE_ADMIN_API, - description='Delete a category.', - operations=[{'path': '/v1/catalog/categories/{category_id}', - 'method': 'DELETE'}]), - policy.DocumentedRuleDefault( - name='add_category', - check_str=base.RULE_ADMIN_API, - description='Create a category.', - operations=[{'path': '/v1/catalog/categories', 'method': 'POST'}]) -] - - -def list_rules(): - return category_policies diff --git a/murano/common/policies/deployment.py b/murano/common/policies/deployment.py deleted file mode 100644 index d2ab93f0d..000000000 --- a/murano/common/policies/deployment.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -deployment_policies = [ - policy.DocumentedRuleDefault( - name='list_deployments', - check_str=base.RULE_DEFAULT, - description='List deployments for an environment.', - operations=[{'path': '/v1/environments/{env_id}/deployments', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='list_deployments_all_environments', - check_str=base.RULE_DEFAULT, - description='List deployments for all environments in a project.', - operations=[{'path': '/v1/deployments', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='statuses_deployments', - check_str=base.RULE_DEFAULT, - description='Show deployment status details for a deployment.', - operations=[{ - 'path': '/v1/environments/{env_id}/deployments/{deployment_id}', - 'method': 'GET'}]) -] - - -def list_rules(): - return deployment_policies diff --git a/murano/common/policies/env_template.py b/murano/common/policies/env_template.py deleted file mode 100644 index 9cd2e7f4e..000000000 --- a/murano/common/policies/env_template.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -template_policies = [ - policy.DocumentedRuleDefault( - name='list_env_templates', - check_str=base.RULE_DEFAULT, - description='List environment templates in a project.', - operations=[{'path': '/v1/templates', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='create_env_template', - check_str=base.RULE_DEFAULT, - description='Create an environment template.', - operations=[{'path': '/v1/templates', - 'method': 'POST'}]), - policy.DocumentedRuleDefault( - name='show_env_template', - check_str=base.RULE_DEFAULT, - description='Show environment template details.', - operations=[{'path': '/v1/templates/{env_template_id}', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='update_env_template', - check_str=base.RULE_DEFAULT, - description='Update an environment template.', - operations=[{'path': '/v1/templates/{env_template_id}', - 'method': 'PUT'}]), - policy.DocumentedRuleDefault( - name='delete_env_template', - check_str=base.RULE_DEFAULT, - description='Delete an environment template.', - operations=[{'path': '/v1/templates/{env_template_id}', - 'method': 'DELETE'}]), - policy.DocumentedRuleDefault( - name='clone_env_template', - check_str=base.RULE_DEFAULT, - description='Clone an environment template.', - operations=[{'path': '/v1/templates/{env_template_id}/clone', - 'method': 'POST'}]) -] - - -def list_rules(): - return template_policies diff --git a/murano/common/policies/environment.py b/murano/common/policies/environment.py deleted file mode 100644 index 6752e5b10..000000000 --- a/murano/common/policies/environment.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -environment_policies = [ - policy.DocumentedRuleDefault( - name='list_environments', - check_str=base.RULE_DEFAULT, - description='List environments in a project.', - operations=[{'path': '/v1/environments', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='list_environments_all_tenants', - check_str=base.RULE_ADMIN_API, - description='List environments across all projects.', - operations=[{'path': '/v1/environments?all_tenants=true', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='show_environment', - check_str=base.RULE_DEFAULT, - description='Show details for an environment or shows the environment ' - 'model.', - operations=[{'path': '/v1/environments/{environment_id}', - 'method': 'GET'}, - {'path': '/v1/environments/{environment_id}/model', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='update_environment', - check_str=base.RULE_DEFAULT, - description='Update or rename an environment.', - operations=[{'path': '/v1/environments/{environment_id}', - 'method': 'PUT'}, - {'path': '/v1/environments/{environment_id}/model', - 'method': 'PATCH'}]), - policy.DocumentedRuleDefault( - name='create_environment', - check_str=base.RULE_DEFAULT, - description='Create an environment or create an environment and ' - 'session from an environment template.', - operations=[ - {'path': '/v1/environments/{environment_id}', - 'method': 'POST'}, - {'path': '/v1/templates/{env_template_id}/create-environment', - 'method': 'POST'}]), - policy.DocumentedRuleDefault( - name='delete_environment', - check_str=base.RULE_DEFAULT, - description='Delete an environment.', - operations=[{'path': '/v1/environments/{environment_id}', - 'method': 'DELETE'}]) -] - - -def list_rules(): - return environment_policies diff --git a/murano/common/policies/package.py b/murano/common/policies/package.py deleted file mode 100644 index 9fdeb913c..000000000 --- a/murano/common/policies/package.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright 2017 AT&T Corporation. -# All Rights Reserved. -# -# Licensed 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 oslo_policy import policy - -from murano.common.policies import base - -package_policies = [ - policy.DocumentedRuleDefault( - name='get_package', - check_str=base.RULE_DEFAULT, - description="""Returns either detailed package information or -information specific to the package's UI or logo. In addition, checks for the -existence of a given package.""", - operations=[{'path': '/v1/catalog/packages/{package_id}', - 'method': 'GET'}, - {'path': '/v1/catalog/packages', - 'method': 'GET'}, - {'path': '/v1/catalog/packages/{package_id}/ui', - 'method': 'GET'}, - {'path': '/v1/catalog/packages/{package_id}/logo', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='upload_package', - check_str=base.RULE_DEFAULT, - description='Upload a package to the application catalog.', - operations=[{'path': '/v1/catalog/packages', - 'method': 'POST'}]), - policy.DocumentedRuleDefault( - name='modify_package', - check_str=base.RULE_DEFAULT, - description='Update package information for a given package.', - operations=[{'path': '/v1/catalog/packages/{package_id}', - 'method': 'PATCH'}]), - policy.DocumentedRuleDefault( - name='publicize_package', - check_str=base.RULE_ADMIN_API, - description="""Publicize a package across all projects. Grants users in -any project the ability to use the package. Enforced only when `is_public` -parameter is set to True in the request body of the `update` or `upload` -package request.""", - operations=[{'path': '/v1/catalog/packages/{package_id}', - 'method': 'PATCH'}, - {'path': '/v1/catalog/packages', - 'method': 'POST'}]), - policy.DocumentedRuleDefault( - name='manage_public_package', - check_str=base.RULE_DEFAULT, - description="""Either update, delete or check for the existence of a -public package. Only enforced when the package is public.""", - operations=[{'path': '/v1/catalog/packages/{package_id}', - 'method': 'PATCH'}, - {'path': '/v1/catalog/packages/{package_id}', - 'method': 'DELETE'}, - {'path': '/v1/catalog/packages', - 'method': 'GET'}]), - policy.DocumentedRuleDefault( - name='delete_package', - check_str=base.RULE_DEFAULT, - description='Delete a given package.', - operations=[{'path': '/v1/catalog/packages/{package_id}', - 'method': 'DELETE'}]), - policy.DocumentedRuleDefault( - name='download_package', - check_str=base.RULE_DEFAULT, - description='Download a package from the application catalog.', - operations=[{'path': '/v1/catalog/packages/{package_id}/download', - 'method': 'GET'}]) -] - - -def list_rules(): - return package_policies diff --git a/murano/common/policy.py b/murano/common/policy.py deleted file mode 100644 index f20a890e3..000000000 --- a/murano/common/policy.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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. - -# Based on designate/policy.py - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_policy import opts -from oslo_policy import policy -from webob import exc as exceptions - -from murano.common import policies - -LOG = logging.getLogger(__name__) - -CONF = cfg.CONF - -_ENFORCER = None - -# TODO(gmann): Remove setting the default value of config policy_file -# once oslo_policy change the default value to 'policy.yaml'. -# https://github.com/openstack/oslo.policy/blob/a626ad12fe5a3abd49d70e3e5b95589d279ab578/oslo_policy/opts.py#L49 -DEFAULT_POLICY_FILE = 'policy.yaml' -opts.set_defaults(CONF, DEFAULT_POLICY_FILE) - - -def reset(): - global _ENFORCER - if _ENFORCER: - _ENFORCER.clear() - _ENFORCER = None - - -def init(use_conf=True): - global _ENFORCER - if not _ENFORCER: - LOG.debug("Enforcer is not present, recreating.") - _ENFORCER = policy.Enforcer(CONF, use_conf=use_conf) - register_rules(_ENFORCER) - - -def set_rules(data, default_rule=None, overwrite=True): - default_rule = default_rule or cfg.CONF.policy_default_rule - if not _ENFORCER: - LOG.debug("Enforcer not present, recreating at rules stage.") - init() - - if default_rule: - _ENFORCER.default_rule = default_rule - - LOG.debug("Loading rules {rules}, default: {def_rule}, overwrite: " - "{overwrite}".format(rules=data, def_rule=default_rule, - overwrite=overwrite)) - - if isinstance(data, dict): - rules = policy.Rules.from_dict(data, default_rule) - else: - rules = policy.Rules.load_json(data, default_rule) - - _ENFORCER.set_rules(rules, overwrite=overwrite) - - -def check(rule, ctxt, target=None, do_raise=True, exc=None): - """Verify that the rule is valid on the target in this context. - - :param rule: String representing the action to be checked, which should - be colon-separated for clarity. - :param ctxt: Request context from which user credentials are extracted. - :param target: Dictionary representing the object of the action for object - creation; this should be a dictionary representing the - location of the object, e.g. {'environment_id': - object.environment_id} - :param do_raise: Whether to raise an exception or not if the check fails. - :param exc: Class of the exception to raise if the check fails. - :raises exceptions.HTTPForbidden: If verification fails. Or if 'exc' is - specified it will raise an exception of - that type. - """ - init() - - if target is None: - target = {} - creds = ctxt.to_dict() - if not exc: - exc = exceptions.HTTPForbidden - - try: - result = _ENFORCER.enforce(rule, target, creds, do_raise, exc) - except Exception: - result = False - raise - else: - return result - finally: - if result: - LOG.debug("Policy check succeeded for rule {rule} on target " - "{target}".format(rule=rule, target=repr(target))) - else: - LOG.debug("Policy check failed for rule {rule} on target: " - "{target}".format(rule=rule, target=repr(target))) - - -def check_is_admin(context): - """Check if the given context is associated with an admin role. - - :param context: Murano request context - :returns: A non-False value if context role is admin. - """ - return check('context_is_admin', context, context.to_dict(), - do_raise=False) - - -def register_rules(enforcer): - enforcer.register_defaults(policies.list_rules()) diff --git a/murano/common/rpc.py b/murano/common/rpc.py deleted file mode 100644 index d54bcca17..000000000 --- a/murano/common/rpc.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_config import cfg -import oslo_messaging as messaging -from oslo_messaging.rpc import dispatcher -from oslo_messaging import target - -CONF = cfg.CONF - -NOTIFICATION_TRANSPORT = None -TRANSPORT = None - - -def init(): - global TRANSPORT, NOTIFICATION_TRANSPORT - TRANSPORT = messaging.get_rpc_transport(CONF) - NOTIFICATION_TRANSPORT = messaging.get_notification_transport(CONF) - - -def initialized(): - return None not in [TRANSPORT, NOTIFICATION_TRANSPORT] - - -def cleanup(): - global TRANSPORT, NOTIFICATION_TRANSPORT - if TRANSPORT is not None: - TRANSPORT.cleanup() - if NOTIFICATION_TRANSPORT is not None: - NOTIFICATION_TRANSPORT.cleanup() - TRANSPORT = NOTIFICATION_TRANSPORT = None - - -def get_client(target, timeout=None): - if TRANSPORT is None: - init() - return messaging.RPCClient( - TRANSPORT, - target, - timeout=timeout - ) - - -def get_server(target, endpoints, executor): - if TRANSPORT is None: - init() - access_policy = dispatcher.DefaultRPCAccessPolicy - return messaging.get_rpc_server( - TRANSPORT, - target, - endpoints, - executor=executor, - access_policy=access_policy - ) - - -def get_notification_listener(targets, endpoints, executor): - if NOTIFICATION_TRANSPORT is None: - init() - return messaging.get_notification_listener( - NOTIFICATION_TRANSPORT, - targets, - endpoints, - executor=executor - ) - - -class ApiClient(object): - def __init__(self): - client_target = target.Target('murano', 'results') - self._client = get_client(client_target, timeout=15) - - def process_result(self, result, environment_id): - return self._client.call({}, 'process_result', result=result, - environment_id=environment_id) - - -class EngineClient(object): - def __init__(self): - client_target = target.Target('murano', 'tasks') - self._client = get_client(client_target, timeout=15) - - def handle_task(self, task): - return self._client.cast({}, 'handle_task', task=task) - - def call_static_action(self, task): - return self._client.call({}, 'call_static_action', task=task) - - def generate_schema(self, credentials, class_name, method_names=None, - class_version=None, package_name=None): - return self._client.call( - credentials, 'generate_schema', - class_name=class_name, - method_names=method_names, - class_version=class_version, - package_name=package_name - ) - - -def api(): - if not initialized(): - init() - return ApiClient() - - -def engine(): - if not initialized(): - init() - return EngineClient() diff --git a/murano/common/server.py b/murano/common/server.py deleted file mode 100644 index af156050d..000000000 --- a/murano/common/server.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 uuid - -from oslo_config import cfg -from oslo_log import log as logging -from oslo_messaging import target -from oslo_service import service -from oslo_utils import timeutils -import pytz -from sqlalchemy import desc - -from murano.common.helpers import token_sanitizer -from murano.common import rpc -from murano.db import models -from murano.db.services import environments -from murano.db.services import instances -from murano.db import session -from murano.engine.system import status_reporter -from murano.services import states - -CONF = cfg.CONF - -LOG = logging.getLogger(__name__) - - -class ResultEndpoint(object): - @staticmethod - def process_result(context, result, environment_id): - secure_result = token_sanitizer.TokenSanitizer().sanitize(result) - LOG.debug('Got result from orchestration ' - 'engine:\n{result}'.format(result=secure_result)) - - model = result['model'] - action_result = result.get('action', {}) - - unit = session.get_session() - environment = unit.query(models.Environment).get(environment_id) - - if not environment: - LOG.warning('Environment result could not be handled, ' - 'specified environment not found in database') - return - - if model['Objects'] is None and model.get('ObjectsCopy', {}) is None: - environments.EnvironmentServices.remove(environment_id) - return - - environment.description = model - if environment.description['Objects'] is not None: - environment.description['Objects']['services'] = \ - environment.description['Objects'].pop('applications', []) - action_name = 'Deployment' - deleted = False - else: - action_name = 'Deletion' - deleted = True - environment.version += 1 - environment.save(unit) - - # close deployment - deployment = get_last_deployment(unit, environment.id) - deployment.finished = timeutils.utcnow() - deployment.result = action_result - - num_errors = unit.query(models.Status)\ - .filter_by(level='error', task_id=deployment.id).count() - num_warnings = unit.query(models.Status)\ - .filter_by(level='warning', task_id=deployment.id).count() - - final_status_text = action_name + ' finished' - if num_errors: - final_status_text += " with errors" - - elif num_warnings: - final_status_text += " with warnings" - - status = models.Status() - status.task_id = deployment.id - status.text = final_status_text - status.level = 'info' - deployment.statuses.append(status) - deployment.save(unit) - - # close session - conf_session = unit.query(models.Session).filter_by( - **{'environment_id': environment.id, - 'state': states.SessionState.DEPLOYING if not deleted - else states.SessionState.DELETING}).first() - if num_errors > 0 or result['action'].get('isException'): - conf_session.state = \ - states.SessionState.DELETE_FAILURE if deleted else \ - states.SessionState.DEPLOY_FAILURE - else: - conf_session.state = states.SessionState.DEPLOYED - conf_session.save(unit) - - # output application tracking information - services = [] - objects = model['Objects'] - if objects: - services = objects.get('services') - if num_errors + num_warnings > 0: - LOG.warning('EnvId: {env_id} TenantId: {tenant_id} Status: ' - 'Failed Apps: {services}' - .format(env_id=environment.id, - tenant_id=environment.tenant_id, - services=services)) - else: - LOG.info('EnvId: {env_id} TenantId: {tenant_id} Status: ' - 'Successful Apps: {services}' - .format(env_id=environment.id, - tenant_id=environment.tenant_id, - services=services)) - if action_name == 'Deployment': - env = environment.to_dict() - env["deployment_started"] = deployment.started - env["deployment_finished"] = deployment.finished - status_reporter.get_notifier().report( - 'environment.deploy.end', env) - - -def notification_endpoint_wrapper(priority='info'): - def wrapper(func): - class NotificationEndpoint(object): - def __init__(self): - setattr(self, priority, self._handler) - - def _handler(self, ctxt, publisher_id, event_type, - payload, metadata): - if event_type == ('murano.%s' % func.__name__): - func(payload) - - def __call__(self, payload): - return func(payload) - return NotificationEndpoint() - return wrapper - - -@notification_endpoint_wrapper() -def track_instance(payload): - LOG.debug('Got track instance request from orchestration ' - 'engine:\n{payload}'.format(payload=payload)) - instance_id = payload['instance'] - instance_type = payload.get('instance_type', 0) - environment_id = payload['environment'] - unit_count = payload.get('unit_count') - type_name = payload['type_name'] - type_title = payload.get('type_title') - - instances.InstanceStatsServices.track_instance( - instance_id, environment_id, instance_type, - type_name, type_title, unit_count) - - -@notification_endpoint_wrapper() -def untrack_instance(payload): - LOG.debug('Got untrack instance request from orchestration ' - 'engine:\n{payload}'.format(payload=payload)) - instance_id = payload['instance'] - environment_id = payload['environment'] - instances.InstanceStatsServices.destroy_instance( - instance_id, environment_id) - - -@notification_endpoint_wrapper() -def report_notification(report): - LOG.debug('Got report from orchestration ' - 'engine:\n{report}'.format(report=report)) - - report['entity_id'] = report.pop('id') - - status = models.Status() - if 'timestamp' in report: - dt = timeutils.parse_isotime(report.pop('timestamp')) - report['created'] = dt.astimezone(pytz.utc).replace(tzinfo=None) - status.update(report) - - unit = session.get_session() - # connect with deployment - with unit.begin(): - running_deployment = get_last_deployment(unit, - status.environment_id) - status.task_id = running_deployment.id - unit.add(status) - - -def get_last_deployment(unit, env_id): - query = unit.query(models.Task) \ - .filter_by(environment_id=env_id) \ - .order_by(desc(models.Task.started)) - return query.first() - - -class Service(service.Service): - """Service class, that contains common methods for custom services""" - - def __init__(self): - super(Service, self).__init__() - self.server = None - - def stop(self, graceful=False): - if self.server: - self.server.stop() - if graceful: - self.server.wait() - super(Service, self).stop() - - def reset(self): - if self.server: - self.server.reset() - super(Service, self).reset() - - -def get_notification_listener(): - endpoints = [report_notification, track_instance, untrack_instance] - s_target = target.Target(topic='murano', server=str(uuid.uuid4())) - listener = rpc.get_notification_listener( - [s_target], endpoints, executor='threading' - ) - - return listener - - -def get_rpc_server(): - endpoints = [ResultEndpoint()] - s_target = target.Target('murano', 'results', server=str(uuid.uuid4())) - server = rpc.get_server( - s_target, endpoints, executor='threading' - ) - - return server - - -class NotificationService(Service): - def __init__(self): - super(NotificationService, self).__init__() - self.server = None - - def start(self): - endpoints = [report_notification, track_instance, untrack_instance] - s_target = target.Target(topic='murano', server=str(uuid.uuid4())) - self.server = rpc.get_notification_listener( - [s_target], endpoints, executor='eventlet' - ) - self.server.start() - super(NotificationService, self).start() - - -class ApiService(Service): - - def start(self): - endpoints = [ResultEndpoint()] - s_target = target.Target('murano', 'results', server=str(uuid.uuid4())) - self.server = rpc.get_server( - s_target, endpoints, executor='eventlet' - ) - self.server.start() - super(ApiService, self).start() diff --git a/murano/common/statservice.py b/murano/common/statservice.py deleted file mode 100644 index e958fa4cb..000000000 --- a/murano/common/statservice.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 json -import multiprocessing -import socket -import time - -import eventlet -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import service -import psutil -from sqlalchemy import desc - -from murano.api import v1 -from murano.api.v1.deployments import set_dep_state -from murano.api.v1 import request_statistics -from murano.db import models -from murano.db.services import environments as envs -from murano.db.services import stats as db_stats -from murano.db import session as db_session -from murano.engine.system import status_reporter - -CONF = cfg.CONF - -CONF_STATS = CONF.stats -LOG = logging.getLogger(__name__) - - -class StatsCollectingService(service.Service): - def __init__(self): - super(StatsCollectingService, self).__init__() - request_statistics.init_stats() - self._hostname = socket.gethostname() - self._stats_db = db_stats.Statistics() - self._prev_time = time.time() - self._notifier = status_reporter.Notification() - - def start(self): - super(StatsCollectingService, self).start() - self.tg.add_thread(self._collect_stats_loop) - self.tg.add_thread(self._report_env_stats_loop) - - def stop(self): - super(StatsCollectingService, self).stop() - - def _collect_stats_loop(self): - period = CONF_STATS.period * 60 - while True: - self.update_stats() - eventlet.sleep(period) - - def _report_env_stats_loop(self): - env_audit_period = CONF_STATS.env_audit_period * 60 - while True: - self.report_env_stats() - eventlet.sleep(env_audit_period) - - def update_stats(self): - LOG.debug("Updating statistic information.") - LOG.debug("Stats object: {stats}".format(stats=v1.stats)) - LOG.debug("Stats: (Requests: {amount} Errors: {error} " - "Ave.Res.Time {time:2.4f}\n Per tenant: {req_count})".format( - amount=v1.stats.request_count, - error=v1.stats.error_count, - time=v1.stats.average_time, - req_count=v1.stats.requests_per_tenant)) - try: - stats = self._stats_db.get_stats_by_host(self._hostname) - if stats is None: - self._stats_db.create(self._hostname, - v1.stats.request_count, - v1.stats.error_count, - v1.stats.average_time, - v1.stats.requests_per_tenant, - multiprocessing.cpu_count(), - psutil.cpu_percent()) - return - - now = time.time() - t_delta = now - self._prev_time - requests_per_second = (v1.stats.request_count - - stats.request_count) / t_delta - errors_per_second = (v1.stats.error_count - - stats.error_count) / t_delta - self._prev_time = now - stats.request_count = v1.stats.request_count - stats.error_count = v1.stats.error_count - stats.average_response_time = v1.stats.average_time - stats.requests_per_tenant = json.dumps(v1.stats. - requests_per_tenant) - stats.requests_per_second = requests_per_second - stats.errors_per_second = errors_per_second - stats.cpu_percent = psutil.cpu_percent() - self._stats_db.update(self._hostname, stats) - except Exception as e: - LOG.exception("Failed to get statistics object from a " - "database. {error_code}".format(error_code=e)) - - def report_env_stats(self): - LOG.debug("Reporting env stats") - try: - environments = envs.EnvironmentServices.get_environments_by({}) - - for env in environments: - deployments = get_env_deployments(env.id) - success_deployments = [d for d in deployments - if d['state'] == "success"] - if success_deployments: - self._notifier.report('environment.exists', env.to_dict()) - except Exception: - LOG.exception("Failed to report existing envs") - - -def get_env_deployments(environment_id): - unit = db_session.get_session() - query = unit.query(models.Task).filter_by( - environment_id=environment_id).order_by(desc(models.Task.created)) - result = query.all() - deployments = [ - set_dep_state(deployment, unit).to_dict() for deployment in result] - return deployments diff --git a/murano/common/utils.py b/murano/common/utils.py deleted file mode 100644 index 2f4ea599c..000000000 --- a/murano/common/utils.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 collections -import functools as func -import re - -import jsonschema -from oslo_log import log as logging - -from murano.common.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class TraverseHelper(object): - value_type = (str, int, float, bool) - - @staticmethod - def get(path, source): - """Provides the ability to traverse a data source - - Provides the ability to traverse a data source made up of any - combination of lists and dicts. Has simple rules for selecting item of - the list: - - * each item should have id property - * to select item from the list, specify id value - - Examples: - source = {'obj': {'attr': True}} - value = TraverseHelper.get('/obj/attr', source) - - source = {'obj': [ - {'id': '1', 'value': 1}, - {'id': '2s', 'value': 2}, - ]} - value = TraverseHelper.get('/obj/2s/value', source) - - - :param path: string with path to desired value - :param source: python object (list or dict) - :return: object - :raise: ValueError if object is malformed - """ - queue = collections.deque(filter(lambda x: x, path.split('/'))) - - while len(queue): - path = queue.popleft() - - if isinstance(source, list): - idx_source = source - iterator = ( - i for i in source - if i.get('?', {}).get('id') == path - ) - source = next(iterator, None) - if source is None and path.isdigit(): - source = idx_source[int(path)] - elif isinstance(source, dict): - source = source[path] - elif isinstance(source, TraverseHelper.value_type): - break - else: - raise ValueError(_('Source object or path is malformed')) - - return source - - @staticmethod - def update(path, value, source): - """Updates value selected with specified path. - - Warning: Root object could not be updated - - :param path: string with path to desired value - :param value: value - :param source: python object (list or dict) - """ - parent_path = '/'.join(path.split('/')[:-1]) - node = TraverseHelper.get(parent_path, source) - key = path[1:].split('/')[-1] - if is_number(key): - node[int(key)] = value - else: - node[key] = value - - @staticmethod - def insert(path, value, source): - """Inserts new item to selected list. - - :param path: string with path to desired value - :param value: value - :param source: List - """ - node = TraverseHelper.get(path, source) - node.append(value) - - @staticmethod - def extend(path, value, source): - """Extend list by appending elements from the iterable. - - :param path: string with path to desired value - :param value: value - :param source: List - """ - node = TraverseHelper.get(path, source) - node.extend(value) - - @staticmethod - def remove(path, source): - """Removes selected item from source. - - :param path: string with path to desired value - :param source: python object (list or dict) - """ - parent_path = '/'.join(path.split('/')[:-1]) - node = TraverseHelper.get(parent_path, source) - key = path[1:].split('/')[-1] - - if isinstance(node, list): - iterator = (i for i in node if i.get('?', {}).get('id') == key) - item = next(iterator, None) - if item is None and key.isdigit(): - del node[int(key)] - else: - node.remove(item) - elif isinstance(node, dict): - del node[key] - else: - raise ValueError(_('Source object or path is malformed')) - - -def is_number(var): - try: - int(var) - return True - except Exception: - return False - - -def is_different(obj1, obj2): - """Stripped-down version of deep.diff comparator - - Compares arbitrary nested objects, handles circular links, but doesn't - point to the first difference as deep.diff does. - """ - class Difference(Exception): - pass - - def is_in(o, st): - for _o in st: - if o is _o: - return True - return False - - def rec(o1, o2, stack1=(), stack2=()): - if is_in(o1, stack1) and is_in(o2, stack2): - # circular reference detected - break the loop - return - elif is_in(o1, stack1): - raise Difference() - else: - stack1 += (o1,) - stack2 += (o2,) - - if o1 is o2: - return - elif (isinstance(o1, str) and - isinstance(o2, str)) and o1 == o2: - return - elif type(o1) != type(o2): - raise Difference() - elif isinstance(o1, dict): - # check for keys inequality - rec(o1.keys(), o2.keys(), stack1, stack2) - for key in o1.keys(): - rec(o1[key], o2[key], stack1, stack2) - elif isinstance(o1, (list, tuple, set)): - if len(o1) != len(o2): - raise Difference() - else: - for _o1, _o2 in zip(o1, o2): - rec(_o1, _o2, stack1, stack2) - elif hasattr(o1, '__dict__'): - return rec(o1.__dict__, o2.__dict__, stack1, stack2) - elif o1 != o2: - raise Difference() - - try: - rec(obj1, obj2) - except Difference: - return True - else: - return False - - -def build_entity_map(value): - def build_entity_map_recursive(value, id_map): - if isinstance(value, dict): - if '?' in value and 'id' in value['?']: - id_map[value['?']['id']] = value - for v in value.values(): - build_entity_map_recursive(v, id_map) - if isinstance(value, list): - for item in value: - build_entity_map_recursive(item, id_map) - - id_map = {} - build_entity_map_recursive(value, id_map) - return id_map - - -def handle(f): - """Handles exception in wrapped function and writes to LOG.""" - - @func.wraps(f) - def f_handle(*args, **kwargs): - try: - return f(*args, **kwargs) - except Exception as e: - LOG.exception(e) - - return f_handle - - -def validate_body(schema): - def deco_validate_body(f): - @func.wraps(f) - def f_validate_body(*args, **kwargs): - if 'body' in kwargs: - jsonschema.validate(kwargs['body'], schema) - return f(*args, **kwargs) - return f_validate_body - return deco_validate_body - - -def validate_quotes(value): - """Validate filter values - - Validation opening/closing quotes in the expression. - """ - open_quotes = True - count_backslash_in_row = 0 - for i in range(len(value)): - if value[i] == '"': - if count_backslash_in_row % 2: - continue - if open_quotes: - if i and value[i - 1] != ',': - msg = _("Invalid filter value %s. There is no comma " - "before opening quotation mark.") % value - raise ValueError(msg) - else: - if i + 1 != len(value) and value[i + 1] != ",": - msg = _("Invalid filter value %s. There is no comma " - "after opening quotation mark.") % value - raise ValueError(msg) - open_quotes = not open_quotes - elif value[i] == '\\': - count_backslash_in_row += 1 - else: - count_backslash_in_row = 0 - if not open_quotes: - msg = _("Invalid filter value %s. The quote is not closed.") % value - raise ValueError(msg) - return True - - -def split_for_quotes(value): - """Split filter values - - Split values by commas and quotes for 'in' operator, according api-wg. - """ - validate_quotes(value) - tmp = re.compile(r''' - "( # if found a double-quote - [^\"\\]* # take characters either non-quotes or backslashes - (?:\\. # take backslashes and character after it - [^\"\\]*)* # take characters either non-quotes or backslashes - ) # before double-quote - ",? # a double-quote with comma maybe - | ([^,]+),? # if not found double-quote take any non-comma - # characters with comma maybe - | , # if we have only comma take empty string - ''', re.VERBOSE) - val_split = [val[0] or val[1] for val in re.findall(tmp, value)] - replaced_inner_quotes = [s.replace(r'\"', '"') for s in val_split] - return replaced_inner_quotes - - -def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None diff --git a/murano/common/uuidutils.py b/murano/common/uuidutils.py deleted file mode 100644 index edeee151f..000000000 --- a/murano/common/uuidutils.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_utils import uuidutils - - -def generate_uuid(): - return uuidutils.generate_uuid(dashed=False) diff --git a/murano/common/wsgi.py b/murano/common/wsgi.py deleted file mode 100644 index 10ffb359f..000000000 --- a/murano/common/wsgi.py +++ /dev/null @@ -1,1121 +0,0 @@ -# Copyright 2011 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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. - -"""Utility methods for working with WSGI servers.""" - -import datetime -import errno -import re -import socket -import sys -import time -from xml.dom import minidom -from xml.parsers import expat - -import eventlet -import eventlet.wsgi -import jsonschema -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import jsonutils -from oslo_service import service -from oslo_service import sslutils -import routes -import routes.middleware -import webob.dec -import webob.exc - -from murano.api.v1 import validation_schemas -from murano.common import config -from murano.common import exceptions -from murano.common.i18n import _ -from murano.common import xmlutils - -eventlet.patcher.monkey_patch(all=False, socket=True) - -wsgi_opts = [ - cfg.IntOpt('backlog', - default=4096, - help="Number of backlog requests to configure the socket with"), - cfg.IntOpt('tcp_keepidle', - default=600, - help="Sets the value of TCP_KEEPIDLE in seconds for each " - "server socket. Not supported on OS X."), - cfg.IntOpt('max_header_line', - default=16384, - help="Maximum line size of message headers to be accepted. " - "max_header_line may need to be increased when using " - "large tokens (typically those generated by the " - "Keystone v3 API with big service catalogs)."), -] - -CONF = cfg.CONF -CONF.register_opts(wsgi_opts) - -LOG = logging.getLogger(__name__) - - -class Service(service.Service): - """Provides a Service API for wsgi servers. - - This gives us the ability to launch wsgi servers with the - Launcher classes in oslo_service.service.py. - """ - - def __init__(self, application, port, - host='0.0.0.0', backlog=4096, threads=1000): - self.application = application - self._port = port - self._host = host - self._backlog = backlog if backlog else CONF.backlog - self._logger = logging.getLogger('eventlet.wsgi') - self.greenthread = None - config.set_middleware_defaults() - super(Service, self).__init__(threads) - - def _get_socket(self, host, port, backlog): - # TODO(dims): eventlet's green dns/socket module does not actually - # support IPv6 in getaddrinfo(). We need to get around this in the - # future or monitor upstream for a fix - info = socket.getaddrinfo(host, - port, - socket.AF_UNSPEC, - socket.SOCK_STREAM)[0] - family = info[0] - bind_addr = info[-1] - - sock = None - retry_until = time.time() + 30 - while not sock and time.time() < retry_until: - try: - sock = eventlet.listen(bind_addr, - backlog=backlog, - family=family) - if sslutils.is_enabled(CONF): - sock = sslutils.wrap(CONF, sock) - - except socket.error as err: - if err.args[0] != errno.EADDRINUSE: - raise - eventlet.sleep(0.1) - if not sock: - raise RuntimeError(_("Could not bind to %(host)s:%(port)s " - "after trying for 30 seconds: Address" - " already in use.") % - {'host': host, 'port': port}) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - # sockets can hang around forever without keepalive - sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - - # This option isn't available in the OS X version of eventlet - if hasattr(socket, 'TCP_KEEPIDLE'): - sock.setsockopt(socket.IPPROTO_TCP, - socket.TCP_KEEPIDLE, - CONF.tcp_keepidle) - - return sock - - def start(self): - """Start serving this service using the provided server instance. - - :returns: None - - """ - super(Service, self).start() - self._socket = self._get_socket(self._host, self._port, self._backlog) - self.greenthread = eventlet.spawn( - self._run, self.application, self._socket) - - @property - def backlog(self): - return self._backlog - - @property - def host(self): - return self._socket.getsockname()[0] if self._socket else self._host - - @property - def port(self): - return self._socket.getsockname()[1] if self._socket else self._port - - def stop(self): - """Stop serving this API. - - :returns: None - - """ - super(Service, self).stop() - if self.greenthread is not None: - self.greenthread.kill() - - def reset(self): - super(Service, self).reset() - logging.setup(cfg.CONF, 'murano') - - def _run(self, application, socket): - """Start a WSGI server in a new green thread.""" - eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line - eventlet.wsgi.server(socket, - application, - custom_pool=self.tg.pool, - log=self._logger) - - -class Middleware(object): - """Base WSGI middleware wrapper. - - These classes require an application to be - initialized that will be called next. By default the middleware will - simply call its wrapped app, or you can override __call__ to customize its - behavior. - """ - - def __init__(self, application): - self.application = application - - def process_request(self, req): - """Called on each request. - - If this returns None, the next application down the stack will be - executed. If it returns a response then that response will be returned - and execution will stop here. - """ - return None - - def process_response(self, response): - """Do whatever you'd like to the response.""" - return response - - @webob.dec.wsgify - def __call__(self, req): - response = self.process_request(req) - if response: - return response - response = req.get_response(self.application) - return self.process_response(response) - - -class Debug(Middleware): - """Helper class to get info about request and response - - Helper class that can be inserted into any WSGI application chain - to get information about the request and response. - """ - - @webob.dec.wsgify - def __call__(self, req): - print(("*" * 40) + " REQUEST ENVIRON") - for key, value in req.environ.items(): - print(key, "=", value) - print("") - resp = req.get_response(self.application) - - print(("*" * 40) + " RESPONSE HEADERS") - for (key, value) in resp.headers.items(): - print(key, "=", value) - print("") - - resp.app_iter = self.print_generator(resp.app_iter) - - return resp - - @staticmethod - def print_generator(app_iter): - """Prints the contents of a wrapper string iterator - - Iterator that prints the contents of a wrapper string iterator - when iterated. - """ - print(("*" * 40) + " BODY") - for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() - yield part - print("") - - -class Router(object): - """WSGI middleware that maps incoming requests to WSGI apps.""" - - def __init__(self, mapper): - """Create a router for the given routes.Mapper. - - Each route in `mapper` must specify a 'controller', which is a - WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be a wsgi.Controller, who will route - the request to the action method. - - Examples: - mapper = routes.Mapper() - sc = ServerController() - - # Explicit mapping of one route to a controller+action - mapper.connect(None, "/svrlist", controller=sc, action="list") - - # Actions are all implicitly defined - mapper.resource("server", "servers", controller=sc) - - # Pointing to an arbitrary WSGI app. You can specify the - # {path_info:.*} parameter so the target app can be handed just that - # section of the URL. - mapper.connect(None, "/v1.0/{path_info:.*}", controller=BlogApp()) - """ - self.map = mapper - self._router = routes.middleware.RoutesMiddleware(self._dispatch, - self.map) - - @webob.dec.wsgify - def __call__(self, req): - """Route the incoming request to a controller - - Route the incoming request to a controller based on self.map. - If no match, return a 404. - """ - return self._router - - @staticmethod - @webob.dec.wsgify - def _dispatch(req): - """Looks for routed WSGI app's response - - Called by self._router after matching the incoming request to - a route and putting the information into req.environ. - Either returns 404 or the routed WSGI app's response. - """ - match = req.environ['wsgiorg.routing_args'][1] - if not match: - return webob.exc.HTTPNotFound() - app = match['controller'] - return app - - -class Request(webob.Request): - """Add some OpenStack API-specific logic to the base webob.Request.""" - - default_request_content_types = ('application/json', - 'application/xml', - 'application/murano-packages-json-patch', - 'application/env-model-json-patch', - 'multipart/form-data') - default_accept_types = ('application/json', - 'application/xml', - 'application/octet-stream') - - def best_match_content_type(self, action, supported_content_types=None, - specific_content_types=None): - """Determine the requested response content-type. - - Based on the query extension then the Accept header. - Raise NotAcceptableContentType exception if we don't find a preference - - """ - supported_content_types = (supported_content_types or - self.default_accept_types) - - parts = self.path.rsplit('.', 1) - if len(parts) > 1: - ctype = 'application/{0}'.format(parts[1]) - if ctype in supported_content_types: - return ctype - - if specific_content_types and action in specific_content_types: - bm = self.accept.best_match(specific_content_types[action]) - else: - bm = self.accept.best_match(supported_content_types) - - if not bm: - raise exceptions.NotAcceptableContentType(content_type=self.accept) - return bm - - def get_content_type(self, allowed_content_types=None): - """Determine content type of the request body. - - Does not do any body introspection, only checks header - - """ - if "Content-Type" not in self.headers: - return None - - content_type = self.content_type - allowed_content_types = (allowed_content_types or - self.default_request_content_types) - - if content_type not in allowed_content_types: - raise exceptions.UnsupportedContentType(content_type=content_type) - return content_type - - -class ResourceExceptionHandler(object): - """Context manager to handle Resource exceptions. - - Used when processing exceptions generated by API implementation - methods. Converts most exceptions to webob exceptions, with the - appropriate logging. - """ - - def __enter__(self): - return None - - def __exit__(self, ex_type, ex_value, ex_traceback): - if not ex_value: - return True - - # TODO(lin.a.yang): current only handle TypeError here, we should - # process other kind of internal exceptions generated by API and - # convert to webob exceptions. - if isinstance(ex_value, TypeError): - exc_info = (ex_type, ex_value, ex_traceback) - LOG.error("Exception handling resource: {0}".format(ex_value), - exc_info=exc_info) - raise webob.exc.HTTPBadRequest() - - # We didn't handle this kind of exception - return False - - -class Resource(object): - """WSGI app that handles (de)serialization and controller dispatch. - - Reads routing information supplied by RoutesMiddleware and calls - the requested action method upon its deserializer, controller, - and serializer. Those three objects may implement any of the basic - controller action methods (create, update, show, index, delete) - along with any that may be specified in the api router. A 'default' - method may also be implemented to be used in place of any - non-implemented actions. Deserializer methods must accept a request - argument and return a dictionary. Controller methods must accept a - request argument. Additionally, they must also accept keyword - arguments that represent the keys returned by the Deserializer. They - may raise a webob.exc exception or return a dict, which will be - serialized by requested content type. - """ - - def __init__(self, controller, deserializer=None, serializer=None): - """Resource init. - - :param controller: object that implement methods created by routes lib - :param deserializer: object that supports webob request deserialization - through controller-like actions - :param serializer: object that supports webob response serialization - through controller-like actions - """ - self.controller = controller - self.serializer = serializer or ResponseSerializer() - self.deserializer = deserializer or RequestDeserializer() - - @webob.dec.wsgify(RequestClass=Request) - def __call__(self, request): - """WSGI method that controls (de)serialization and method dispatch.""" - - LOG.debug("{method} {url}\nHEADERS: {headers}".format( - method=request.method, - url=request.url, - headers=self._format_request_headers(request.headers))) - - try: - action, action_args, accept = self.deserialize_request(request) - except exceptions.UnsupportedContentType: - msg = _("Unsupported Content-Type") - return webob.exc.HTTPUnsupportedMediaType(detail=msg) - except exceptions.NotAcceptableContentType: - msg = _("Acceptable response can not be provided") - return webob.exc.HTTPNotAcceptable(detail=msg) - except exceptions.MalformedRequestBody: - msg = _("Malformed request body") - return webob.exc.HTTPBadRequest(explanation=msg) - - with ResourceExceptionHandler(): - action_result = self.execute_action(action, request, **action_args) - - try: - return self.serialize_response(action, action_result, accept) - # return unserializable result (typically a webob exc) - except Exception: - return action_result - - def deserialize_request(self, request): - return self.deserializer.deserialize(request) - - def serialize_response(self, action, action_result, accept): - return self.serializer.serialize(action_result, accept, action) - - def execute_action(self, action, request, **action_args): - return self.dispatch(self.controller, action, request, **action_args) - - def dispatch(self, obj, action, *args, **kwargs): - """Find action-specific method on self and call it.""" - try: - method = getattr(obj, action) - except AttributeError: - method = getattr(obj, 'default') - return method(*args, **kwargs) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - def _format_request_headers(self, headers): - """Format the request headers to be logged. - - To keep log more clear, only show the X-* header include murano own - header and several useful headers added by keystone auth middleware, - and skip other X-* headers. - """ - string_parts = [] - - # Only show following X-* header - useful_headers = ("X-Configuration-Session", - "X-Roles", - "X-User-Id", - "X-Tenant-Id") - - for header, value in headers.items(): - if header.startswith("X-") and header not in useful_headers: - continue - string_parts.append("{0}: {1}".format(header, value)) - - return ', '.join(string_parts) - - -class ActionDispatcher(object): - """Maps method name to local methods through action name.""" - - def dispatch(self, *args, **kwargs): - """Find and call local method.""" - action = kwargs.pop('action', 'default') - action_method = getattr(self, str(action), self.default) - return action_method(*args, **kwargs) - - def default(self, data): - raise NotImplementedError() - - -class DictSerializer(ActionDispatcher): - """Default request body serialization.""" - - def serialize(self, data, action='default'): - return self.dispatch(data, action=action) - - def default(self, data): - return "" - - -class JSONDictSerializer(DictSerializer): - """Default JSON request body serialization.""" - - def default(self, data, result=None): - def sanitizer(obj): - if isinstance(obj, datetime.datetime): - _dtime = obj - datetime.timedelta(microseconds=obj.microsecond) - return _dtime.isoformat() - return str(obj) - if result: - data.body = jsonutils.dump_as_bytes(result) - return jsonutils.dump_as_bytes(data, default=sanitizer) - - -class XMLDictSerializer(DictSerializer): - def __init__(self, metadata=None, xmlns=None): - """Default XML request body serialization. - - :param metadata: information needed to deserialize xml into - a dictionary. - :param xmlns: XML namespace to include with serialized xml - """ - super(XMLDictSerializer, self).__init__() - self.metadata = metadata or {} - self.xmlns = xmlns - - def default(self, data, result=None): - # We expect data to contain a single key which is the XML root. - root_key = list(data.keys())[0] - doc = minidom.Document() - node = self._to_xml_node(doc, self.metadata, root_key, data[root_key]) - - return self.to_xml_string(node) - - def to_xml_string(self, node, has_atom=False): - self._add_xmlns(node, has_atom) - return node.toprettyxml(indent=' ', encoding='UTF-8') - - # NOTE (ameade): the has_atom should be removed after all the - # xml serializers and view builders have been updated to the current - # spec that required all responses include the xmlns:atom, the has_atom - # flag is to prevent current tests from breaking - def _add_xmlns(self, node, has_atom=False): - if self.xmlns is not None: - node.setAttribute('xmlns', self.xmlns) - if has_atom: - node.setAttribute('xmlns:atom', "http://www.w3.org/2005/Atom") - - def _to_xml_node(self, doc, metadata, nodename, data): - """Recursive method to convert data members to XML nodes.""" - result = doc.createElement(nodename) - - # Set the xml namespace if one is specified - # TODO(justinsb): We could also use prefixes on the keys - xmlns = metadata.get('xmlns', None) - if xmlns: - result.setAttribute('xmlns', xmlns) - - # TODO(bcwaldon): accomplish this without a type-check - if type(data) is list: - collections = metadata.get('list_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for item in data: - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(item)) - result.appendChild(node) - return result - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - node = self._to_xml_node(doc, metadata, singular, item) - result.appendChild(node) - # TODO(bcwaldon): accomplish this without a type-check - elif type(data) is dict: - collections = metadata.get('dict_collections', {}) - if nodename in collections: - metadata = collections[nodename] - for k, v in data.items(): - node = doc.createElement(metadata['item_name']) - node.setAttribute(metadata['item_key'], str(k)) - text = doc.createTextNode(str(v)) - node.appendChild(text) - result.appendChild(node) - return result - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in data.items(): - if k in attrs: - result.setAttribute(k, str(v)) - else: - node = self._to_xml_node(doc, metadata, k, v) - result.appendChild(node) - else: - # Type is atom - node = doc.createTextNode(str(data)) - result.appendChild(node) - return result - - def _create_link_nodes(self, xml_doc, links): - link_nodes = [] - for link in links: - link_node = xml_doc.createElement('atom:link') - link_node.setAttribute('rel', link['rel']) - link_node.setAttribute('href', link['href']) - if 'type' in link: - link_node.setAttribute('type', link['type']) - link_nodes.append(link_node) - return link_nodes - - -class BlankSerializer(DictSerializer): - """Return raw data.""" - def default(self, data): - return data - - -class ResponseHeadersSerializer(ActionDispatcher): - """Default response headers serialization.""" - - def serialize(self, response, data, action): - self.dispatch(response, data, action=action) - - def default(self, response, data): - response.status_int = 200 - - -class ResponseSerializer(object): - """Encode the necessary pieces into a response object.""" - - def __init__(self, body_serializers=None, headers_serializer=None): - self.body_serializers = { - 'application/xml': XMLDictSerializer(), - 'application/json': JSONDictSerializer(), - 'text/plain': BlankSerializer(), - 'application/octet-stream': BlankSerializer() - } - self.body_serializers.update(body_serializers or {}) - - self.headers_serializer = (headers_serializer or - ResponseHeadersSerializer()) - - def serialize(self, response_data, content_type, action='default'): - """Serialize a dict into a string and wrap in a wsgi.Request object. - - :param response_data: dict produced by the Controller - :param content_type: expected mimetype of serialized response body - - """ - response = webob.Response() - self.serialize_headers(response, response_data, action) - self.serialize_body(response, response_data, content_type, action) - return response - - def serialize_headers(self, response, data, action): - self.headers_serializer.serialize(response, data, action) - - def serialize_body(self, response, data, content_type, action): - response.headers['Content-Type'] = content_type - if data is not None: - serializer = self.get_body_serializer(content_type) - response.body = serializer.serialize(data, action) - - def get_body_serializer(self, content_type): - try: - return self.body_serializers[content_type] - except (KeyError, TypeError): - raise exceptions.UnsupportedContentType(content_type=content_type) - - -class ServiceBrokerResponseSerializer(ResponseSerializer): - def __init__(self): - super(ServiceBrokerResponseSerializer, self).__init__() - - def serialize(self, response_data, content_type, action='default'): - if isinstance(response_data, webob.Response): - response = response_data - self.serialize_body(response, response.data, content_type, action) - else: - response = super(ServiceBrokerResponseSerializer, self).serialize( - response_data, content_type, action='default') - return response - - -class RequestHeadersDeserializer(ActionDispatcher): - """Default request headers deserializer.""" - - def deserialize(self, request, action): - return self.dispatch(request, action=action) - - def default(self, request): - return {} - - -class RequestDeserializer(object): - """Break up a Request object into more useful pieces.""" - - def __init__(self, body_deserializers=None, headers_deserializer=None, - supported_content_types=None, specific_content_types=None): - - self.supported_content_types = supported_content_types - self.specific_content_types = specific_content_types - - self.body_deserializers = { - 'application/xml': XMLDeserializer(), - 'application/json': JSONDeserializer(), - 'application/murano-packages-json-patch': - MuranoPackageJSONPatchDeserializer(), - 'application/env-model-json-patch': - EnvModelJSONPatchDeserializer(), - 'multipart/form-data': FormDataDeserializer() - } - self.body_deserializers.update(body_deserializers or {}) - - self.headers_deserializer = (headers_deserializer or - RequestHeadersDeserializer()) - - def deserialize(self, request): - """Extract necessary pieces of the request. - - :param request: Request object - :returns: tuple of (expected controller action name, dictionary of - keyword arguments to pass to the controller, the expected - content type of the response) - - """ - action_args = self.get_action_args(request.environ) - action = action_args.pop('action', None) - - action_args.update(self.deserialize_headers(request, action)) - action_args.update(self.deserialize_body(request, action)) - - accept = self.get_expected_content_type(request, action) - - return (action, action_args, accept) - - def deserialize_headers(self, request, action): - return self.headers_deserializer.deserialize(request, action) - - def deserialize_body(self, request, action): - if not len(request.body) > 0: - LOG.debug("Empty body provided in request") - return {} - - try: - content_type = request.get_content_type() - except exceptions.UnsupportedContentType as e: - LOG.error("Unrecognized Content-Type provided in request: " - "{error}".format(error=str(e))) - raise - - if content_type is None: - LOG.debug("No Content-Type provided in request") - return {} - - try: - deserializer = self.get_body_deserializer(content_type) - except exceptions.UnsupportedContentType: - LOG.debug("Unable to deserialize body as provided Content-Type") - raise - - return deserializer.deserialize(request, action) - - def get_body_deserializer(self, content_type): - try: - return self.body_deserializers[content_type] - except (KeyError, TypeError): - raise exceptions.UnsupportedContentType(content_type=content_type) - - def get_expected_content_type(self, request, action): - return request.best_match_content_type(action, - self.supported_content_types, - self.specific_content_types) - - def get_action_args(self, request_environment): - """Parse dictionary created by routes library.""" - try: - args = request_environment['wsgiorg.routing_args'][1].copy() - except Exception: - return {} - - try: - del args['controller'] - except KeyError: - pass - - try: - del args['format'] - except KeyError: - pass - - return args - - -class TextDeserializer(ActionDispatcher): - """Default request body deserialization.""" - - def deserialize(self, request, action='default'): - return self.dispatch(request, action=action) - - def default(self, request): - return {} - - -class JSONDeserializer(TextDeserializer): - def _from_json(self, datastring): - try: - return jsonutils.loads(datastring) - except ValueError: - msg = _("cannot understand JSON") - raise exceptions.MalformedRequestBody(reason=msg) - - def default(self, request): - datastring = request.body - return {'body': self._from_json(datastring)} - - -class JSONPatchDeserializer(TextDeserializer): - allowed_operations = {} - schema = None - allow_unknown_path = False - - def _from_json_patch(self, datastring): - try: - operations = jsonutils.loads(datastring) - except ValueError: - msg = _("cannot understand JSON") - raise exceptions.MalformedRequestBody(reason=msg) - - if not isinstance(operations, list): - msg = _('JSON-patch must be a list.') - raise webob.exc.HTTPBadRequest(explanation=msg) - - changes = [] - for raw_change in operations: - if not isinstance(raw_change, dict): - msg = _('Operations must be JSON objects.') - raise webob.exc.HTTPBadRequest(explanation=msg) - - (op, path) = self._parse_json_schema_change(raw_change) - - self._validate_path(path) - change = {'op': op, 'path': path} - - change['value'] = self._get_change_value(raw_change, op) - self._validate_change(change) - - changes.append(change) - return changes - - def _get_change_value(self, raw_change, op): - if 'value' not in raw_change: - msg = _('Operation "%s" requires a member named "value".') - raise webob.exc.HTTPBadRequest(explanation=msg % op) - return raw_change['value'] - - def _get_change_operation(self, raw_change): - try: - return raw_change['op'] - except KeyError: - msg = _("Unable to find '%s' in JSON Schema change") % 'op' - raise webob.exc.HTTPBadRequest(explanation=msg) - - def _get_change_path(self, raw_change): - try: - return raw_change['path'] - except KeyError: - msg = _("Unable to find '%s' in JSON Schema change") % 'path' - raise webob.exc.HTTPBadRequest(explanation=msg) - - def _validate_change(self, change): - self._validate_allowed_methods(change, self.allow_unknown_path) - - if self.schema: - self._validate_schema(change) - - def _validate_allowed_methods(self, change, allow_unknown_path=False): - full_path = '/'.join(change['path']) - change_op = change['op'] - allowed_methods = self.allowed_operations.get(full_path) - - if allowed_methods is None: - if allow_unknown_path: - allowed_methods = ['add', 'replace', 'remove'] - else: - msg = _("Attribute '{0}' is invalid").format(full_path) - raise webob.exc.HTTPForbidden(explanation=str(msg)) - - if change_op not in allowed_methods: - ops = ', '.join(allowed_methods) if allowed_methods\ - else 'no operations' - msg = _("Method '{method}' is not allowed for a path with name " - "'{name}'. Allowed operations are: " - "{ops}").format(method=change_op, name=full_path, ops=ops) - - raise webob.exc.HTTPForbidden(explanation=str(msg)) - - def _validate_schema(self, change): - property_to_update = change['value'] - can_validate = True - schema = self.schema - for p in change['path']: - if schema['type'] == 'array': - try: - schema = schema['items'] - except KeyError: - can_validate = False - elif schema['type'] == 'object': - try: - schema = schema['properties'][p] - except KeyError: - can_validate = False - if can_validate: - try: - jsonschema.validate(property_to_update, schema) - except jsonschema.ValidationError as e: - LOG.error("Schema validation error occurred: %s", e) - raise webob.exc.HTTPBadRequest(explanation=e.message) - - def _decode_json_pointer(self, pointer): - """Parse a json pointer. - - Json Pointers are defined in - http://tools.ietf.org/html/draft-pbryan-zyp-json-pointer . - The pointers use '/' for separation between object attributes, such - that '/A/B' would evaluate to C in {"A": {"B": "C"}}. A '/' - character - in an attribute name is encoded as "~1" and a '~' character is - encoded - as "~0". - """ - self._validate_json_pointer(pointer) - ret = [] - for part in pointer.lstrip('/').split('/'): - ret.append(part.replace('~1', '/').replace('~0', '~').strip()) - return ret - - def _validate_json_pointer(self, pointer): - """Validate a json pointer. - - Only limited form of json pointers is accepted. - """ - if not pointer.startswith('/'): - msg = _('Pointer `%s` does not start with "/".') % pointer - raise webob.exc.HTTPBadRequest(explanation=msg) - if re.search('/\s*?/', pointer[1:]): - msg = _('Pointer `%s` contains adjacent "/".') % pointer - raise webob.exc.HTTPBadRequest(explanation=msg) - if len(pointer) > 1 and pointer.endswith('/'): - msg = _('Pointer `%s` ends with "/".') % pointer - raise webob.exc.HTTPBadRequest(explanation=msg) - if pointer[1:].strip() == '/': - msg = _('Pointer `%s` does not contain a valid token.') % pointer - raise webob.exc.HTTPBadRequest(explanation=msg) - if re.search('~[^01]', pointer) or pointer.endswith('~'): - msg = _('Pointer `%s` contains "~", which is not part of' - ' a recognized escape sequence.') % pointer - raise webob.exc.HTTPBadRequest(explanation=msg) - - def _parse_json_schema_change(self, raw_change): - op = self._get_change_operation(raw_change) - path = self._get_change_path(raw_change) - - path_list = self._decode_json_pointer(path) - return op, path_list - - def _validate_path(self, path): - pass - - def default(self, request): - return {'body': self._from_json_patch(request.body)} - - -class MuranoPackageJSONPatchDeserializer(JSONPatchDeserializer): - allowed_operations = {"categories": ["add", "replace", "remove"], - "tags": ["add", "replace", "remove"], - "is_public": ["replace"], - "enabled": ["replace"], - "description": ["replace"], - "name": ["replace"]} - allow_unknown_path = False - schema = validation_schemas.PKG_UPDATE_SCHEMA - - def _validate_path(self, path): - if len(path) > 1: - msg = _('Nested paths are not allowed') - raise webob.exc.HTTPBadRequest(explanation=msg) - - -class EnvModelJSONPatchDeserializer(JSONPatchDeserializer): - allowed_operations = {"": [], - "defaultNetworks": ["replace"], - "defaultNetworks/environment": ["replace"], - "defaultNetworks/environment/?/id": [], - "defaultNetworks/flat": ["replace"], - "name": ["replace"], - "region": ["replace"], - "?/type": ["replace"], - "?/id": [] - } - allow_unknown_path = True - schema = validation_schemas.ENV_SCHEMA - - -class XMLDeserializer(TextDeserializer): - def __init__(self, metadata=None): - """XMLDeserializer. - - :param metadata: information needed to deserialize xml into - a dictionary. - """ - super(XMLDeserializer, self).__init__() - self.metadata = metadata or {} - - def _from_xml(self, request): - datastring = request.body - plurals = set(self.metadata.get('plurals', {})) - - try: - node = xmlutils.safe_minidom_parse_string(datastring).childNodes[0] - return {node.nodeName: self._from_xml_node(node, plurals)} - except expat.ExpatError: - msg = _("cannot understand XML") - raise exceptions.MalformedRequestBody(reason=msg) - - def _from_xml_node(self, node, listnames): - """Convert a minidom node to a simple Python type. - - :param listnames: list of XML node names whose subnodes should - be considered list items. - - """ - - if len(node.childNodes) == 1 and node.childNodes[0].nodeType == 3: - return node.childNodes[0].nodeValue - elif node.nodeName in listnames: - return [self._from_xml_node(n, listnames) for n in node.childNodes] - else: - result = dict() - for attr in node.attributes.keys(): - result[attr] = node.attributes[attr].nodeValue - for child in node.childNodes: - if child.nodeType != node.TEXT_NODE: - result[child.nodeName] = self._from_xml_node(child, - listnames) - return result - - def find_first_child_named(self, parent, name): - """Find first child which has the given name of a node.""" - for node in parent.childNodes: - if node.nodeName == name: - return node - return None - - def find_children_named(self, parent, name): - """Return all children of a node with the given name.""" - for node in parent.childNodes: - if node.nodeName == name: - yield node - - def extract_text(self, node): - """Get the text field contained by the given node.""" - if len(node.childNodes) == 1: - child = node.childNodes[0] - if child.nodeType == child.TEXT_NODE: - return child.nodeValue - return "" - - def default(self, datastring): - return {'body': self._from_xml(datastring)} - - -class FormDataDeserializer(TextDeserializer): - def _from_json(self, datastring): - value = datastring - try: - LOG.debug("Trying to deserialize '{data}' to json".format( - data=datastring)) - value = jsonutils.loads(datastring) - except ValueError: - LOG.warning("Unable to deserialize to json, using raw text") - return value - - def default(self, request): - form_data_parts = request.POST - for key, value in form_data_parts.items(): - if isinstance(value, str): - form_data_parts[key] = self._from_json(value) - return {'body': form_data_parts} diff --git a/murano/common/xmlutils.py b/murano/common/xmlutils.py deleted file mode 100644 index 7e3a78b2d..000000000 --- a/murano/common/xmlutils.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# Licensed 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 xml.dom import minidom -from xml.parsers import expat -from xml import sax -from xml.sax import expatreader - - -class ProtectedExpatParser(expatreader.ExpatParser): - """An expat parser which disables DTD's and entities by default.""" - - def __init__(self, forbid_dtd=True, forbid_entities=True, - *args, **kwargs): - # Python 2.x old style class - expatreader.ExpatParser.__init__(self, *args, **kwargs) - self.forbid_dtd = forbid_dtd - self.forbid_entities = forbid_entities - - def start_doctype_decl(self, name, sysid, pubid, has_internal_subset): - raise ValueError("Inline DTD forbidden") - - def entity_decl(self, entityName, is_parameter_entity, value, base, - systemId, publicId, notationName): - raise ValueError(" entity declaration forbidden") - - def unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): - # expat 1.2 - raise ValueError(" unparsed entity forbidden") - - def external_entity_ref(self, context, base, systemId, publicId): - raise ValueError(" external entity forbidden") - - def notation_decl(self, name, base, sysid, pubid): - raise ValueError(" notation forbidden") - - def reset(self): - expatreader.ExpatParser.reset(self) - if self.forbid_dtd: - self._parser.StartDoctypeDeclHandler = self.start_doctype_decl - self._parser.EndDoctypeDeclHandler = None - if self.forbid_entities: - self._parser.EntityDeclHandler = self.entity_decl - self._parser.UnparsedEntityDeclHandler = self.unparsed_entity_decl - self._parser.ExternalEntityRefHandler = self.external_entity_ref - self._parser.NotationDeclHandler = self.notation_decl - try: - self._parser.SkippedEntityHandler = None - except AttributeError: - # some pyexpat versions do not support SkippedEntity - pass - - -def safe_minidom_parse_string(xml_string): - """Parse an XML string using minidom safely. - - """ - try: - return minidom.parseString( # nosec - xml_string, parser=ProtectedExpatParser()) # nosec - except sax.SAXParseException: - raise expat.ExpatError() diff --git a/murano/context.py b/murano/context.py deleted file mode 100644 index 6935bd8e1..000000000 --- a/murano/context.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_context import context - -from murano.common import policy - - -class RequestContext(context.RequestContext): - """Class that stores context info about an API request. - - Stores creditentials of the user, that is accessing API - as well as additional request information. - """ - - def __init__(self, session=None, service_catalog=None, - **kwargs): - super(RequestContext, self).__init__(**kwargs) - self.session = session - self.service_catalog = service_catalog - if self.is_admin is None: - self.is_admin = policy.check_is_admin(self) diff --git a/murano/db/__init__.py b/murano/db/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/api.py b/murano/db/api.py deleted file mode 100644 index 8d9cf6001..000000000 --- a/murano/db/api.py +++ /dev/null @@ -1,25 +0,0 @@ -# Licensed 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 murano.db import models -from murano.db import session - - -def setup_db(): - engine = session.get_engine() - models.register_models(engine) - - -def drop_db(): - engine = session.get_engine() - models.unregister_models(engine) diff --git a/murano/db/catalog/__init__.py b/murano/db/catalog/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/catalog/api.py b/murano/db/catalog/api.py deleted file mode 100644 index cecb0f93d..000000000 --- a/murano/db/catalog/api.py +++ /dev/null @@ -1,570 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 oslo_db import api as oslo_db_api -from oslo_db import exception as db_exceptions -from oslo_db.sqlalchemy import utils -from oslo_log import log as logging -import re -import sqlalchemy as sa -from sqlalchemy import or_ -from sqlalchemy.orm import attributes -# TODO(ruhe) use exception declared in openstack/common/db -from webob import exc - -from murano.common.i18n import _ -from murano.db import models -from murano.db import session as db_session - - -SEARCH_MAPPING = {'fqn': 'fully_qualified_name', - 'name': 'name', - 'created': 'created' - } - -LOG = logging.getLogger(__name__) - - -def _package_get(package_id, session): - # TODO(sjmc7): update openstack/common and pull in - # uuidutils, check that package_id_or_name resembles a - # UUID before trying to treat it as one - package = session.query(models.Package).get(package_id) - if not package: - msg = _("Package id '{pkg_id}' not found").format(pkg_id=package_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - return package - - -def _authorize_package(package, context, allow_public=False): - - if package.owner_id != context.project_id: - if not allow_public: - msg = _("Package '{pkg_id}' is not owned by tenant " - "'{tenant}'").format(pkg_id=package.id, - tenant=context.project_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - if not package.is_public: - msg = _("Package '{pkg_id}' is not public and not owned by " - "tenant '{tenant}' ").format(pkg_id=package.id, - tenant=context.project_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - -def package_get(package_id, context): - """Return package details - - :param package_id: ID or name of a package, string - :returns: detailed information about package, dict - """ - session = db_session.get_session() - package = _package_get(package_id, session) - if not context.is_admin: - _authorize_package(package, context, allow_public=True) - return package - - -def _get_categories(category_names, session=None): - """Return existing category objects or raise an exception. - - :param category_names: name of categories - to associate with package, list - :returns: list of Category objects to associate with package, list - """ - if session is None: - session = db_session.get_session() - categories = [] - for ctg_name in category_names: - ctg_obj = session.query(models.Category).filter_by( - name=ctg_name).first() - if not ctg_obj: - msg = _("Category '{name}' doesn't exist").format(name=ctg_name) - LOG.error(msg) - # it's not allowed to specify non-existent categories - raise exc.HTTPBadRequest(explanation=msg) - - categories.append(ctg_obj) - return categories - - -def _existing_tag(tag_name, session=None): - if session is None: - session = db_session.get_session() - return session.query(models.Tag).filter_by(name=tag_name).first() - - -def _get_tags(tag_names, session=None): - """Return existing tags object or create new ones - - :param tag_names: name of tags to associate with package, list - :returns: list of Tag objects to associate with package, list - """ - if session is None: - session = db_session.get_session() - tags = [] - # This function can be called inside a transaction and outside it. - # In the former case this line is no-op, in the latter - # starts a transaction we need to be inside a transaction, to correctly - # handle DBDuplicateEntry errors without failing the whole transaction. - # For more take a look at SQLAlchemy docs. - with session.begin(subtransactions=True): - for tag_name in tag_names: - tag_obj = _existing_tag(tag_name, session) - if not tag_obj: - try: - # Start a new SAVEPOINT transaction. If it fails - # only the savepoint will be roll backed, not the - # whole transaction. - with session.begin(nested=True): - tag_obj = models.Tag(name=tag_name) - session.add(tag_obj) - session.flush(objects=[tag_obj]) - except db_exceptions.DBDuplicateEntry: - # new session is needed here to get access to the tag - tag_obj = _existing_tag(tag_name) - tags.append(tag_obj) - - return tags - - -def _get_class_definitions(class_names, session=None): - classes = [] - for name in class_names: - class_record = models.Class(name=name) - classes.append(class_record) - return classes - - -def _do_replace(package, change): - path = change['path'][0] - value = change['value'] - calculate = {'categories': _get_categories, - 'tags': _get_tags} - if path in ('categories', 'tags'): - existing_items = getattr(package, path) - - duplicates = list(set(i.name for i in existing_items) & set(value)) - unique_values = [x for x in value if x not in duplicates] - items_to_replace = calculate[path](unique_values) - - # NOTE(efedorova): Replacing duplicate entities is not allowed, - # so need to remove anything, but duplicates - # and append everything but duplicates - for item in list(existing_items): - if item.name not in duplicates: - existing_items.remove(item) - - existing_items.extend(items_to_replace) - else: - setattr(package, path, value) - - return package - - -def _do_add(package, change): - # Only categories and tags support addition - path = change['path'][0] - value = change['value'] - - calculate = {'categories': _get_categories, - 'tags': _get_tags} - items_to_add = calculate[path](value) - for item in items_to_add: - try: - getattr(package, path).append(item) - except AssertionError: - LOG.warning('One of the specified {path} is already associated' - ' with a package. Doing nothing.'.format(path=path)) - return package - - -def _do_remove(package, change): - # Only categories and tags support removal - def find(seq, predicate): - for elt in seq: - if predicate(elt): - return elt - - path = change['path'][0] - values = change['value'] - - current_values = getattr(package, path) - for value in values: - if value not in [i.name for i in current_values]: - msg = _("Value '{value}' of property '{path}' " - "does not exist.").format(value=value, path=path) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - item_to_remove = find(current_values, lambda i: i.name == value) - current_values.remove(item_to_remove) - return package - - -def _get_packages_for_category(session, category_id): - """Return detailed list of packages, belonging to the provided category - - :param session: - :param category_id: - :return: list of dictionaries, containing id, name and package fqn - """ - pkg = models.Package - packages = (session.query(pkg.id, pkg.name, pkg.fully_qualified_name) - .filter(pkg.categories - .any(models.Category.id == category_id)) - .all()) - - result_packages = [] - for package in packages: - id, name, fqn = package - result_packages.append({'id': id, - 'name': name, - 'fully_qualified_name': fqn}) - return result_packages - - -@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True) -def package_update(pkg_id_or_name, changes, context): - """Update package information - - :param changes: parameters to update - :returns: detailed information about new package, dict - """ - - operation_methods = {'add': _do_add, - 'replace': _do_replace, - 'remove': _do_remove} - session = db_session.get_session() - with session.begin(): - pkg = _package_get(pkg_id_or_name, session) - was_private = not pkg.is_public - if not context.is_admin: - _authorize_package(pkg, context) - - for change in changes: - pkg = operation_methods[change['op']](pkg, change) - became_public = pkg.is_public - class_names = [clazz.name for clazz in pkg.class_definitions] - if was_private and became_public: - with db_session.get_lock("public_packages", session): - _check_for_public_packages_with_fqn(session, - pkg.fully_qualified_name, - pkg.id) - _check_for_existing_classes(session, class_names, None, - check_public=True, - ignore_package_with_id=pkg.id) - - session.add(pkg) - return pkg - - -def package_search(filters, context, manage_public=False, - limit=None, catalog=False): - """Search packages with different filters - - Catalog param controls the base query creation. Catalog queries - only search packages a user can deploy. Non-catalog queries searches - packages a user can edit. - * Admin is allowed to browse all the packages - * Regular user is allowed to browse all packages belongs to user tenant - and all other packages marked is_public in catalog mode. - In edit-mode non-admins are able to get own packages and public - packages if corresponding policy is passed. - * Use marker (inside filters param) and limit for pagination: - The typical pattern of limit and marker is to make an initial limited - request and then to use the ID of the last package from the response - as the marker parameter in a subsequent limited request. - """ - # pylint: disable=too-many-branches - - session = db_session.get_session() - pkg = models.Package - - query = session.query(pkg) - - if catalog: - # Only show packages one can deploy, i.e. own + public - query = query.filter(or_(pkg.owner_id == context.project_id, - pkg.is_public)) - else: - # Show packages one can edit. - if not context.is_admin: - if manage_public: - query = query.filter(or_(pkg.owner_id == context.project_id, - pkg.is_public)) - else: - query = query.filter(pkg.owner_id == context.project_id) - # No else here admin can edit everything. - - if not filters.get('include_disabled', '').lower() == 'true': - query = query.filter(pkg.enabled) - - if filters.get('owned', '').lower() == 'true': - query = query.filter(pkg.owner_id == context.project_id) - - if 'type' in filters.keys(): - query = query.filter(pkg.type == filters['type'].title()) - - if 'id' in filters: - query = query.filter(models.Package.id.in_(filters['id'])) - if 'category' in filters.keys(): - query = query.filter(pkg.categories.any( - models.Category.name.in_(filters['category']))) - if 'tag' in filters.keys(): - query = query.filter(pkg.tags.any( - models.Tag.name.in_(filters['tag']))) - if 'class_name' in filters.keys(): - query = query.filter(pkg.class_definitions.any( - models.Class.name == filters['class_name'])) - if 'fqn' in filters.keys(): - query = query.filter(pkg.fully_qualified_name == filters['fqn']) - if 'name' in filters.keys(): - query = query.filter(pkg.name == filters['name']) - - if 'search' in filters.keys(): - fk_fields = {'categories': 'Category', - 'tags': 'Tag', - 'class_definitions': 'Class'} - # the default search order - fields = ['name', - 'fully_qualified_name', - 'description', - 'categories', - 'tags', - 'class_definitions', - 'author'] - # split to searching words - key_words = re.split(';|,', filters['search']) - - conditions = [] - order_cases = [] - sorted_fields = fields + list(set(dir(pkg)).difference(set(fields))) - for index in range(0, len(sorted_fields)): - attr = sorted_fields[index] - if attr.startswith('_'): - continue - if not isinstance(getattr(pkg, attr), - attributes.InstrumentedAttribute): - continue - priority = min(index, len(fields)) - for key_word in key_words: - _word = u'%{value}%'.format(value=key_word) - if attr in fk_fields.keys(): - condition = getattr(pkg, attr).any( - getattr(models, fk_fields[attr]).name.like(_word)) - conditions.append(condition) - order_cases.append((condition, priority)) - elif isinstance(getattr(pkg, attr) - .property.columns[0].type, sa.String): - condition = getattr(pkg, attr).like(_word) - conditions.append(condition) - order_cases.append((condition, priority)) - - order_expression = sa.case(order_cases).label("tmp_weight_uuid") - query = query.filter(or_(*conditions)).order_by(order_expression.asc()) - - sort_keys = [SEARCH_MAPPING[sort_key] for sort_key in - filters.get('order_by', ['name'])] - sort_keys.append('id') - marker = filters.get('marker') - sort_dir = filters.get('sort_dir') - - if marker is not None: # set marker to real object instead of its id - marker = _package_get(marker, session) - - query = utils.paginate_query( - query, pkg, limit, sort_keys, marker, sort_dir) - - return query.all() - - -@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True) -def package_upload(values, tenant_id): - """Upload a package with new application - - :param values: parameters describing the new package - :returns: detailed information about new package, dict - """ - session = db_session.get_session() - package = models.Package() - - composite_attr_to_func = {'categories': _get_categories, - 'tags': _get_tags, - 'class_definitions': _get_class_definitions} - is_public = values.get('is_public', False) - - if is_public: - public_lock = db_session.get_lock("public_packages", session) - else: - public_lock = None - tenant_lock = db_session.get_lock("classes_of_" + tenant_id, session) - try: - _check_for_existing_classes(session, values.get('class_definitions'), - tenant_id, check_public=is_public) - if is_public: - _check_for_public_packages_with_fqn( - session, - values.get('fully_qualified_name')) - for attr, func in composite_attr_to_func.items(): - if values.get(attr): - result = func(values[attr], session) - setattr(package, attr, result) - del values[attr] - package.update(values) - package.owner_id = tenant_id - package.save(session) - tenant_lock.commit() - if public_lock is not None: - public_lock.commit() - except Exception: - tenant_lock.rollback() - if public_lock is not None: - public_lock.rollback() - raise - - return package - - -@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True) -def package_delete(package_id, context): - """Delete a package by ID.""" - session = db_session.get_session() - - with session.begin(): - package = _package_get(package_id, session) - if not context.is_admin and package.owner_id != context.project_id: - raise exc.HTTPForbidden( - explanation="Package is not owned by the" - " tenant '{0}'".format(context.project_id)) - session.delete(package) - - -def category_get(category_id, session=None, packages=False): - """Return category details - - :param category_id: ID of a category, string - :returns: detailed information about category, dict - """ - if not session: - session = db_session.get_session() - - category = session.query(models.Category).get(category_id) - if not category: - msg = _("Category id '{id}' not found").format(id=category_id) - LOG.error(msg) - raise exc.HTTPNotFound(msg) - if packages: - category.packages = _get_packages_for_category(session, category_id) - return category - - -def categories_list(filters=None, limit=None, marker=None): - if filters is None: - filters = {} - sort_keys = filters.get('sort_keys', ['name']) - sort_dir = filters.get('sort_dir', 'asc') - - session = db_session.get_session() - query = session.query(models.Category) - if marker is not None: - marker = category_get(marker, session) - - query = utils.paginate_query( - query, models.Category, limit, sort_keys, marker, sort_dir) - return query.all() - - -def category_get_names(): - session = db_session.get_session() - categories = [] - for row in session.query(models.Category.name).all(): - for name in row: - categories.append(name) - return categories - - -def category_add(category_name): - session = db_session.get_session() - category = models.Category() - - with session.begin(): - category.update({'name': category_name}) - # NOTE(kzaitsev) update package_count, so we can safely access from - # outside the session - category.package_count = 0 - category.save(session) - - return category - - -def category_delete(category_id): - """Delete a category by ID.""" - session = db_session.get_session() - - with session.begin(): - category = session.query(models.Category).get(category_id) - if not category: - msg = _("Category id '{id}' not found").format(id=category_id) - LOG.error(msg) - raise exc.HTTPNotFound(msg) - session.delete(category) - - -def _check_for_existing_classes(session, class_names, tenant_id, - check_public=False, - ignore_package_with_id=None): - if not class_names: - return - q = session.query(models.Class.name).filter( - models.Class.name.in_(class_names)) - private_filter = None - public_filter = None - predicate = None - if tenant_id is not None: - private_filter = models.Class.package.has( - models.Package.owner_id == tenant_id) - if check_public: - public_filter = models.Class.package.has( - models.Package.is_public) - if private_filter is not None and public_filter is not None: - predicate = sa.or_(private_filter, public_filter) - elif private_filter is not None: - predicate = private_filter - elif public_filter is not None: - predicate = public_filter - if predicate is not None: - q = q.filter(predicate) - if ignore_package_with_id is not None: - q = q.filter(models.Class.package_id != ignore_package_with_id) - if q.first() is not None: - msg = _('Class with the same full name is already ' - 'registered in the visibility scope') - LOG.error(msg) - raise exc.HTTPConflict(msg) - - -def _check_for_public_packages_with_fqn(session, fqn, - ignore_package_with_id=None): - q = session.query(models.Package.id).\ - filter(models.Package.is_public).\ - filter(models.Package.fully_qualified_name == fqn) - if ignore_package_with_id is not None: - q = q.filter(models.Package.id != ignore_package_with_id) - if q.first() is not None: - msg = _('Package with the same Name is already made public') - LOG.error(msg) - raise exc.HTTPConflict(msg) diff --git a/murano/db/cfapi_migration/__init__.py b/murano/db/cfapi_migration/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/cfapi_migration/alembic.ini b/murano/db/cfapi_migration/alembic.ini deleted file mode 100644 index 886c80883..000000000 --- a/murano/db/cfapi_migration/alembic.ini +++ /dev/null @@ -1,54 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = murano/db/cfapi_migration/alembic_migrations - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# max length of characters to apply to the -# "slug" field -#truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -sqlalchemy.url = - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S diff --git a/murano/db/cfapi_migration/alembic_migrations/README b/murano/db/cfapi_migration/alembic_migrations/README deleted file mode 100644 index f81a8c70e..000000000 --- a/murano/db/cfapi_migration/alembic_migrations/README +++ /dev/null @@ -1,15 +0,0 @@ -Please see https://alembic.readthedocs.org/en/latest/index.html for general documentation - -To create alembic migrations use: -$ murano-cfapi-db-manage revision --message --autogenerate - -Stamp db with most recent migration version, without actually running migrations -$ murano-cfapi-db-manage stamp --revision head - -Upgrade can be performed by: -$ murano-cfapi-db-manage upgrade -$ murano-cfapi-db-manage upgrade --revision head - -Downgrading db: -$ murano-cfapi-db-manage downgrade -$ murano-cfapi-db-manage downgrade --revision base diff --git a/murano/db/cfapi_migration/alembic_migrations/env.py b/murano/db/cfapi_migration/alembic_migrations/env.py deleted file mode 100644 index f98fda028..000000000 --- a/murano/db/cfapi_migration/alembic_migrations/env.py +++ /dev/null @@ -1,48 +0,0 @@ -# Licensed 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 alembic import context -from sqlalchemy import create_engine, pool - -from murano.db import cfapi_models as models - - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config -murano_config = config.murano_config - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -target_metadata = models.Base.metadata - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - engine = create_engine( - murano_config.database.connection, - poolclass=pool.NullPool) - - with engine.connect() as connection: - context.configure(connection=connection, - target_metadata=target_metadata) - with context.begin_transaction(): - context.run_migrations() - - -run_migrations_online() diff --git a/murano/db/cfapi_migration/alembic_migrations/script.py.mako b/murano/db/cfapi_migration/alembic_migrations/script.py.mako deleted file mode 100644 index 4a23e952c..000000000 --- a/murano/db/cfapi_migration/alembic_migrations/script.py.mako +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright ${create_date.year} OpenStack Foundation. -# -# Licensed 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. - -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision} -Create Date: ${create_date} - -""" - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} \ No newline at end of file diff --git a/murano/db/cfapi_migration/alembic_migrations/versions/001_initial_version.py b/murano/db/cfapi_migration/alembic_migrations/versions/001_initial_version.py deleted file mode 100644 index cd5ecdbbb..000000000 --- a/murano/db/cfapi_migration/alembic_migrations/versions/001_initial_version.py +++ /dev/null @@ -1,66 +0,0 @@ -# Licensed 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. - -""" -Revision ID: 001 -Revises: None -Create Date: 2016-03-30 16:34:33.698760 - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '001' -down_revision = None - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.create_table( - 'cf_orgs', - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('tenant', sa.String(length=255), nullable=False), - sa.UniqueConstraint('tenant'), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - - op.create_table( - 'cf_spaces', - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('environment_id', sa.String(length=255), nullable=False), - sa.UniqueConstraint('environment_id'), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - - op.create_table( - 'cf_serv_inst', - sa.Column('id', sa.String(length=255), primary_key=True), - sa.Column('service_id', sa.String(255), nullable=False), - sa.Column('environment_id', sa.String(255), nullable=False), - sa.Column('tenant', sa.String(255), nullable=False), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - # end Alembic commands # - - -def downgrade(): - op.drop_table('cf_orgs') - op.drop_table('cf_spaces') - op.drop_table('cf_serv_inst') - # end Alembic commands # diff --git a/murano/db/cfapi_migration/migration.py b/murano/db/cfapi_migration/migration.py deleted file mode 100644 index 3f1c8108e..000000000 --- a/murano/db/cfapi_migration/migration.py +++ /dev/null @@ -1,86 +0,0 @@ -# Licensed 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 os - -import alembic -from alembic import config as alembic_config -from alembic import migration as alembic_migration - -from murano.db import session as db_session - - -def get_alembic_config(): - path = os.path.join(os.path.dirname(__file__), 'alembic.ini') - - config = alembic_config.Config(path) - config.set_main_option('script_location', - 'murano.db.cfapi_migration:alembic_migrations') - return config - - -def version(engine=None): - """Returns current database version.""" - engine = engine or db_session.get_engine() - with engine.connect() as conn: - context = alembic_migration.MigrationContext.configure(conn) - return context.get_current_revision() - - -def upgrade(revision, config=None): - """Used for upgrading database. - - :param version: Desired database version - :type version: string - """ - revision = revision or 'head' - config = config or get_alembic_config() - - alembic.command.upgrade(config, revision) - - -def downgrade(revision, config=None): - """Used for downgrading database. - - :param version: Desired database version7 - :type version: string - """ - revision = revision or 'base' - config = config or get_alembic_config() - return alembic.command.downgrade(config, revision) - - -def stamp(revision, config=None): - """Stamps database with provided revision. - - Don't run any migrations. - - :param revision: Should match one from repository or head - to stamp - database with most recent revision - :type revision: string - """ - config = config or get_alembic_config() - return alembic.command.stamp(config, revision=revision) - - -def revision(message=None, autogenerate=False, config=None): - """Creates template for migration. - - :param message: Text that will be used for migration title - :type message: string - :param autogenerate: If True - generates diff based on current database - state - :type autogenerate: bool - """ - config = config or get_alembic_config() - return alembic.command.revision(config, message=message, - autogenerate=autogenerate) diff --git a/murano/db/cfapi_models.py b/murano/db/cfapi_models.py deleted file mode 100644 index 04c7fcb77..000000000 --- a/murano/db/cfapi_models.py +++ /dev/null @@ -1,61 +0,0 @@ -# Licensed 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. - -""" -SQLAlchemy models for service broker data -""" -from oslo_db.sqlalchemy import models -import sqlalchemy as sa -from sqlalchemy.ext import declarative - - -class _ServiceBrokerBase(models.ModelBase): - pass - - -Base = declarative.declarative_base(cls=_ServiceBrokerBase) - - -class CFOrganization(Base): - __tablename__ = "cf_orgs" - id = sa.Column(sa.String(255), primary_key=True) - tenant = sa.Column(sa.String(255), nullable=False) - - -class CFSpace(Base): - __tablename__ = "cf_spaces" - - id = sa.Column(sa.String(255), primary_key=True) - environment_id = sa.Column(sa.String(255), nullable=False) - - -class CFServiceInstance(Base): - __tablename__ = 'cf_serv_inst' - - id = sa.Column(sa.String(255), primary_key=True) - service_id = sa.Column(sa.String(255), nullable=False) - environment_id = sa.Column(sa.String(255), nullable=False) - tenant = sa.Column(sa.String(255), nullable=False) - - -def register_models(engine): - """Creates database tables for all models with the given engine.""" - models = (CFSpace, CFOrganization, CFServiceInstance) - for model in models: - model.metadata.create_all(engine) - - -def unregister_models(engine): - """Drops database tables for all models with the given engine.""" - models = (CFOrganization, CFSpace, CFServiceInstance) - for model in models: - model.metadata.drop_all(engine) diff --git a/murano/db/migration/__init__.py b/murano/db/migration/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/migration/alembic.ini b/murano/db/migration/alembic.ini deleted file mode 100644 index 378da37be..000000000 --- a/murano/db/migration/alembic.ini +++ /dev/null @@ -1,54 +0,0 @@ -# A generic, single database configuration. - -[alembic] -# path to migration scripts -script_location = murano/db/migration/alembic_migrations - -# template used to generate migration files -# file_template = %%(rev)s_%%(slug)s - -# max length of characters to apply to the -# "slug" field -#truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -sqlalchemy.url = - - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARN -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARN -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S \ No newline at end of file diff --git a/murano/db/migration/alembic_migrations/README b/murano/db/migration/alembic_migrations/README deleted file mode 100644 index f3c1411e4..000000000 --- a/murano/db/migration/alembic_migrations/README +++ /dev/null @@ -1,15 +0,0 @@ -Please see https://alembic.readthedocs.org/en/latest/index.html for general documentation - -To create alembic migrations use: -$ murano-db-manage revision --message --autogenerate - -Stamp db with most recent migration version, without actually running migrations -$ murano-db-manage stamp --revision head - -Upgrade can be performed by: -$ murano-db-manage upgrade -$ murano-db-manage upgrade --revision head - -Downgrading db: -$ murano-db-manage downgrade -$ murano-db-manage downgrade --revision base diff --git a/murano/db/migration/alembic_migrations/env.py b/murano/db/migration/alembic_migrations/env.py deleted file mode 100644 index 50c263b3c..000000000 --- a/murano/db/migration/alembic_migrations/env.py +++ /dev/null @@ -1,48 +0,0 @@ -# Licensed 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 alembic import context -from sqlalchemy import create_engine, pool - -from murano.db import models - - -# this is the Alembic Config object, which provides -# access to the values within the .ini file in use. -config = context.config -murano_config = config.murano_config - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -target_metadata = models.Base.metadata - - -def run_migrations_online(): - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - engine = create_engine( - murano_config.database.connection, - poolclass=pool.NullPool) - - with engine.connect() as connection: - context.configure(connection=connection, - target_metadata=target_metadata) - with context.begin_transaction(): - context.run_migrations() - - -run_migrations_online() diff --git a/murano/db/migration/alembic_migrations/script.py.mako b/murano/db/migration/alembic_migrations/script.py.mako deleted file mode 100644 index 4a23e952c..000000000 --- a/murano/db/migration/alembic_migrations/script.py.mako +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright ${create_date.year} OpenStack Foundation. -# -# Licensed 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. - -"""${message} - -Revision ID: ${up_revision} -Revises: ${down_revision} -Create Date: ${create_date} - -""" - -# revision identifiers, used by Alembic. -revision = ${repr(up_revision)} -down_revision = ${repr(down_revision)} - -from alembic import op -import sqlalchemy as sa -${imports if imports else ""} - -def upgrade(): - ${upgrades if upgrades else "pass"} - - -def downgrade(): - ${downgrades if downgrades else "pass"} \ No newline at end of file diff --git a/murano/db/migration/alembic_migrations/versions/001_initial_version.py b/murano/db/migration/alembic_migrations/versions/001_initial_version.py deleted file mode 100644 index 8bb4e4de1..000000000 --- a/murano/db/migration/alembic_migrations/versions/001_initial_version.py +++ /dev/null @@ -1,274 +0,0 @@ -# Licensed 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. - -"""empty message - -Revision ID: 001 -Revises: None -Create Date: 2014-05-29 16:32:33.698760 - -""" -import uuid - -from alembic import op -from oslo_utils import timeutils -import sqlalchemy as sa -from sqlalchemy.sql.expression import table as sa_table - -from murano.common import consts -from murano.db.sqla import types as st - -# revision identifiers, used by Alembic. -revision = '001' -down_revision = None - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def _create_default_categories(op): - bind = op.get_bind() - table = sa_table( - 'category', - sa.Column('id', sa.String(length=36), primary_key=True), - sa.Column('created', sa.DateTime()), - sa.Column('updated', sa.DateTime()), - sa.Column('name', sa.String(length=80))) - - now = timeutils.utcnow() - - for category in consts.CATEGORIES: - values = {'id': uuid.uuid4().hex, - 'name': category, - 'updated': now, - 'created': now} - bind.execute(table.insert(values=values)) - - -def upgrade(): - op.create_table( - 'environment', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('name', sa.String(length=255), nullable=False), - sa.Column('tenant_id', sa.String(length=36), nullable=False), - sa.Column('version', sa.BigInteger(), nullable=False), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('networking', sa.Text(), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('tenant_id', 'name'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'tag', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('name', sa.String(length=80), nullable=False), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('name'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'category', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('name', sa.String(length=80), nullable=False, unique=True), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_index('ix_category_name', 'category', ['name']) - - op.create_table( - 'apistats', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('host', sa.String(length=80), nullable=True), - sa.Column('request_count', sa.BigInteger(), nullable=True), - sa.Column('error_count', sa.BigInteger(), nullable=True), - sa.Column('average_response_time', sa.Float(), nullable=True), - sa.Column('requests_per_tenant', sa.Text(), nullable=True), - sa.Column('requests_per_second', sa.Float(), nullable=True), - sa.Column('errors_per_second', sa.Float(), nullable=True), - sa.Column('cpu_count', sa.Integer(), nullable=True), - sa.Column('cpu_percent', sa.Float(), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'instance_stats', - sa.Column('environment_id', sa.String(length=255), nullable=False), - sa.Column('instance_id', sa.String(length=255), nullable=False), - sa.Column('instance_type', sa.Integer(), nullable=False), - sa.Column('created', sa.Integer(), nullable=False), - sa.Column('destroyed', sa.Integer(), nullable=True), - sa.Column('type_name', sa.String(length=512), nullable=False), - sa.Column('type_title', sa.String(length=512), nullable=True), - sa.Column('unit_count', sa.Integer(), nullable=True), - sa.Column('tenant_id', sa.String(length=36), nullable=False), - sa.PrimaryKeyConstraint('environment_id', 'instance_id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'package', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('archive', st.LargeBinary(), nullable=True), - sa.Column('fully_qualified_name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('type', sa.String(length=20), nullable=False), - sa.Column('author', sa.String(length=80), nullable=True), - sa.Column('name', sa.String(length=80), nullable=False), - sa.Column('enabled', sa.Boolean(), nullable=True), - sa.Column('description', sa.String(length=512), nullable=False), - sa.Column('is_public', sa.Boolean(), nullable=True), - sa.Column('logo', st.LargeBinary(), nullable=True), - sa.Column('owner_id', sa.String(length=36), nullable=False), - sa.Column('ui_definition', sa.Text(), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_index('ix_package_fqn', - 'package', - ['fully_qualified_name']) - - op.create_table( - 'session', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('environment_id', sa.String(length=255), nullable=True), - sa.Column('user_id', sa.String(length=36), nullable=False), - sa.Column('state', sa.String(length=36), nullable=False), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('version', sa.BigInteger(), nullable=False), - sa.ForeignKeyConstraint(['environment_id'], ['environment.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'deployment', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('started', sa.DateTime(), nullable=False), - sa.Column('finished', sa.DateTime(), nullable=True), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('environment_id', sa.String(length=255), nullable=True), - sa.ForeignKeyConstraint(['environment_id'], ['environment.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'class_definition', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('package_id', sa.String(length=36), nullable=True), - sa.ForeignKeyConstraint(['package_id'], ['package.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_index('ix_class_definition_name', - 'class_definition', - ['name']) - - op.create_table( - 'status', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('entity_id', sa.String(length=255), nullable=True), - sa.Column('entity', sa.String(length=10), nullable=True), - sa.Column('deployment_id', sa.String(length=36), nullable=True), - sa.Column('text', sa.Text(), nullable=False), - sa.Column('level', sa.String(length=32), nullable=False), - sa.Column('details', sa.Text(), nullable=True), - sa.ForeignKeyConstraint(['deployment_id'], ['deployment.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'package_to_tag', - sa.Column('package_id', sa.String(length=36), nullable=False), - sa.Column('tag_id', sa.String(length=36), nullable=False), - sa.ForeignKeyConstraint(['package_id'], ['package.id'], ), - sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ondelete='CASCADE'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_table( - 'package_to_category', - sa.Column('package_id', sa.String(length=36), nullable=False), - sa.Column('category_id', sa.String(length=36), nullable=False), - sa.ForeignKeyConstraint(['category_id'], - ['category.id'], - ondelete='RESTRICT'), - sa.ForeignKeyConstraint(['package_id'], ['package.id'], ), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - _create_default_categories(op) - # end Alembic commands # - - -def downgrade(): - op.drop_index('ix_category_name', table_name='category') - op.drop_index('ix_package_fqn', table_name='package') - op.drop_index('ix_class_definition_name', table_name='class_definition') - - op.drop_table('status') - op.drop_table('package_to_category') - op.drop_table('class_definition') - op.drop_table('deployment') - op.drop_table('package_to_tag') - op.drop_table('session') - op.drop_table('instance_stats') - op.drop_table('package') - op.drop_table('apistats') - op.drop_table('category') - op.drop_table('tag') - op.drop_table('environment') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/002_add_package_supplier_info.py b/murano/db/migration/alembic_migrations/versions/002_add_package_supplier_info.py deleted file mode 100644 index 14e3ee7e0..000000000 --- a/murano/db/migration/alembic_migrations/versions/002_add_package_supplier_info.py +++ /dev/null @@ -1,47 +0,0 @@ -# Licensed 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. - -"""empty message - -Revision ID: 002 -Revises: None -Create Date: 2014-06-23 16:34:33.698760 - -""" -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '002' -down_revision = '001' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.add_column( - 'package', - sa.Column('supplier_logo', sa.types.LargeBinary) - ) - op.add_column( - 'package', - sa.Column('supplier', sa.types.Text()) - ) - # end Alembic commands # - - -def downgrade(): - op.drop_column('package', 'supplier') - op.drop_column('package', 'supplier_logo') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/003_add_action_entry.py b/murano/db/migration/alembic_migrations/versions/003_add_action_entry.py deleted file mode 100644 index 8e8b04b34..000000000 --- a/murano/db/migration/alembic_migrations/versions/003_add_action_entry.py +++ /dev/null @@ -1,91 +0,0 @@ -# Licensed 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. - -""" -Add action column to deployment table. - -Revision ID: 003 -Revises: table deployment -Create Date: 2014-07-30 16:11:33.244 - -""" -from alembic import op -import sqlalchemy as sa - -import murano.db.migration.helpers as helpers - -# revision identifiers, used by Alembic. -revision = '003' -down_revision = '002' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.rename_table('deployment', 'task') - op.add_column( - 'task', - sa.Column('action', sa.types.Text()) - ) - op.create_table( - 'deployment', - sa.Column('id', sa.String(length=36), nullable=False)) - - helpers.transform_table( - 'status', {'deployment_id': 'task_id'}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('entity_id', sa.String(length=255), nullable=True), - sa.Column('entity', sa.String(length=10), nullable=True), - sa.Column('task_id', sa.String(length=36), nullable=True), - sa.Column('text', sa.Text(), nullable=False), - sa.Column('level', sa.String(length=32), nullable=False), - sa.Column('details', sa.Text(), nullable=True), - sa.ForeignKeyConstraint(['task_id'], ['task.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.drop_table('deployment') - - -def downgrade(): - op.drop_column('task', 'action') - op.rename_table('task', 'deployment') - - op.create_table( - 'task', - sa.Column('id', sa.String(length=36), nullable=False)) - - helpers.transform_table( - 'status', {'task_id': 'deployment_id'}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('entity_id', sa.String(length=255), nullable=True), - sa.Column('entity', sa.String(length=10), nullable=True), - sa.Column('deployment_id', sa.String(length=36), nullable=True), - sa.Column('text', sa.Text(), nullable=False), - sa.Column('level', sa.String(length=32), nullable=False), - sa.Column('details', sa.Text(), nullable=True), - sa.ForeignKeyConstraint(['deployment_id'], ['deployment.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.drop_table('task') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/004_change_package_desc_type.py b/murano/db/migration/alembic_migrations/versions/004_change_package_desc_type.py deleted file mode 100644 index 1f9e8c820..000000000 --- a/murano/db/migration/alembic_migrations/versions/004_change_package_desc_type.py +++ /dev/null @@ -1,168 +0,0 @@ -# Licensed 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. - -""" -Change type of description field in package table. - -Revision ID: 004 -Revises: table package - -""" -from alembic import op -import sqlalchemy as sa - -import murano.db.migration.helpers as helpers -from murano.db.sqla import types as st - -# revision identifiers, used by Alembic. -revision = '004' -down_revision = '003' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=0') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.drop_constraint('package_to_tag_package_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_tag_tag_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_category_package_id_fkey', - 'package_to_category', 'foreignkey') - op.drop_constraint('class_definition_package_id_fkey', - 'class_definition', 'foreignkey') - - helpers.transform_table( - 'package', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('archive', st.LargeBinary(), nullable=True), - sa.Column('fully_qualified_name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('type', sa.String(length=20), nullable=False), - sa.Column('author', sa.String(length=80), nullable=True), - sa.Column('name', sa.String(length=80), nullable=False), - sa.Column('enabled', sa.Boolean(), nullable=True), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('is_public', sa.Boolean(), nullable=True), - sa.Column('logo', st.LargeBinary(), nullable=True), - sa.Column('owner_id', sa.String(length=36), nullable=False), - sa.Column('ui_definition', sa.Text(), nullable=True), - sa.Column('supplier_logo', sa.types.LargeBinary), - sa.Column('supplier', sa.types.Text()), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_index('ix_package_fqn', - 'package', - ['fully_qualified_name']) - - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=1') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.create_foreign_key('package_to_tag_package_id_fkey', - 'package_to_tag', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('package_to_tag_tag_id_fkey', - 'package_to_tag', 'tag', - ['tag_id'], ['id']) - - op.create_foreign_key('package_to_category_package_id_fkey', - 'package_to_category', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('class_definition_package_id_fkey', - 'class_definition', 'package', - ['package_id'], ['id']) - - # end Alembic commands # - - -def downgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=0') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.drop_constraint('package_to_tag_package_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_tag_tag_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_category_package_id_fkey', - 'package_to_category', 'foreignkey') - op.drop_constraint('class_definition_package_id_fkey', - 'class_definition', 'foreignkey') - - helpers.transform_table( - 'package', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('archive', st.LargeBinary(), nullable=True), - sa.Column('fully_qualified_name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('type', sa.String(length=20), nullable=False), - sa.Column('author', sa.String(length=80), nullable=True), - sa.Column('name', sa.String(length=80), nullable=False), - sa.Column('enabled', sa.Boolean(), nullable=True), - sa.Column('description', sa.String(length=512), nullable=False), - sa.Column('is_public', sa.Boolean(), nullable=True), - sa.Column('logo', st.LargeBinary(), nullable=True), - sa.Column('owner_id', sa.String(length=36), nullable=False), - sa.Column('ui_definition', sa.Text(), nullable=True), - sa.Column('supplier_logo', sa.types.LargeBinary), - sa.Column('supplier', sa.types.Text()), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - op.create_index('ix_package_fqn', - 'package', - ['fully_qualified_name']) - - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=1') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.create_foreign_key('package_to_tag_package_id_fkey', - 'package_to_tag', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('package_to_tag_tag_id_fkey', - 'package_to_tag', 'tag', - ['tag_id'], ['id']) - - op.create_foreign_key('package_to_category_package_id_fkey', - 'package_to_category', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('class_definition_package_id_fkey', - 'class_definition', 'package', - ['package_id'], ['id']) - - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/005_environment-template.py b/murano/db/migration/alembic_migrations/versions/005_environment-template.py deleted file mode 100644 index e32888213..000000000 --- a/murano/db/migration/alembic_migrations/versions/005_environment-template.py +++ /dev/null @@ -1,58 +0,0 @@ -# Licensed 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. - -""" -Create the environment-template table for the environment -template functionality. - -Revision ID: 005 -Revises: table template - -""" -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '005' -down_revision = '004' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - """It creates the table environment-template. - - The name plus the tenant_id should be unique in the table, - since each tenant cannot duplicate template names. - """ - - op.create_table( - 'environment-template', - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('name', sa.String(length=255), nullable=False), - sa.Column('tenant_id', sa.String(length=36), nullable=False), - sa.Column('version', sa.BigInteger(), nullable=False), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('networking', sa.Text(), nullable=True), - sa.PrimaryKeyConstraint('id'), - sa.UniqueConstraint('tenant_id', 'name'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - -def downgrade(): - op.drop_table('environment-template') diff --git a/murano/db/migration/alembic_migrations/versions/006_add_task_result.py b/murano/db/migration/alembic_migrations/versions/006_add_task_result.py deleted file mode 100644 index 18977401e..000000000 --- a/murano/db/migration/alembic_migrations/versions/006_add_task_result.py +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed 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. - -"""empty message - -Revision ID: 006 -Revises: None -Create Date: 2015-02-15 12:14:12 - -""" -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '006' -down_revision = '005' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.add_column('task', sa.Column('result', sa.types.Text())) - # end Alembic commands # - - -def downgrade(): - op.drop_column('task', 'result') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/007_add_locks.py b/murano/db/migration/alembic_migrations/versions/007_add_locks.py deleted file mode 100644 index 297e9164f..000000000 --- a/murano/db/migration/alembic_migrations/versions/007_add_locks.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2015 OpenStack Foundation. -# -# Licensed 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 alembic import op -import sqlalchemy as sa - -"""add_locks - -Revision ID: 007 -Revises: 006 -Create Date: 2015-04-08 14:01:06.458512 - -""" - -# revision identifiers, used by Alembic. -revision = '007' -down_revision = '006' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.create_table( - 'locks', - sa.Column('id', sa.String(length=50), nullable=False), - sa.Column('ts', sa.DateTime(), nullable=False), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - - -def downgrade(): - op.drop_table('locks') diff --git a/murano/db/migration/alembic_migrations/versions/008_fix_unique_constraints.py b/murano/db/migration/alembic_migrations/versions/008_fix_unique_constraints.py deleted file mode 100644 index d7f94edb5..000000000 --- a/murano/db/migration/alembic_migrations/versions/008_fix_unique_constraints.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright 2015 OpenStack Foundation. -# -# Licensed 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 alembic import op -import sqlalchemy as sa - -import murano.db.migration.helpers as helpers -from murano.db.sqla import types as st - -"""fix_unique_constraints - -Revision ID: 008 -Revises: 007 -Create Date: 2015-04-08 14:01:06.458512 - -""" - -# revision identifiers, used by Alembic. -revision = '008' -down_revision = '007' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=0') - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.drop_constraint('package_to_tag_package_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_tag_tag_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_category_package_id_fkey', - 'package_to_category', 'foreignkey') - op.drop_constraint('class_definition_package_id_fkey', - 'class_definition', 'foreignkey') - helpers.transform_table( - 'package', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('archive', st.LargeBinary(), nullable=True), - sa.Column('fully_qualified_name', - sa.String(length=128), - nullable=False), - sa.Column('type', sa.String(length=20), nullable=False), - sa.Column('author', sa.String(length=80), nullable=True), - sa.Column('name', sa.String(length=80), nullable=False), - sa.Column('enabled', sa.Boolean(), nullable=True), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('is_public', sa.Boolean(), nullable=True), - sa.Column('logo', st.LargeBinary(), nullable=True), - sa.Column('owner_id', sa.String(length=36), nullable=False), - sa.Column('ui_definition', sa.Text(), nullable=True), - sa.Column('supplier_logo', sa.types.LargeBinary), - sa.Column('supplier', sa.types.Text()), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - helpers.transform_table( - 'class_definition', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('name', - sa.String(length=128), - nullable=False), - sa.Column('package_id', sa.String(length=36), nullable=True), - sa.ForeignKeyConstraint(['package_id'], ['package.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=1') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.create_foreign_key('package_to_tag_package_id_fkey', - 'package_to_tag', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('package_to_tag_tag_id_fkey', - 'package_to_tag', 'tag', - ['tag_id'], ['id']) - - op.create_foreign_key('package_to_category_package_id_fkey', - 'package_to_category', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('class_definition_package_id_fkey', - 'class_definition', 'package', - ['package_id'], ['id']) - - op.create_index('ix_package_fqn_and_owner', table_name='package', - columns=['fully_qualified_name', 'owner_id'], unique=True) - op.create_index('ix_class_definition_name', - 'class_definition', - ['name']) - - -def downgrade(): - op.drop_index('ix_package_fqn_and_owner', table_name='package') - op.drop_index('ix_class_definition_name', table_name='class_definition') - - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=0') - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.drop_constraint('package_to_tag_package_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_tag_tag_id_fkey', - 'package_to_tag', 'foreignkey') - op.drop_constraint('package_to_category_package_id_fkey', - 'package_to_category', 'foreignkey') - op.drop_constraint('class_definition_package_id_fkey', - 'class_definition', 'foreignkey') - - helpers.transform_table( - 'class_definition', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('package_id', sa.String(length=36), nullable=True), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - helpers.transform_table( - 'package', {}, {}, - sa.Column('created', sa.DateTime(), nullable=False), - sa.Column('updated', sa.DateTime(), nullable=False), - sa.Column('id', sa.String(length=36), nullable=False), - sa.Column('archive', st.LargeBinary(), nullable=True), - sa.Column('fully_qualified_name', - sa.String(length=128), - nullable=False, - unique=True), - sa.Column('type', sa.String(length=20), nullable=False), - sa.Column('author', sa.String(length=80), nullable=True), - sa.Column('name', sa.String(length=80), nullable=False), - sa.Column('enabled', sa.Boolean(), nullable=True), - sa.Column('description', sa.Text(), nullable=False), - sa.Column('is_public', sa.Boolean(), nullable=True), - sa.Column('logo', st.LargeBinary(), nullable=True), - sa.Column('owner_id', sa.String(length=36), nullable=False), - sa.Column('ui_definition', sa.Text(), nullable=True), - sa.Column('supplier_logo', sa.types.LargeBinary), - sa.Column('supplier', sa.types.Text()), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET - ) - - if engine.dialect.dialect_description.startswith('mysql'): - engine.execute('SET FOREIGN_KEY_CHECKS=1') - - if engine.dialect.dialect_description == 'postgresql+psycopg2': - op.create_foreign_key('package_to_tag_package_id_fkey', - 'package_to_tag', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('package_to_tag_tag_id_fkey', - 'package_to_tag', 'tag', - ['tag_id'], ['id']) - - op.create_foreign_key('package_to_category_package_id_fkey', - 'package_to_category', 'package', - ['package_id'], ['id']) - - op.create_foreign_key('class_definition_package_id_fkey', - 'class_definition', 'package', - ['package_id'], ['id']) - - op.create_index('ix_class_definition_name', - 'class_definition', - ['name']) diff --git a/murano/db/migration/alembic_migrations/versions/009_add_cloudfoundry_connections.py b/murano/db/migration/alembic_migrations/versions/009_add_cloudfoundry_connections.py deleted file mode 100644 index c491251a6..000000000 --- a/murano/db/migration/alembic_migrations/versions/009_add_cloudfoundry_connections.py +++ /dev/null @@ -1,67 +0,0 @@ -# Licensed 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. - -""" -Revision ID: 009 -Revises: None -Create Date: 2015-08-17 16:34:33.698760 - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '009' -down_revision = '008' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.create_table( - 'cf_orgs', - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('tenant', sa.String(length=255), nullable=False), - sa.UniqueConstraint('tenant'), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - - op.create_table( - 'cf_spaces', - sa.Column('id', sa.String(length=255), nullable=False), - sa.Column('environment_id', sa.String(length=255), nullable=False), - sa.ForeignKeyConstraint(['environment_id'], ['environment.id'], ), - sa.PrimaryKeyConstraint('id'), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - - op.create_table( - 'cf_serv_inst', - sa.Column('id', sa.String(length=255), primary_key=True), - sa.Column('service_id', sa.String(255), nullable=False), - sa.Column('environment_id', sa.String(255), nullable=False), - sa.Column('tenant', sa.String(255), nullable=False), - sa.ForeignKeyConstraint(['environment_id'], ['environment.id'],), - mysql_engine=MYSQL_ENGINE, - mysql_charset=MYSQL_CHARSET) - # end Alembic commands # - - -def downgrade(): - op.drop_table('cf_orgs') - op.drop_table('cf_spaces') - op.drop_table('cf_serv_inst') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/010_remove_unused_networking_column.py b/murano/db/migration/alembic_migrations/versions/010_remove_unused_networking_column.py deleted file mode 100644 index f426bf757..000000000 --- a/murano/db/migration/alembic_migrations/versions/010_remove_unused_networking_column.py +++ /dev/null @@ -1,43 +0,0 @@ -# Licensed 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. - -""" -Revision ID: 010 -Revises: None -Create Date: 2015-09-08 00:00:00.698760 - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '010' -down_revision = '009' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - with op.batch_alter_table("environment") as batch_op: - batch_op.drop_column('networking') - with op.batch_alter_table("environment-template") as batch_op2: - batch_op2.drop_column('networking') - - -def downgrade(): - op.add_column('environment', sa.Column('networking', sa.Text(), - nullable=True)) - op.add_column('environment-template', sa.Column('networking', sa.Text(), - nullable=True)) diff --git a/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py b/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py deleted file mode 100644 index 2a18ff366..000000000 --- a/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py +++ /dev/null @@ -1,43 +0,0 @@ -# Licensed 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. - -""" -Add the is_public column to the environment-template for public -environment template functionality. - -Revision ID: 011 -Revises: table template - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '011' -down_revision = '010' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.add_column('environment-template', - sa.Column('is_public', sa.Boolean(), - default=False, nullable=True)) - # end Alembic commands # - - -def downgrade(): - op.drop_column('environment-template', 'is_public') - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/012_support_domain_users.py b/murano/db/migration/alembic_migrations/versions/012_support_domain_users.py deleted file mode 100644 index a81f1d491..000000000 --- a/murano/db/migration/alembic_migrations/versions/012_support_domain_users.py +++ /dev/null @@ -1,47 +0,0 @@ -# Licensed 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. - -""" -Change sizes of columns that hold keystone user ID to support domain users -which are 64 characters long. - -Revision ID: 012 -Revises: table session, table package - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '012' -down_revision = '011' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - with op.batch_alter_table("session") as batch_op: - batch_op.alter_column('user_id', type_=sa.String(64), nullable=False) - with op.batch_alter_table("package") as batch_op2: - batch_op2.alter_column('owner_id', type_=sa.String(64), nullable=False) - # end Alembic commands # - - -def downgrade(): - with op.batch_alter_table("session") as batch_op: - batch_op.alter_column('user_id', type_=sa.String(36), nullable=False) - with op.batch_alter_table("package") as batch_op2: - batch_op2.alter_column('owner_id', type_=sa.String(36), nullable=False) - # end Alembic commands # diff --git a/murano/db/migration/alembic_migrations/versions/013_increase_description_text_size.py b/murano/db/migration/alembic_migrations/versions/013_increase_description_text_size.py deleted file mode 100644 index b6c0ce469..000000000 --- a/murano/db/migration/alembic_migrations/versions/013_increase_description_text_size.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2016 OpenStack Foundation. -# -# Licensed 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. - -"""Increase the size of the text columns storing object model - -Revision ID: 013 -Revises: 012 -Create Date: 2016-04-12 15:46:12.251155 - -""" - -from alembic import op -import sqlalchemy as sa -import sqlalchemy.dialects.mysql as sa_mysql - -# revision identifiers, used by Alembic. -revision = '013' -down_revision = '012' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - with op.batch_alter_table('session') as batch_op: - batch_op.alter_column('description', - type_=sa_mysql.LONGTEXT()) - with op.batch_alter_table('environment') as batch_op: - batch_op.alter_column('description', - type_=sa_mysql.LONGTEXT()) - with op.batch_alter_table('environment-template') as batch_op: - batch_op.alter_column('description', - type_=sa_mysql.LONGTEXT()) - - -def downgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - with op.batch_alter_table('session') as batch_op: - batch_op.alter_column('description', - type_=sa.TEXT()) - with op.batch_alter_table('environment') as batch_op: - batch_op.alter_column('description', - type_=sa.TEXT()) - with op.batch_alter_table('environment-template') as batch_op: - batch_op.alter_column('description', - type_=sa.TEXT()) diff --git a/murano/db/migration/alembic_migrations/versions/014_increase_status_time_resolution.py b/murano/db/migration/alembic_migrations/versions/014_increase_status_time_resolution.py deleted file mode 100644 index 9cd8043b6..000000000 --- a/murano/db/migration/alembic_migrations/versions/014_increase_status_time_resolution.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright 2016 OpenStack Foundation. -# -# Licensed 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. - -"""Increase time resolution for status reports - -Revision ID: 014 -Create Date: 2016-04-28 - -""" - -from alembic import op -import sqlalchemy.dialects.mysql as sa_mysql - -# revision identifiers, used by Alembic. -revision = '014' -down_revision = '013' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def _check_dbms(engine): - dialect = engine.dialect.dialect_description - version = engine.dialect.server_version_info - if dialect.startswith('mysql') and version >= (5, 6, 4): - return True - if 'MariaDB' in version and version >= (5, 3): - return True - return False - - -def upgrade(): - engine = op.get_bind() - if _check_dbms(engine): - with op.batch_alter_table('status') as batch_op: - batch_op.alter_column( - 'created', type_=sa_mysql.DATETIME(fsp=6), nullable=False) - batch_op.alter_column( - 'updated', type_=sa_mysql.DATETIME(fsp=6), nullable=False) - - -def downgrade(): - engine = op.get_bind() - if _check_dbms(engine): - with op.batch_alter_table('status') as batch_op: - batch_op.alter_column( - 'created', type_=sa_mysql.DATETIME(), nullable=False) - batch_op.alter_column( - 'updated', type_=sa_mysql.DATETIME(), nullable=False) diff --git a/murano/db/migration/alembic_migrations/versions/015_adding_text_description.py b/murano/db/migration/alembic_migrations/versions/015_adding_text_description.py deleted file mode 100644 index c41c5536f..000000000 --- a/murano/db/migration/alembic_migrations/versions/015_adding_text_description.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2016 OpenStack Foundation. -# -# Licensed 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. - -"""Increase time resolution for status reports - -Revision ID: 015 -Create Date: 2016-06-17 - -""" - -from alembic import op -import sqlalchemy as sa - -# revision identifiers, used by Alembic. -revision = '015' -down_revision = '014' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - op.add_column('environment', sa.Column('description_text', sa.Text(), - nullable=True)) - op.add_column('environment-template', sa.Column('description_text', - sa.Text(), - nullable=True)) - - -def downgrade(): - with op.batch_alter_table("environment") as batch_op: - batch_op.drop_column('description_text') - with op.batch_alter_table("environment-template") as batch_op2: - batch_op2.drop_column('description_text') diff --git a/murano/db/migration/alembic_migrations/versions/016_increase_task_description_text_size.py b/murano/db/migration/alembic_migrations/versions/016_increase_task_description_text_size.py deleted file mode 100644 index dcd916be2..000000000 --- a/murano/db/migration/alembic_migrations/versions/016_increase_task_description_text_size.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2016 OpenStack Foundation. -# -# Licensed 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. - -"""Increase the size of the text columns storing object model in the task table - -Revision ID: 016 -Revises: 015 -Create Date: 2016-08-30 10:45:00 - -""" - -from alembic import op -import sqlalchemy as sa -import sqlalchemy.dialects.mysql as sa_mysql - -# revision identifiers, used by Alembic. -revision = '016' -down_revision = '015' - -MYSQL_ENGINE = 'InnoDB' -MYSQL_CHARSET = 'utf8' - - -def upgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - with op.batch_alter_table('task') as batch_op: - batch_op.alter_column('description', - type_=sa_mysql.LONGTEXT()) - - -def downgrade(): - engine = op.get_bind() - if engine.dialect.dialect_description.startswith('mysql'): - with op.batch_alter_table('task') as batch_op: - batch_op.alter_column('description', - type_=sa.TEXT()) diff --git a/murano/db/migration/helpers.py b/murano/db/migration/helpers.py deleted file mode 100644 index 8acd8c0a8..000000000 --- a/murano/db/migration/helpers.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 alembic import op -import sqlalchemy as sa - - -def transform_table(name, renames, defaults, *columns, **kw): - def escape(val): - if isinstance(val, str): - return "'{0}'".format(val) - elif val is None: - return 'NULL' - else: - return val - - engine = op.get_bind() - meta = sa.MetaData(bind=engine) - meta.reflect() - new_name = name + '__tmp' - old_table = meta.tables[name] - mapping = dict( - (renames.get(col.name, col.name), col.name) for col in old_table.c - ) - - columns_to_select = [ - old_table.c[mapping[c.name]] - if c.name in mapping else escape(defaults.get(c.name)) - for c in columns if isinstance(c, sa.Column) - ] - select_as = [ - c.name for c in columns if isinstance(c, sa.Column) - ] - select = sa.sql.select(columns_to_select) - - op.create_table(new_name, *columns, **kw) - meta.reflect() - new_table = meta.tables[new_name] - insert = sa.sql.insert(new_table) - if engine.dialect.dialect_description == 'postgresql+psycopg2': - insert = insert.returning(next(iter(new_table.primary_key.columns))) - insert = insert.from_select(select_as, select) - engine.execute(insert) - op.drop_table(name) - op.rename_table(new_name, name) diff --git a/murano/db/migration/migration.py b/murano/db/migration/migration.py deleted file mode 100644 index 21444be67..000000000 --- a/murano/db/migration/migration.py +++ /dev/null @@ -1,86 +0,0 @@ -# Licensed 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 os - -import alembic -from alembic import config as alembic_config -from alembic import migration as alembic_migration - -from murano.db import session as db_session - - -def get_alembic_config(): - path = os.path.join(os.path.dirname(__file__), 'alembic.ini') - - config = alembic_config.Config(path) - config.set_main_option('script_location', - 'murano.db.migration:alembic_migrations') - return config - - -def version(engine=None): - """Returns current database version.""" - engine = engine or db_session.get_engine() - with engine.connect() as conn: - context = alembic_migration.MigrationContext.configure(conn) - return context.get_current_revision() - - -def upgrade(revision, config=None): - """Used for upgrading database. - - :param version: Desired database version - :type version: string - """ - revision = revision or 'head' - config = config or get_alembic_config() - - alembic.command.upgrade(config, revision) - - -def downgrade(revision, config=None): - """Used for downgrading database. - - :param version: Desired database version7 - :type version: string - """ - revision = revision or 'base' - config = config or get_alembic_config() - return alembic.command.downgrade(config, revision) - - -def stamp(revision, config=None): - """Stamps database with provided revision. - - Don't run any migrations. - - :param revision: Should match one from repository or head - to stamp - database with most recent revision - :type revision: string - """ - config = config or get_alembic_config() - return alembic.command.stamp(config, revision=revision) - - -def revision(message=None, autogenerate=False, config=None): - """Creates template for migration. - - :param message: Text that will be used for migration title - :type message: string - :param autogenerate: If True - generates diff based on current database - state - :type autogenerate: bool - """ - config = config or get_alembic_config() - return alembic.command.revision(config, message=message, - autogenerate=autogenerate) diff --git a/murano/db/models.py b/murano/db/models.py deleted file mode 100644 index a9f03f4a3..000000000 --- a/murano/db/models.py +++ /dev/null @@ -1,380 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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. - -""" -SQLAlchemy models for murano data -""" -from oslo_db.sqlalchemy import models -from oslo_utils import timeutils -import sqlalchemy as sa -from sqlalchemy.ext import declarative -from sqlalchemy import orm as sa_orm - -from murano.common import uuidutils -from murano.db.sqla import types as st - - -class TimestampMixin(object): - __protected_attributes__ = set(["created", "updated"]) - - created = sa.Column(sa.DateTime, default=timeutils.utcnow, - nullable=False) - updated = sa.Column(sa.DateTime, default=timeutils.utcnow, - nullable=False, onupdate=timeutils.utcnow) - - def update(self, values): - """dict.update() behaviour.""" - self.updated = timeutils.utcnow() - super(TimestampMixin, self).update(values) - - def __setitem__(self, key, value): - self.updated = timeutils.utcnow() - super(TimestampMixin, self).__setitem__(key, value) - - -class _MuranoBase(models.ModelBase): - def to_dict(self): - dictionary = self.__dict__.copy() - return dict((k, v) for k, v in dictionary.items() - if k != '_sa_instance_state') - - -Base = declarative.declarative_base(cls=_MuranoBase) - - -class Environment(Base, TimestampMixin): - """Represents an Environment in the metadata-store.""" - __tablename__ = 'environment' - __table_args__ = (sa.Index( - 'ix_name_tenant_id', 'name', 'tenant_id', unique=True),) - - id = sa.Column(sa.String(255), - primary_key=True, - default=uuidutils.generate_uuid) - name = sa.Column(sa.String(255), nullable=False) - tenant_id = sa.Column(sa.String(36), nullable=False) - description_text = sa.Column(sa.String(), nullable=False, default='') - version = sa.Column(sa.BigInteger, nullable=False, default=0) - description = sa.Column(st.JsonBlob(), nullable=False, default={}) - - sessions = sa_orm.relationship("Session", backref='environment', - cascade='save-update, merge, delete') - - tasks = sa_orm.relationship('Task', backref='environment', - cascade='save-update, merge, delete') - - cf_spaces = sa_orm.relationship("CFSpace", backref='environment', - cascade='save-update, merge, delete') - - cf_serv_inst = sa_orm.relationship("CFServiceInstance", - backref='environment', - cascade='save-update, merge, delete') - - def to_dict(self): - dictionary = super(Environment, self).to_dict() - del dictionary['description'] - return dictionary - - -class EnvironmentTemplate(Base, TimestampMixin): - """Represents an Environment Template in the metadata-store.""" - __tablename__ = 'environment-template' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - name = sa.Column(sa.String(255), nullable=False) - tenant_id = sa.Column(sa.String(36), nullable=False) - description_text = sa.Column(sa.String(), nullable=False, default='') - version = sa.Column(sa.BigInteger, nullable=False, default=0) - description = sa.Column(st.JsonBlob(), nullable=False, default={}) - is_public = sa.Column(sa.Boolean, default=False) - - def to_dict(self): - dictionary = super(EnvironmentTemplate, self).to_dict() - if 'description' in dictionary: - del dictionary['description'] - return dictionary - - -class Session(Base, TimestampMixin): - __tablename__ = 'session' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id')) - - user_id = sa.Column(sa.String(64), nullable=False) - state = sa.Column(sa.String(36), nullable=False) - description = sa.Column(st.JsonBlob(), nullable=False) - version = sa.Column(sa.BigInteger, nullable=False, default=0) - - def to_dict(self): - dictionary = super(Session, self).to_dict() - del dictionary['description'] - # object relations may be not loaded yet - if 'environment' in dictionary: - del dictionary['environment'] - return dictionary - - -class Task(Base, TimestampMixin): - __tablename__ = 'task' - - id = sa.Column(sa.String(36), primary_key=True, - default=uuidutils.generate_uuid) - started = sa.Column(sa.DateTime, default=timeutils.utcnow, nullable=False) - finished = sa.Column(sa.DateTime, default=None, nullable=True) - description = sa.Column(st.JsonBlob(), nullable=False) - environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id')) - action = sa.Column(st.JsonBlob()) - - statuses = sa_orm.relationship("Status", backref='task', - cascade='save-update, merge, delete') - result = sa.Column(st.JsonBlob(), nullable=True, default={}) - - def to_dict(self): - dictionary = super(Task, self).to_dict() - if 'statuses' in dictionary: - del dictionary['statuses'] - if 'environment' in dictionary: - del dictionary['environment'] - return dictionary - - -class Status(Base, TimestampMixin): - __tablename__ = 'status' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - entity_id = sa.Column(sa.String(255), nullable=True) - entity = sa.Column(sa.String(10), nullable=True) - task_id = sa.Column(sa.String(32), sa.ForeignKey('task.id')) - text = sa.Column(sa.Text(), nullable=False) - level = sa.Column(sa.String(32), nullable=False) - details = sa.Column(sa.Text(), nullable=True) - - def to_dict(self): - dictionary = super(Status, self).to_dict() - # object relations may be not loaded yet - if 'deployment' in dictionary: - del dictionary['deployment'] - return dictionary - - -class ApiStats(Base, TimestampMixin): - __tablename__ = 'apistats' - - id = sa.Column(sa.Integer(), primary_key=True) - host = sa.Column(sa.String(80)) - request_count = sa.Column(sa.BigInteger()) - error_count = sa.Column(sa.BigInteger()) - average_response_time = sa.Column(sa.Float()) - requests_per_tenant = sa.Column(sa.Text()) - requests_per_second = sa.Column(sa.Float()) - errors_per_second = sa.Column(sa.Float()) - cpu_count = sa.Column(sa.Integer()) - cpu_percent = sa.Column(sa.Float()) - - -package_to_category = sa.Table('package_to_category', - Base.metadata, - sa.Column('package_id', - sa.String(36), - sa.ForeignKey('package.id')), - sa.Column('category_id', - sa.String(36), - sa.ForeignKey('category.id', - ondelete="RESTRICT"))) - -package_to_tag = sa.Table('package_to_tag', - Base.metadata, - sa.Column('package_id', - sa.String(36), - sa.ForeignKey('package.id')), - sa.Column('tag_id', - sa.String(36), - sa.ForeignKey('tag.id', - ondelete="CASCADE"))) - - -class Instance(Base): - __tablename__ = 'instance_stats' - - environment_id = sa.Column( - sa.String(255), primary_key=True, nullable=False) - instance_id = sa.Column( - sa.String(255), primary_key=True, nullable=False) - instance_type = sa.Column(sa.Integer, default=0, nullable=False) - created = sa.Column(sa.Integer, nullable=False) - destroyed = sa.Column(sa.Integer, nullable=True) - type_name = sa.Column('type_name', sa.String(512), nullable=False) - type_title = sa.Column('type_title', sa.String(512)) - unit_count = sa.Column('unit_count', sa.Integer()) - tenant_id = sa.Column('tenant_id', sa.String(36), nullable=False) - - -class Package(Base, TimestampMixin): - """Represents a meta information about application package.""" - __tablename__ = 'package' - __table_args__ = (sa.Index('ix_package_fqn_and_owner', - 'fully_qualified_name', - 'owner_id', unique=True),) - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - archive = sa.Column(st.LargeBinary()) - fully_qualified_name = sa.Column(sa.String(128), - nullable=False) - type = sa.Column(sa.String(20), nullable=False, default='class') - author = sa.Column(sa.String(80), default='OpenStack') - supplier = sa.Column(st.JsonBlob(), nullable=True, default={}) - name = sa.Column(sa.String(80), nullable=False) - enabled = sa.Column(sa.Boolean, default=True) - description = sa.Column(sa.Text(), - nullable=False, - default='The description is not provided') - is_public = sa.Column(sa.Boolean, default=False) - tags = sa_orm.relationship("Tag", - secondary=package_to_tag, - cascade='save-update, merge', - lazy='joined') - logo = sa.Column(st.LargeBinary(), nullable=True) - owner_id = sa.Column(sa.String(64), nullable=False) - ui_definition = sa.Column(sa.Text) - supplier_logo = sa.Column(sa.LargeBinary, nullable=True) - categories = sa_orm.relationship("Category", - secondary=package_to_category, - cascade='save-update, merge', - lazy='joined') - class_definitions = sa_orm.relationship( - "Class", cascade='save-update, merge, delete', lazy='joined', - backref='package') - - def to_dict(self): - d = self.__dict__.copy() - not_serializable = ['_sa_instance_state', - 'archive', - 'logo', - 'ui_definition', - 'supplier_logo'] - nested_objects = ['categories', 'tags', 'class_definitions'] - for key in not_serializable: - if key in d.keys(): - del d[key] - for key in nested_objects: - d[key] = [a.name for a in d.get(key, [])] - return d - - -class Category(Base, TimestampMixin): - """Represents an application categories in the datastore.""" - __tablename__ = 'category' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - name = sa.Column(sa.String(80), nullable=False, index=True, unique=True) - - package_count = sa_orm.column_property( - sa.select([sa.func.count(package_to_category.c.package_id)]). - where(package_to_category.c.category_id == id). - correlate_except(package_to_category) - ) - - def to_dict(self): - d = super(Category, self).to_dict() - d['package_count'] = self.package_count - return d - - -class Tag(Base, TimestampMixin): - """Represents tags in the datastore.""" - __tablename__ = 'tag' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - name = sa.Column(sa.String(80), nullable=False, unique=True) - - -class Class(Base, TimestampMixin): - """Represents a class definition in the datastore.""" - __tablename__ = 'class_definition' - - id = sa.Column(sa.String(36), - primary_key=True, - default=uuidutils.generate_uuid) - name = sa.Column(sa.String(128), nullable=False, index=True) - package_id = sa.Column(sa.String(36), sa.ForeignKey('package.id')) - - -class Lock(Base): - __tablename__ = 'locks' - id = sa.Column(sa.String(50), primary_key=True) - ts = sa.Column(sa.DateTime, nullable=False) - - -class CFOrganization(Base): - __tablename__ = "cf_orgs" - id = sa.Column(sa.String(255), primary_key=True) - tenant = sa.Column(sa.String(255), nullable=False) - - -class CFSpace(Base): - __tablename__ = "cf_spaces" - id = sa.Column(sa.String(255), primary_key=True) - environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id'), - nullable=False) - - def to_dict(self): - dictionary = super(CFSpace, self).to_dict() - if 'environment' in dictionary: - del dictionary['environment'] - return dictionary - - -class CFServiceInstance(Base): - __tablename__ = 'cf_serv_inst' - id = sa.Column(sa.String(255), primary_key=True) - service_id = sa.Column(sa.String(255), nullable=False) - environment_id = sa.Column(sa.String(255), sa.ForeignKey('environment.id'), - nullable=False) - tenant = sa.Column(sa.String(255), nullable=False) - - def to_dict(self): - dictionary = super(CFServiceInstance, self).to_dict() - if 'environment' in dictionary: - del dictionary['environment'] - return dictionary - - -def register_models(engine): - """Creates database tables for all models with the given engine.""" - models = (Environment, Status, Session, Task, - ApiStats, Package, Category, Class, Instance, Lock, CFSpace, - CFOrganization) - for model in models: - model.metadata.create_all(engine) - - -def unregister_models(engine): - """Drops database tables for all models with the given engine.""" - models = (Environment, Status, Session, Task, - ApiStats, Package, Category, Class, Lock, CFOrganization, - CFSpace) - for model in models: - model.metadata.drop_all(engine) diff --git a/murano/db/services/__init__.py b/murano/db/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/services/actions.py b/murano/db/services/actions.py deleted file mode 100644 index 0eea4f6cd..000000000 --- a/murano/db/services/actions.py +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed 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 murano.common.helpers import token_sanitizer -from murano.db import models -from murano.services import states - - -def get_environment(session, unit): - environment = unit.query(models.Environment).get( - session.environment_id) - return environment - - -def update_task(action, session, task, unit): - objects = session.description.get('Objects', None) - session.state = states.SessionState.DELETING if objects is None \ - else states.SessionState.DEPLOYING - task_info = models.Task() - task_info.environment_id = session.environment_id - if objects: - task_info.description = token_sanitizer.TokenSanitizer().sanitize( - dict(session.description.get('Objects'))) - task_info.action = task['action'] - status = models.Status() - status.text = 'Action {0} is scheduled'.format(action) - status.level = 'info' - task_info.statuses.append(status) - with unit.begin(): - unit.add(session) - unit.add(task_info) - return task_info.id diff --git a/murano/db/services/cf_connections.py b/murano/db/services/cf_connections.py deleted file mode 100644 index c264aae1c..000000000 --- a/murano/db/services/cf_connections.py +++ /dev/null @@ -1,90 +0,0 @@ -# Licensed 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 oslo_db import exception -import sqlalchemy - -from murano.db import cfapi_models as models -from murano.db import session as db_session - - -def set_tenant_for_org(cf_org_id, tenant): - """Store tenant-org link to db""" - unit = db_session.get_session() - try: - with unit.begin(): - org = models.CFOrganization() - org.id = cf_org_id - org.tenant = tenant - unit.add(org) - except exception.DBDuplicateEntry: - unit.execute(sqlalchemy.update(models.CFOrganization).where( - models.CFOrganization.id == cf_org_id).values( - tenant=tenant)) - - -def set_environment_for_space(cf_space_id, environment_id): - """Store env-space link to db""" - unit = db_session.get_session() - try: - with unit.begin(): - space = models.CFSpace() - space.id = cf_space_id - space.environment_id = environment_id - unit.add(space) - except exception.DBDuplicateEntry: - unit.execute(sqlalchemy.update(models.CFSpace).where( - models.CFSpace.id == cf_space_id).values( - environment_id=environment_id)) - - -def set_instance_for_service(instance_id, service_id, environment_id, tenant): - """Store env-space link to db""" - unit = db_session.get_session() - try: - with unit.begin(): - connection = models.CFServiceInstance() - connection.id = instance_id - connection.service_id = service_id - connection.environment_id = environment_id - connection.tenant = tenant - unit.add(connection) - except exception.DBDuplicateEntry: - unit.execute(sqlalchemy.update(models.CFServiceInstance).where( - models.CFServiceInstance.id == instance_id).values( - environment_id=environment_id)) - - -def get_environment_for_space(cf_space_id): - """Take env id related to space from db""" - unit = db_session.get_session() - connection = unit.query(models.CFSpace).get(cf_space_id) - return connection.environment_id - - -def get_tenant_for_org(cf_org_id): - """Take tenant id related to org from db""" - unit = db_session.get_session() - connection = unit.query(models.CFOrganization).get(cf_org_id) - return connection.tenant - - -def get_service_for_instance(instance_id): - unit = db_session.get_session() - connection = unit.query(models.CFServiceInstance).get(instance_id) - return connection - - -def delete_environment_from_space(environment_id): - unit = db_session.get_session() - unit.query(models.CFSpace).filter( - models.CFSpace.environment_id == environment_id).delete() diff --git a/murano/db/services/core_services.py b/murano/db/services/core_services.py deleted file mode 100644 index 3f1ca40be..000000000 --- a/murano/db/services/core_services.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_log import log as logging -from oslo_utils import timeutils -from webob import exc - -from murano.common.i18n import _ -from murano.common import utils -from murano.db.services import environment_templates as env_temp -from murano.db.services import environments as envs - -LOG = logging.getLogger(__name__) - - -class CoreServices(object): - @staticmethod - def get_service_status(environment_id, service_id): - """Service can have one of three distinguished statuses: - - - Deploying: if environment has status deploying and there is at least - one orchestration engine report for this service; - - Pending: if environment has status `deploying` and there is no - report from orchestration engine about this service; - - Ready: If environment has status ready. - - :param environment_id: Service environment, we always know to which - environment service belongs to - :param service_id: Id of service for which we checking status. - :return: Service status - """ - # Now we assume that service has same status as environment. - # TODO(ruhe): implement as designed and described above - - return envs.EnvironmentServices.get_status(environment_id) - - @staticmethod - def get_data(environment_id, path, session_id=None): - get_description = envs.EnvironmentServices.get_environment_description - - env_description = get_description(environment_id, session_id) - - if env_description is None: - return None - - if 'services' not in env_description: - return [] - - result = utils.TraverseHelper.get(path, env_description) - - if path == '/services': - get_status = CoreServices.get_service_status - for srv in result: - srv['?']['status'] = get_status(environment_id, srv['?']['id']) - - return result - - @staticmethod - def get_template_data(env_template_id, path): - """It obtains the data for the template. - - It includes all the services. In case the path includes information - such as the env_template_id, the information provided will - be related to the entity specified in the path - - :param env_template_id: The env_template_id to obtain the data - :param path: Id of service for which we checking status. - :return: The template description - """ - temp_description = env_temp.EnvTemplateServices.\ - get_description(env_template_id) - - if temp_description is None: - return None - - if 'services' not in temp_description: - return [] - - result = utils.TraverseHelper.get(path, temp_description) - if result is None: - msg = _('Environment Template is not found').format( - id=env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - return result - - @staticmethod - def post_env_template_data(env_template_id, data, path): - """It stores the template data inside the template description. - - :param env_template_id: The env_template_id to obtain the data - :param data: the template description - :param path: Id of service for which we checking status. - :return: The template description - """ - get_description = env_temp.EnvTemplateServices.get_description - save_description = env_temp.EnvTemplateServices.save_description - - temp_description = get_description(env_template_id) - if temp_description is None: - msg = _('Environment Template is not found').format( - id=env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if 'services' not in temp_description: - temp_description['services'] = [] - - if path == '/services': - if isinstance(data, list): - utils.TraverseHelper.extend(path, data, temp_description) - else: - utils.TraverseHelper.insert(path, data, temp_description) - save_description(temp_description) - return data - - @staticmethod - def post_application_data(env_template_id, data, path): - """It stores the application data inside the template description. - - :param env_template_id: The env_template_id to obtain the data - :param data: the template description - :param path: Id of service for which we checking status. - :return: The template description - """ - get_description = env_temp.EnvTemplateServices.get_description - save_description = env_temp.EnvTemplateServices.save_description - - temp_description = get_description(env_template_id) - if temp_description is None: - msg = _('Environment Template is not found').format( - env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if 'services' not in temp_description: - temp_description['services'] = [] - - if path == '/services': - if isinstance(data, list): - utils.TraverseHelper.extend(path, data, temp_description) - else: - utils.TraverseHelper.insert(path, data, temp_description) - - save_description(temp_description, env_template_id) - - return data - - @staticmethod - def post_data(environment_id, session_id, data, path): - get_description = envs.EnvironmentServices.get_environment_description - save_description = envs.EnvironmentServices.\ - save_environment_description - - env_description = get_description(environment_id, session_id) - if env_description is None: - msg = _('Environment is not found').format( - environment_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if 'services' not in env_description: - env_description['services'] = [] - - if path == '/services': - if isinstance(data, list): - utils.TraverseHelper.extend(path, data, env_description) - else: - utils.TraverseHelper.insert(path, data, env_description) - - save_description(session_id, env_description) - - return data - - @staticmethod - def put_data(environment_id, session_id, data, path): - get_description = envs.EnvironmentServices.get_environment_description - save_description = envs.EnvironmentServices.\ - save_environment_description - - env_description = get_description(environment_id, session_id) - - utils.TraverseHelper.update(path, data, env_description) - env_description['?']['updated'] = str(timeutils.utcnow()) - - save_description(session_id, env_description) - - return data - - @staticmethod - def delete_data(environment_id, session_id, path): - get_description = envs.EnvironmentServices.get_environment_description - save_description = envs.EnvironmentServices.\ - save_environment_description - - env_description = get_description(environment_id, session_id) - - utils.TraverseHelper.remove(path, env_description) - save_description(session_id, env_description) - - @staticmethod - def delete_env_template_data(env_template_id, path): - """It deletes a template. - - :param env_template_id: The env_template_id to be deleted. - :param path: The path to check. - """ - get_description = env_temp.EnvTemplateServices.get_description - save_description = env_temp.EnvTemplateServices.save_description - - tmp_description = get_description(env_template_id) - if tmp_description is None: - msg = _('Environment Template is not found').format( - env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - utils.TraverseHelper.remove(path, tmp_description) - save_description(tmp_description, env_template_id) - return tmp_description - - @staticmethod - def put_application_data(env_template_id, data, path): - """It stores the application data inside the template description. - - :param env_template_id: The env_template_id to obtain the data - :param data: the template description - :param path: Id of service for which we checking status. - :return: The template description - """ - get_description = env_temp.EnvTemplateServices.get_description - save_description = env_temp.EnvTemplateServices.save_description - - temp_description = get_description(env_template_id) - if temp_description is None: - msg = _('Environment Template is not found').format( - env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - count = 0 - id = path[1:].split('/')[-1] - for service in temp_description["services"]: - if service["?"]["id"]: - if service["?"]["id"] == id: - break - count + 1 - - utils.TraverseHelper.update("services/{0}".format(count), - data, temp_description) - temp_description['updated'] = str(timeutils.utcnow()) - save_description(temp_description, env_template_id) - return data diff --git a/murano/db/services/environment_templates.py b/murano/db/services/environment_templates.py deleted file mode 100644 index c9fa8567f..000000000 --- a/murano/db/services/environment_templates.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D. -# -# Licensed 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 murano.common.i18n import _ -from murano.common import uuidutils -from murano.db import models -from murano.db import session as db_session - -from oslo_db import exception as db_exc -from oslo_log import log as logging -from sqlalchemy.sql import or_ - -LOG = logging.getLogger(__name__) - - -class EnvTemplateServices(object): - @staticmethod - def get_env_templates_by(filters): - """Returns list of environment-templates. - - :param filters: property filters - :return: Returns list of environment-templates - """ - - unit = db_session.get_session() - templates = unit.query(models.EnvironmentTemplate). \ - filter_by(**filters).all() - - return templates - - @staticmethod - def get_env_templates_or_by(filters): - """Returns list of environment-templates. - - :param filters: property filters - :return: Returns list of environment-templates - """ - unit = db_session.get_session() - templates = unit.query(models.EnvironmentTemplate). \ - filter(or_(*filters)).all() - - return templates - - @staticmethod - def create(env_template_params, tenant_id): - """Creates environment-template with specified params, - in particular - name. - - :param env_template_params: Dict, e.g. {'name': 'temp-name'} - :param tenant_id: Tenant Id - :return: Created Template - """ - - env_template_params['id'] = uuidutils.generate_uuid() - env_template_params['tenant_id'] = tenant_id - env_template = models.EnvironmentTemplate() - env_template.update(env_template_params) - - unit = db_session.get_session() - with unit.begin(): - try: - unit.add(env_template) - except db_exc.DBDuplicateEntry: - msg = _('Environment template specified name already exists') - LOG.error(msg) - raise db_exc.DBDuplicateEntry(explanation=msg) - env_template.update({'description': env_template_params}) - env_template.save(unit) - - return env_template - - @staticmethod - def delete(env_template_id): - """Deletes template. - - :param env_template_id: Template that is going to be deleted - """ - - env_temp_description = EnvTemplateServices.get_description( - env_template_id) - env_temp_description['description'] = None - EnvTemplateServices.save_description( - env_temp_description, env_template_id) - - @staticmethod - def remove(env_template_id): - """It deletes the environment template from database. - - :param env_template_id: Template Id to be deleted. - """ - - unit = db_session.get_session() - template = unit.query(models.EnvironmentTemplate).get(env_template_id) - if template: - with unit.begin(): - unit.delete(template) - - @staticmethod - def update(env_template_id, body): - """It updates the description of an environment template. - - :param env_template_id: Template Id to be deleted. - :param body: The description to be updated. - :return: the template description updated - """ - - unit = db_session.get_session() - template = unit.query(models.EnvironmentTemplate).get(env_template_id) - template.update(body) - template.save(unit) - return template - - @staticmethod - def get_description(env_template_id): - """Returns environment template description for specified template. - - :param env_template_id: Template Id - :return: environment-template Description Object - """ - - template = EnvTemplateServices.get_env_template(env_template_id) - if template is None: - raise ValueError("The environment template does not exist") - return template.description - - @staticmethod - def get_application_description(env_template_id): - """Returns environment template description for specified applications. - - :param env_template_id: Template Id - :return: Template Description Object - """ - - env_temp_desc = EnvTemplateServices.get_description(env_template_id) - if "services" not in env_temp_desc: - return [] - else: - return env_temp_desc['services'] - - @staticmethod - def save_description(env_template_des, env_template_id=None): - """Saves environment template description to specified session. - - :param env_template_des: Template Description - :param env_template_id: The template ID. - """ - unit = db_session.get_session() - template = unit.query(models.EnvironmentTemplate).get(env_template_id) - template.update({'description': env_template_des}) - template.save(unit) - - @staticmethod - def env_template_exist(env_template_id): - """It checks if the environment template exits in database. - - :param env_template_id: The template ID - """ - - template = EnvTemplateServices.get_env_template(env_template_id) - if template is None: - return False - else: - return True - - @staticmethod - def get_env_template(env_template_id): - """It obtains the environment template information from the database. - - :param env_template_id: The template ID - """ - session = db_session.get_session() - return session.query(models.EnvironmentTemplate).get(env_template_id) - - @staticmethod - def clone(env_template_id, tenant_id, env_template_name, is_public): - """Clones environment-template with specified params, - in particular - name. - - :param env_template_params: Dict, e.g. {'name': 'temp-name'} - :param tenant_id: Tenant Id - :return: Created Template - """ - - template = EnvTemplateServices.get_env_template(env_template_id) - env_template_params = template.to_dict() - env_template_params['id'] = uuidutils.generate_uuid() - env_template_params['tenant_id'] = tenant_id - env_template_params['name'] = env_template_name - env_template_params['is_public'] = is_public - env_temp_desc = EnvTemplateServices.get_description(env_template_id) - if "services" in env_temp_desc: - env_template_params['services'] = env_temp_desc['services'] - - env_template = models.EnvironmentTemplate() - env_template.update(env_template_params) - - unit = db_session.get_session() - with unit.begin(): - unit.add(env_template) - env_template.update({'description': env_template_params}) - env_template.save(unit) - return env_template diff --git a/murano/db/services/environments.py b/murano/db/services/environments.py deleted file mode 100644 index 7158d4d3f..000000000 --- a/murano/db/services/environments.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 keystoneclient import exceptions as ks_exceptions -from oslo_config import cfg -from oslo_log import log as logging -import yaml - -from murano.common import auth_utils -from murano.common import uuidutils -from murano.db import models -from murano.db.services import sessions -from murano.db import session as db_session -from murano.services import states - -CONF = cfg.CONF - -LOG = logging.getLogger(__name__) -DEFAULT_NETWORK_TYPES = { - "nova": 'io.murano.resources.NovaNetwork', - "neutron": 'io.murano.resources.NeutronNetwork' -} - - -class EnvironmentServices(object): - @staticmethod - def get_environments_by(filters): - """Returns list of environments - - :param filters: property filters - :return: Returns list of environments - """ - unit = db_session.get_session() - environments = unit.query(models.Environment). \ - filter_by(**filters).all() - - for env in environments: - env['status'] = EnvironmentServices.get_status(env['id']) - - return environments - - @staticmethod - def get_status(environment_id): - """Environment can have one of the following statuses: - - - deploying: there is ongoing deployment for environment - - deleting: environment is currently being deleted - - deploy failure: last deployment session has failed - - delete failure: last delete session has failed - - pending: there is at least one session with status `open` and no - errors in previous sessions - - ready: there are no sessions for environment - - :param environment_id: Id of environment for which we checking status. - :return: Environment status - """ - # Deploying: there is at least one valid session with status deploying - session_list = sessions.SessionServices.get_sessions(environment_id) - has_opened = False - for session in session_list: - if session.state == states.SessionState.DEPLOYING: - return states.EnvironmentStatus.DEPLOYING - elif session.state == states.SessionState.DELETING: - return states.EnvironmentStatus.DELETING - elif session.state == states.SessionState.DEPLOY_FAILURE: - return states.EnvironmentStatus.DEPLOY_FAILURE - elif session.state == states.SessionState.DELETE_FAILURE: - return states.EnvironmentStatus.DELETE_FAILURE - elif session.state == states.SessionState.OPENED: - has_opened = True - elif session.state == states.SessionState.DEPLOYED: - break - if has_opened: - return states.EnvironmentStatus.PENDING - - return states.EnvironmentStatus.READY - - @staticmethod - def create(environment_params, context): - # tagging environment by tenant_id for later checks - """Creates environment with specified params, in particular - name - - :param environment_params: Dict, e.g. {'name': 'env-name'} - :param context: request context to get the tenant id and the token - :return: Created Environment - """ - objects = {'?': { - 'id': uuidutils.generate_uuid(), - }} - network_driver = EnvironmentServices.get_network_driver(context) - objects.update(environment_params) - if not objects.get('defaultNetworks'): - objects['defaultNetworks'] = \ - EnvironmentServices.generate_default_networks(objects['name'], - network_driver) - objects['?']['type'] = 'io.murano.Environment' - objects['?']['metadata'] = {} - - data = { - 'Objects': objects, - 'Attributes': [], - 'project_id': context.project_id, - 'user_id': context.user - } - - environment_params['tenant_id'] = context.project_id - environment = models.Environment() - environment.update(environment_params) - - unit = db_session.get_session() - with unit.begin(): - unit.add(environment) - - # saving environment as Json to itself - environment.update({'description': data}) - environment.save(unit) - - return environment - - @staticmethod - def delete(environment_id, session_id): - """Deletes environment and notify orchestration engine about deletion - - :param environment_id: Environment that is going to be deleted - :param session_id: Session Id - """ - - env_description = EnvironmentServices.get_environment_description( - environment_id, session_id, False) - env_description['Objects'] = None - EnvironmentServices.save_environment_description( - session_id, env_description, False) - - @staticmethod - def remove(environment_id): - unit = db_session.get_session() - environment = unit.query(models.Environment).get(environment_id) - if environment: - with unit.begin(): - unit.delete(environment) - - @staticmethod - def get_environment_description(environment_id, session_id=None, - inner=True): - """Returns environment description for specified environment. - - If session is specified and not in deploying state function - returns modified environment description, - otherwise returns actual environment desc. - - :param environment_id: Environment Id - :param session_id: Session Id - :param inner: return contents of environment rather than whole - Object Model structure - :return: Environment Description Object - """ - unit = db_session.get_session() - - if session_id: - session = unit.query(models.Session).get(session_id) - if sessions.SessionServices.validate(session): - if session.state != states.SessionState.DEPLOYED: - env_description = session.description - else: - env = unit.query(models.Environment) \ - .get(session.environment_id) - env_description = env.description - else: - env = unit.query(models.Environment) \ - .get(session.environment_id) - env_description = env.description - else: - env = (unit.query(models.Environment).get(environment_id)) - env_description = env.description - - if not inner: - return env_description - else: - return env_description['Objects'] - - @staticmethod - def save_environment_description(session_id, environment, inner=True): - """Saves environment description to specified session. - - :param session_id: Session Id - :param environment: Environment Description - :param inner: save modifications to only content of environment - rather than whole Object Model structure - """ - unit = db_session.get_session() - session = unit.query(models.Session).get(session_id) - if inner: - data = session.description.copy() - data['Objects'] = environment - session.description = data - else: - session.description = environment - session.save(unit) - - @staticmethod - def generate_default_networks(env_name, network_driver): - net_config = CONF.find_file( - CONF.networking.network_config_file) - if net_config: - LOG.debug("Loading network configuration from file") - with open(net_config) as f: - data = yaml.safe_load(f) - return EnvironmentServices._objectify(data, { - 'ENV': env_name - }) - - network_type = DEFAULT_NETWORK_TYPES[network_driver] - LOG.debug("Setting '{net_type}' as environment's " - "default network".format(net_type=network_type)) - return { - 'environment': { - '?': { - 'id': uuidutils.generate_uuid(), - 'type': network_type - }, - 'name': env_name + '-network' - }, - 'flat': None - } - - @staticmethod - def deploy(session, unit, context): - environment = unit.query(models.Environment).get( - session.environment_id) - - if (session.description['Objects'] is None and - 'ObjectsCopy' not in session.description): - EnvironmentServices.remove(session.environment_id) - else: - sessions.SessionServices.deploy( - session, environment, unit, context) - - @staticmethod - def _objectify(data, replacements): - if isinstance(data, dict): - if isinstance(data.get('?'), dict): - data['?']['id'] = uuidutils.generate_uuid() - result = {} - for key, value in data.items(): - result[key] = EnvironmentServices._objectify( - value, replacements) - return result - elif isinstance(data, list): - return [EnvironmentServices._objectify(v, replacements) - for v in data] - elif isinstance(data, str): - for key, value in replacements.items(): - data = data.replace('%' + key + '%', value) - return data - - @staticmethod - def get_network_driver(context): - driver = CONF.networking.driver - if driver: - LOG.debug("Will use {} as a network driver".format(driver)) - return driver - - session = auth_utils.get_token_client_session( - context.auth_token, context.project_id) - try: - session.get_endpoint(service_type='network') - except ks_exceptions.EndpointNotFound: - LOG.debug("Will use NovaNetwork as a network driver") - return "nova" - else: - LOG.debug("Will use Neutron as a network driver") - return "neutron" diff --git a/murano/db/services/instances.py b/murano/db/services/instances.py deleted file mode 100644 index 6ed20e90b..000000000 --- a/murano/db/services/instances.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 oslo_db import exception -from oslo_utils import timeutils -import sqlalchemy -from sqlalchemy.sql import func - -from murano.db import models -from murano.db import session as db_session - - -UNCLASSIFIED = 0 -APPLICATION = 100 -OS_INSTANCE = 200 - - -class InstanceStatsServices(object): - @staticmethod - def track_instance(instance_id, environment_id, instance_type, - type_name, type_title=None, unit_count=None): - - unit = db_session.get_session() - try: - with unit.begin(): - env = unit.query(models.Environment).get(environment_id) - instance = models.Instance() - instance.instance_id = instance_id - instance.environment_id = environment_id - instance.tenant_id = env.tenant_id - instance.instance_type = instance_type - instance.created = timeutils.utcnow_ts() - instance.destroyed = None - instance.type_name = type_name - instance.type_title = type_title - instance.unit_count = unit_count - - unit.add(instance) - except exception.DBDuplicateEntry: - unit.execute( - sqlalchemy.update(models.Instance).where( - models.Instance.instance_id == instance_id and - models.Instance.environment_id == environment_id).values( - unit_count=unit_count)) - - @staticmethod - def destroy_instance(instance_id, environment_id): - unit = db_session.get_session() - instance = unit.query(models.Instance).get( - (environment_id, instance_id)) - if instance and not instance.destroyed: - instance.destroyed = timeutils.utcnow_ts() - instance.save(unit) - - @staticmethod - def get_aggregated_stats(environment_id): - unit = db_session.get_session() - now = timeutils.utcnow_ts() - query = unit.query(models.Instance.instance_type, func.sum( - func.coalesce(models.Instance.destroyed, now) - - models.Instance.created), func.count()).filter( - models.Instance.environment_id == environment_id) - - res = query.group_by(models.Instance.instance_type).all() - - return [{ - 'type': int(record[0]), - 'duration': int(record[1]), - 'count': int(record[2]) - } for record in res] - - @staticmethod - def get_raw_environment_stats(environment_id, instance_id=None): - unit = db_session.get_session() - now = timeutils.utcnow_ts() - query = unit.query(models.Instance).filter( - models.Instance.environment_id == environment_id) - - if instance_id: - query = query.filter(models.Instance.instance_id == instance_id) - - res = query.all() - - return [{ - 'type': record.instance_type, - 'duration': (record.destroyed or now) - record.created, - 'type_name': record.type_name, - 'unit_count': record.unit_count, - 'instance_id': record.instance_id, - 'type_title': record.type_title, - 'active': True if not record.destroyed else False - } for record in res] diff --git a/murano/db/services/sessions.py b/murano/db/services/sessions.py deleted file mode 100644 index 3167eaeb0..000000000 --- a/murano/db/services/sessions.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 murano.db import models -from murano.db import session as db_session -from murano.services import actions -from murano.services import states - - -class SessionServices(object): - @staticmethod - def get_sessions(environment_id, state=None): - """Get list of sessions for specified environment. - - :param environment_id: Environment Id - :param state: murano.services.states.EnvironmentStatus - :return: Sessions for specified Environment, if SessionState is - not defined all sessions for specified environment is returned. - """ - - unit = db_session.get_session() - # Here we duplicate logic for reducing calls to database - # Checks for validation is same as in validate. - query = unit.query(models.Session).filter( - # Get all session for this environment - models.Session.environment_id == environment_id, - # Only sessions with same version as current env version are valid - ) - - if state: - # in this state, if state is not specified return in all states - query = query.filter(models.Session.state == state) - - return query.order_by(models.Session.version.desc(), - models.Session.updated.desc()).all() - - @staticmethod - def create(environment_id, user_id): - """Creates session object for specific environment for specified user. - - :param environment_id: Environment Id - :param user_id: User Id - :return: Created session - """ - unit = db_session.get_session() - environment = unit.query(models.Environment).get(environment_id) - - session = models.Session() - session.environment_id = environment.id - session.user_id = user_id - session.state = states.SessionState.OPENED - # used for checking if other sessions was deployed before this one - session.version = environment.version - # all changes to environment is stored here, and translated to - # environment only after deployment completed - session.description = environment.description - - with unit.begin(): - unit.add(session) - - return session - - @staticmethod - def validate(session): - """Validates session - - Session is valid only if no other session for same. - environment was already deployed on in deploying state, - - :param session: Session for validation - """ - - # if other session is deploying now current session is invalid - unit = db_session.get_session() - - # if environment version is higher than version on which current - # session is created then other session was already deployed - current_env = unit.query(models.Environment).\ - get(session.environment_id) - if current_env.version > session.version: - return False - - # if other session is deploying now current session is invalid - other_is_deploying = unit.query(models.Session).filter_by( - environment_id=session.environment_id, - state=states.SessionState.DEPLOYING - ).count() > 0 - if session.state == states.SessionState.OPENED and other_is_deploying: - return False - - return True - - @staticmethod - def deploy(session, environment, unit, context): - """Prepares and deployes environment - - Prepares environment for deployment and send deployment command to - orchestration engine - - :param session: session that is going to be deployed - :param unit: SQLalchemy session - :param token: auth token that is going to be used by orchestration - """ - - deleted = session.description['Objects'] is None - action_name = None if deleted else 'deploy' - actions.ActionServices.submit_task( - action_name, environment.id, - {}, environment, session, - context, unit) diff --git a/murano/db/services/stats.py b/murano/db/services/stats.py deleted file mode 100644 index ad9607743..000000000 --- a/murano/db/services/stats.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 json - -from murano.db import models as m -from murano.db import session as db_session - - -class Statistics(object): - - @staticmethod - def get_all(): - db = db_session.get_session() - stats = db.query(m.ApiStats).all() - return stats - - @staticmethod - def get_stats_by_id(stats_id): - db = db_session.get_session() - stats = db.query(m.ApiStats).get(stats_id) - return stats - - @staticmethod - def get_stats_by_host(host): - db = db_session.get_session() - stats = db.query(m.ApiStats).filter(m.ApiStats.host == host).first() - return stats - - @staticmethod - def create(host, request_count, error_count, - average_response_time, requests_per_tenant, cpu_count, - cpu_percent): - stats = m.ApiStats() - stats.host = host - stats.request_count = request_count - stats.error_count = error_count - stats.average_response_time = average_response_time - stats.requests_per_tenant = json.dumps(requests_per_tenant) - stats.requests_per_second = 0.0 - stats.errors_per_second = 0.0 - stats.cpu_count = cpu_count - stats.cpu_percent = cpu_percent - - db = db_session.get_session() - stats.save(db) - - @staticmethod - def update(host, stats): - db = db_session.get_session() - stats.save(db) diff --git a/murano/db/session.py b/murano/db/session.py deleted file mode 100644 index e94ebf63f..000000000 --- a/murano/db/session.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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. - -"""Session management functions.""" -import threading - -from oslo_config import cfg -from oslo_db import exception -from oslo_db import options -from oslo_db.sqlalchemy import session as db_session -from oslo_utils import timeutils - -from murano.db import models - -CONF = cfg.CONF - -options.set_defaults(CONF) - -_FACADE = None -_LOCK = threading.Lock() - -MAX_LOCK_RETRIES = 10 - - -def _create_facade_lazily(): - global _LOCK, _FACADE - - if _FACADE is None: - # FIXME(zigo): autocommit=True it's not compatible with - # SQLAlchemy 2.0, and will be removed in future - _FACADE = db_session.EngineFacade.from_config(CONF, autocommit=True) - with _LOCK: - if _FACADE is None: - _FACADE = db_session.EngineFacade.from_config(CONF, - sqlite_fk=True) - return _FACADE - - -def get_engine(): - facade = _create_facade_lazily() - return facade.get_engine() - - -def get_session(**kwargs): - facade = _create_facade_lazily() - return facade.get_session(**kwargs) - - -def get_lock(name, session=None): - if session is None: - session = get_session() - nested = False - else: - nested = session.transaction is not None - return _get_or_create_lock(name, session, nested) - - -def _get_or_create_lock(name, session, nested, retry=0): - if nested: - session.begin_nested() - else: - session.begin() - existing = session.query(models.Lock).get(name) - if existing is None: - try: - # no lock found, creating a new one - lock = models.Lock(id=name, ts=timeutils.utcnow()) - lock.save(session) - return session.transaction - # lock created and acquired - except exception.DBDuplicateEntry: - session.rollback() - if retry >= MAX_LOCK_RETRIES: - raise - else: - # other transaction has created a lock, repeat to acquire - # via update - return _get_or_create_lock(name, session, nested, retry + 1) - else: - # lock found, acquiring by doing update - existing.ts = timeutils.utcnow() - existing.save(session) - return session.transaction diff --git a/murano/db/sqla/__init__.py b/murano/db/sqla/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/db/sqla/types.py b/murano/db/sqla/types.py deleted file mode 100644 index 77e4e3fd5..000000000 --- a/murano/db/sqla/types.py +++ /dev/null @@ -1,31 +0,0 @@ -# Licensed 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 oslo_serialization import jsonutils -import sqlalchemy as sa -from sqlalchemy.dialects import mysql - - -def LargeBinary(): - return sa.LargeBinary().with_variant(mysql.LONGBLOB(), 'mysql') - - -class JsonBlob(sa.TypeDecorator): - impl = sa.Text - - def process_bind_param(self, value, dialect): - return jsonutils.dumps(value) - - def process_result_value(self, value, dialect): - if value is not None: - return jsonutils.loads(value) - return None diff --git a/murano/dsl/__init__.py b/murano/dsl/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/dsl/attribute_store.py b/murano/dsl/attribute_store.py deleted file mode 100644 index fef82e1b4..000000000 --- a/murano/dsl/attribute_store.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 collections - - -from murano.dsl import dsl_types - - -class AttributeStore(object): - def __init__(self): - self._attributes = collections.defaultdict(lambda: {}) - - @staticmethod - def _get_attribute_key(tagged_object, owner_type, name): - if isinstance(owner_type, dsl_types.MuranoTypeReference): - owner_type = owner_type.type - if isinstance(tagged_object, dsl_types.MuranoObjectInterface): - tagged_object = tagged_object.object - return tagged_object.object_id, (owner_type.name, name) - - def get(self, tagged_object, owner_type, name): - key1, key2 = self._get_attribute_key(tagged_object, owner_type, name) - return self._attributes[key1].get(key2) - - def set(self, tagged_object, owner_type, name, value): - if isinstance(value, dsl_types.MuranoObjectInterface): - value = value.id - elif isinstance(value, dsl_types.MuranoObject): - value = value.object_id - key1, key2 = self._get_attribute_key(tagged_object, owner_type, name) - if value is None: - self._attributes[key1].pop(key2, None) - else: - self._attributes[key1][key2] = value - - def serialize(self, known_objects): - return [ - [key1, key2[0], key2[1], value] - for key1, inner in self._attributes.items() - for key2, value in inner.items() - if key1 in known_objects - ] - - def load(self, data): - for item in data: - if item[3] is not None: - self._attributes[item[0]][(item[1], item[2])] = item[3] - - def forget_object(self, obj): - if isinstance(obj, dsl_types.MuranoObjectInterface): - obj = obj.id - elif isinstance(obj, dsl_types.MuranoObject): - obj = obj.object_id - self._attributes.pop(obj, None) diff --git a/murano/dsl/constants.py b/murano/dsl/constants.py deleted file mode 100644 index 0b7c88f99..000000000 --- a/murano/dsl/constants.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 semantic_version - - -EXPRESSION_MEMORY_QUOTA = 512 * 1024 - -CTX_ACTIONS_ONLY = '?actionsOnly' -CTX_ALLOW_PROPERTY_WRITES = '$?allowPropertyWrites' -CTX_ARGUMENT_OWNER = '$?argumentOwner' -CTX_CALLER_CONTEXT = '$?callerContext' -CTX_CURRENT_INSTRUCTION = '$?currentInstruction' -CTX_CURRENT_EXCEPTION = '$?currentException' -CTX_CURRENT_METHOD = '$?currentMethod' -CTX_NAMES_SCOPE = '$?namesScope' -CTX_ORIGINAL_CONTEXT = '$?originalContext' -CTX_SKIP_FRAME = '$?skipFrame' -CTX_THIS = '$?this' -CTX_TYPE = '$?type' -CTX_VARIABLE_SCOPE = '$?variableScope' -CTX_YAQL_ENGINE = '$?yaqlEngine' - -DM_OBJECTS = 'Objects' -DM_OBJECTS_COPY = 'ObjectsCopy' -DM_ATTRIBUTES = 'Attributes' - -META_MURANO_METHOD = '?muranoMethod' -META_NO_TRACE = '?noTrace' -META_MPL_META = 'Meta' -META_USAGE = 'Usage' -META_SCOPE = 'Scope' - -CORE_LIBRARY = 'io.murano' -CORE_LIBRARY_OBJECT = 'io.murano.Object' - -TL_CONTEXT = '__murano_context' -TL_ID = '__thread_id' -TL_OBJECT_STORE = '__murano_object_store' -TL_SESSION = '__murano_execution_session' -TL_CONTRACT_PASSKEY = '__murano_contract_passkey' -TL_OBJECTS_DRY_RUN = '__murano_objects_dry_run' - - -RUNTIME_VERSION_1_0 = semantic_version.Version('1.0.0') -RUNTIME_VERSION_1_1 = semantic_version.Version('1.1.0') -RUNTIME_VERSION_1_2 = semantic_version.Version('1.2.0') -RUNTIME_VERSION_1_3 = semantic_version.Version('1.3.0') -RUNTIME_VERSION_1_4 = semantic_version.Version('1.4.0') -RUNTIME_VERSION_1_5 = semantic_version.Version('1.5.0') diff --git a/murano/dsl/context_manager.py b/murano/dsl/context_manager.py deleted file mode 100644 index 36c6626ac..000000000 --- a/murano/dsl/context_manager.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 murano.dsl import yaql_integration - - -# noinspection PyMethodMayBeStatic -class ContextManager(object): - """Context manager for the MuranoDslExecutor. - - DSL hosting project should subclass this and override methods in order - to insert yaql function at various scopes. For example it may override - create_root_context to register its own global yaql functions. - """ - - def create_root_context(self, runtime_version): - return yaql_integration.create_context(runtime_version) - - def create_package_context(self, package): - return package.context - - def create_type_context(self, murano_type): - return murano_type.context - - def create_object_context(self, obj): - return None diff --git a/murano/dsl/contracts/__init__.py b/murano/dsl/contracts/__init__.py deleted file mode 100644 index cee7c9c75..000000000 --- a/murano/dsl/contracts/__init__.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 abc - - -class ContractMethod(object): - @abc.abstractmethod - def transform(self): - raise NotImplementedError() - - @abc.abstractmethod - def validate(self): - raise NotImplementedError() - - def finalize(self): - return self.value - - def check_type(self): - return self.validate() - - def generate_schema(self): - return self.value - - def __getattr__(self, item): - return self.context[item] - - -class ObjRef(object): - def __init__(self, object_id): - self.object_id = object_id diff --git a/murano/dsl/contracts/basic.py b/murano/dsl/contracts/basic.py deleted file mode 100644 index 5d5922a70..000000000 --- a/murano/dsl/contracts/basic.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 murano.dsl import contracts -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers - - -class String(contracts.ContractMethod): - name = 'string' - - def transform(self): - if self.value is None: - return None - if isinstance(self.value, str): - return self.value - if isinstance(self.value, str) or \ - isinstance(self.value, int): - return str(self.value) - if isinstance(self.value, dsl_types.MuranoObject): - return self.value.object_id - if isinstance(self.value, dsl_types.MuranoObjectInterface): - return self.value.id - raise exceptions.ContractViolationException( - 'Value {0} violates string() contract'.format( - helpers.format_scalar(self.value))) - - def validate(self): - if self.value is None or isinstance(self.value, str): - return self.value - raise exceptions.ContractViolationException() - - def generate_schema(self): - types = 'string' - if '_notNull' not in self.value: - types = [types] + ['null'] - - return { - 'type': types - } - - -class Bool(contracts.ContractMethod): - name = 'bool' - - def validate(self): - if self.value is None or isinstance(self.value, bool): - return self.value - raise exceptions.ContractViolationException() - - def transform(self): - if self.value is None: - return None - return True if self.value else False - - def generate_schema(self): - types = 'boolean' - if '_notNull' not in self.value: - types = [types] + ['null'] - - return { - 'type': types - } - - -class Int(contracts.ContractMethod): - name = 'int' - - def validate(self): - if self.value is None or isinstance( - self.value, int) and not isinstance(self.value, bool): - return self.value - raise exceptions.ContractViolationException() - - def transform(self): - if self.value is None: - return None - try: - return int(self.value) - except Exception: - raise exceptions.ContractViolationException( - 'Value {0} violates int() contract'.format( - helpers.format_scalar(self.value))) - - def generate_schema(self): - types = 'integer' - if '_notNull' not in self.value: - types = [types] + ['null'] - - return { - 'type': types - } - - -class NotNull(contracts.ContractMethod): - name = 'not_null' - - def validate(self): - if self.value is None: - raise exceptions.ContractViolationException( - 'null value violates notNull() contract') - return self.value - - def transform(self): - return self.validate() - - def generate_schema(self): - types = self.value.get('type') - if isinstance(types, list) and 'null' in types: - types.remove('null') - if len(types) == 1: - types = types[0] - self.value['type'] = types - self.value['_notNull'] = True - return self.value diff --git a/murano/dsl/contracts/check.py b/murano/dsl/contracts/check.py deleted file mode 100644 index d994478a4..000000000 --- a/murano/dsl/contracts/check.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 yaql.language import exceptions as yaql_exceptions -from yaql.language import expressions -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.dsl import contracts -from murano.dsl import exceptions -from murano.dsl import helpers - - -class Check(contracts.ContractMethod): - name = 'check' - - @specs.parameter('predicate', yaqltypes.YaqlExpression()) - @specs.parameter('msg', yaqltypes.String(nullable=True)) - def __init__(self, engine, predicate, msg=None): - self.engine = engine - self.predicate = predicate - self.msg = msg - - def _call_predicate(self, value): - context = self.root_context.create_child_context() - context['$'] = value - return self.predicate(utils.NO_VALUE, context, self.engine) - - def validate(self): - if isinstance(self.value, contracts.ObjRef) or self._call_predicate( - self.value): - return self.value - else: - msg = self.msg - if not msg: - msg = "Value {0} doesn't match predicate".format( - helpers.format_scalar(self.value)) - raise exceptions.ContractViolationException(msg) - - def transform(self): - return self.validate() - - def generate_schema(self): - rest = [True] - while rest: - if (isinstance(self.predicate, expressions.BinaryOperator) and - self.predicate.operator == 'and'): - rest = self.predicate.args[1] - self.predicate = self.predicate.args[0] - else: - rest = [] - res = extract_pattern(self.predicate, self.engine, - self.root_context) - if res is not None: - self.value.update(res) - self.predicate = rest - return self.value - - -def is_dollar(expr): - """Check $-expressions in YAQL AST""" - return (isinstance(expr, expressions.GetContextValue) and - expr.path.value in ('$', '$1')) - - -def extract_pattern(expr, engine, context): - """Translation of certain known patterns of check() contract expressions""" - if isinstance(expr, expressions.BinaryOperator): - ops = ('>', '<', '>=', '<=') - if expr.operator in ops: - op_index = ops.index(expr.operator) - if is_dollar(expr.args[0]): - constant = evaluate_constant(expr.args[1], engine, context) - if constant is None: - return None - elif is_dollar(expr.args[1]): - constant = evaluate_constant(expr.args[0], engine, context) - if constant is None: - return None - op_index = -1 - op_index - else: - return None - op = ops[op_index] - if op == '>': - return {'minimum': constant, 'exclusiveMinimum': True} - elif op == '>=': - return {'minimum': constant, 'exclusiveMinimum': False} - if op == '<': - return {'maximum': constant, 'exclusiveMaximum': True} - elif op == '<=': - return {'maximum': constant, 'exclusiveMaximum': False} - elif expr.operator == 'in' and is_dollar(expr.args[0]): - lst = evaluate_constant(expr.args[1], engine, context) - if isinstance(lst, tuple): - return {'enum': list(lst)} - - elif (expr.operator == '.' and is_dollar(expr.args[0]) and - isinstance(expr.args[1], expressions.Function)): - func = expr.args[1] - if func.name == 'matches': - constant = evaluate_constant(func.args[0], engine, context) - if constant is not None: - return {'pattern': constant} - - -def evaluate_constant(expr, engine, context): - """Evaluate yaql expression into constant value if possible""" - if isinstance(expr, expressions.Constant): - return expr.value - context = context.create_child_context() - trap = utils.create_marker('trap') - context['$'] = trap - - @specs.parameter('name', yaqltypes.StringConstant()) - @specs.name('#get_context_data') - def get_context_data(name, context): - res = context[name] - if res is trap: - raise yaql_exceptions.ResolutionError() - return res - - context.register_function(get_context_data) - - try: - return expressions.Statement(expr, engine).evaluate(context=context) - except yaql_exceptions.YaqlException: - return None diff --git a/murano/dsl/contracts/contracts.py b/murano/dsl/contracts/contracts.py deleted file mode 100644 index b4e4fe791..000000000 --- a/murano/dsl/contracts/contracts.py +++ /dev/null @@ -1,302 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 copy - -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.dsl import constants -from murano.dsl.contracts import basic -from murano.dsl.contracts import check -from murano.dsl.contracts import instances -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import yaql_integration - - -CONTRACT_METHODS = [ - basic.String, - basic.Bool, - basic.Int, - basic.NotNull, - check.Check, - instances.Class, - instances.Template, - instances.Owned, - instances.NotOwned -] - - -class Contract(object): - def __init__(self, spec, declaring_type): - self._spec = spec - self._runtime_version = declaring_type.package.runtime_version - - @property - def spec(self): - return self._spec - - @staticmethod - def _get_contract_factory(cls, action_func): - def payload(context, value, *args, **kwargs): - instance = object.__new__(cls) - instance.value = value - instance.context = context - instance.__init__(*args, **kwargs) - return action_func(instance) - - name = yaql_integration.CONVENTION.convert_function_name(cls.name) - try: - init_spec = specs.get_function_definition( - helpers.function(cls.__init__), - name=name, method=True, convention=yaql_integration.CONVENTION) - except AttributeError: - init_spec = specs.get_function_definition( - lambda self: None, - name=name, method=True) - - init_spec.parameters['self'] = specs.ParameterDefinition( - 'self', yaqltypes.PythonType(object, nullable=True), 0) - init_spec.insert_parameter(specs.ParameterDefinition( - '?1', yaqltypes.Context(), 0)) - init_spec.payload = payload - return init_spec - - @staticmethod - def _prepare_context(runtime_version, action): - context = yaql_integration.create_context( - runtime_version).create_child_context() - for cls in CONTRACT_METHODS: - context.register_function(Contract._get_contract_factory( - cls, action)) - return context - - @staticmethod - @helpers.memoize - def _prepare_transform_context(runtime_version, finalize): - if finalize: - def action(c): - c.value = c.transform() - return c.finalize() - else: - def action(c): - return c.transform() - return Contract._prepare_context( - runtime_version, action) - - @staticmethod - @helpers.memoize - def _prepare_validate_context(runtime_version): - return Contract._prepare_context( - runtime_version, lambda c: c.validate()) - - @staticmethod - @helpers.memoize - def _prepare_check_type_context(runtime_version): - return Contract._prepare_context( - runtime_version, lambda c: c.check_type()) - - @staticmethod - @helpers.memoize - def _prepare_schema_generation_context(runtime_version): - context = Contract._prepare_context( - runtime_version, lambda c: c.generate_schema()) - - @specs.name('#finalize') - def finalize(obj): - if isinstance(obj, dict): - obj.pop('_notNull', None) - return obj - - context.register_function(finalize) - return context - - @staticmethod - @helpers.memoize - def _prepare_finalize_context(runtime_version): - return Contract._prepare_context( - runtime_version, lambda c: c.finalize()) - - def _map_dict(self, data, spec, context, path): - if data is None or data is dsl.NO_VALUE: - data = {} - if not isinstance(data, utils.MappingType): - raise exceptions.ContractViolationException( - 'Value {0} is not of a dictionary type'.format( - helpers.format_scalar(data))) - if not spec: - return data - result = {} - yaql_key = None - for key, value in spec.items(): - if isinstance(key, dsl_types.YaqlExpression): - if yaql_key is not None: - raise exceptions.DslContractSyntaxError( - 'Dictionary contract ' - 'cannot have more than one expression key') - else: - yaql_key = key - else: - result[key] = self._map( - data.get(key), value, context, '{0}[{1}]'.format( - path, helpers.format_scalar(key))) - - if yaql_key is not None: - yaql_value = spec[yaql_key] - for key, value in data.items(): - if key in result: - continue - key = self._map(key, yaql_key, context, path) - result[key] = self._map( - value, yaql_value, context, '{0}[{1}]'.format( - path, helpers.format_scalar(key))) - - return utils.FrozenDict(result) - - def _map_list(self, data, spec, context, path): - if utils.is_iterator(data): - data = list(data) - elif not utils.is_sequence(data): - if data is None or data is dsl.NO_VALUE: - data = [] - else: - data = [data] - if len(spec) < 1: - return data - shift = 0 - max_length = -1 - min_length = 0 - if isinstance(spec[-1], int): - min_length = spec[-1] - shift += 1 - if len(spec) >= 2 and isinstance(spec[-2], int): - max_length = min_length - min_length = spec[-2] - shift += 1 - - if max_length >= 0 and not min_length <= len(data) <= max_length: - raise exceptions.ContractViolationException( - 'Array length {0} is not within [{1}..{2}] range'.format( - len(data), min_length, max_length)) - elif not min_length <= len(data): - raise exceptions.ContractViolationException( - 'Array length {0} must not be less than {1}'.format( - len(data), min_length)) - - def map_func(): - for index, item in enumerate(data): - spec_item = ( - spec[-1 - shift] - if index >= len(spec) - shift - else spec[index] - ) - yield self._map( - item, spec_item, context, '{0}[{1}]'.format(path, index)) - - return tuple(map_func()) - - def _map_scalar(self, data, spec): - if data != spec: - raise exceptions.ContractViolationException( - 'Value {0} is not equal to {1}'.format( - helpers.format_scalar(data), spec)) - else: - return data - - def _map(self, data, spec, context, path): - if helpers.is_passkey(data): - return data - child_context = context.create_child_context() - if isinstance(spec, dsl_types.YaqlExpression): - child_context[''] = data - try: - result = spec(context=child_context) - return result - except exceptions.ContractViolationException as e: - e.path = path - raise - elif isinstance(spec, utils.MappingType): - return self._map_dict(data, spec, child_context, path) - elif utils.is_sequence(spec): - return self._map_list(data, spec, child_context, path) - else: - return self._map_scalar(data, spec) - - def _execute(self, base_context_func, data, context, default, **kwargs): - # TODO(ativelkov, slagun): temporary fix, need a better way of handling - # composite defaults - # A bug (#1313694) has been filed - - if data is dsl.NO_VALUE: - data = helpers.evaluate(default, context) - - if helpers.is_passkey(data): - return data - - contract_context = base_context_func( - self._runtime_version).create_child_context() - contract_context['root_context'] = context - for key, value in kwargs.items(): - contract_context[key] = value - contract_context[constants.CTX_NAMES_SCOPE] = \ - context[constants.CTX_NAMES_SCOPE] - return self._map(data, self._spec, contract_context, '') - - def transform(self, data, context, this, owner, default, calling_type, - finalize=True): - return self._execute( - lambda runtime_version: self._prepare_transform_context( - runtime_version, finalize), data, context, default, - this=this, owner=owner, calling_type=calling_type) - - def validate(self, data, context, default, calling_type): - self._execute(self._prepare_validate_context, - data, context, default, calling_type=calling_type) - return True - - def check_type(self, data, context, default, calling_type): - if helpers.is_passkey(data): - return False - try: - self._execute(self._prepare_check_type_context, - data, context, default, calling_type=calling_type) - return True - except exceptions.ContractViolationException: - return False - - def finalize(self, data, context, calling_type): - return self._execute( - self._prepare_finalize_context, - data, context, None, calling_type=calling_type) - - @staticmethod - def generate_expression_schema(expression, context, runtime_version, - initial_schema=None): - if initial_schema is None: - initial_schema = {} - else: - initial_schema = copy.deepcopy(initial_schema) - - contract_context = Contract._prepare_schema_generation_context( - runtime_version).create_child_context() - - contract_context['root_context'] = context - contract_context[constants.CTX_NAMES_SCOPE] = \ - context[constants.CTX_NAMES_SCOPE] - contract_context['$'] = initial_schema - return expression(context=contract_context) diff --git a/murano/dsl/contracts/instances.py b/murano/dsl/contracts/instances.py deleted file mode 100644 index b67088aa7..000000000 --- a/murano/dsl/contracts/instances.py +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.dsl import constants -from murano.dsl import contracts -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import serializer - - -class Class(contracts.ContractMethod): - name = 'class' - - @specs.parameter('name', dsl.MuranoTypeParameter( - nullable=False, lazy=True)) - @specs.parameter('default_name', dsl.MuranoTypeParameter( - nullable=True, lazy=True)) - @specs.parameter('version_spec', yaqltypes.String(True)) - def __init__(self, name, default_name=None, version_spec=None): - self.type = name(self.context).type - self.default_type = default_name(self.context) or self.type - self.version_spec = version_spec - - def validate(self): - if self.value is None or helpers.is_instance_of( - self.value, self.type.name, - self.version_spec or helpers.get_names_scope( - self.root_context)): - return self.value - if not isinstance(self.value, (dsl_types.MuranoObject, - dsl_types.MuranoObjectInterface)): - raise exceptions.ContractViolationException( - 'Value is not an object') - raise exceptions.ContractViolationException( - 'Object of type {0} is not compatible with ' - 'requested type {1}'.format(self.value.type, self.type)) - - def transform(self): - value = self.value - object_store = helpers.get_object_store() - if isinstance(self.value, contracts.ObjRef): - value = self.value.object_id - if value is None: - return None - if isinstance(value, dsl_types.MuranoObject): - obj = value - elif isinstance(value, dsl_types.MuranoObjectInterface): - obj = value.object - elif isinstance(value, utils.MappingType): - obj = object_store.load( - value, self.owner, context=self.root_context, - default_type=self.default_type, - scope_type=self.calling_type) - elif isinstance(value, str): - obj = object_store.get(value) - if obj is None: - if not object_store.initializing: - raise exceptions.NoObjectFoundError(value) - else: - return contracts.ObjRef(value) - else: - raise exceptions.ContractViolationException( - 'Value {0} cannot be represented as class {1}'.format( - helpers.format_scalar(value), self.type)) - self.value = obj - return self.validate() - - def generate_schema(self): - return self.generate_class_schema(self.value, self.type) - - @staticmethod - def generate_class_schema(value, type_): - types = 'muranoObject' - if '_notNull' not in value: - types = [types] + ['null'] - - return { - 'type': types, - 'muranoType': type_.name - } - - -class Template(contracts.ContractMethod): - name = 'template' - - @specs.parameter('type_', dsl.MuranoTypeParameter( - nullable=False, lazy=True)) - @specs.parameter('default_type', dsl.MuranoTypeParameter( - nullable=True, lazy=True)) - @specs.parameter('version_spec', yaqltypes.String(True)) - @specs.parameter( - 'exclude_properties', yaqltypes.Sequence(nullable=True)) - def __init__(self, engine, type_, default_type=None, version_spec=None, - exclude_properties=None): - self.type = type_(self.context).type - self.default_type = default_type(self.context) or self.type - self.version_spec = version_spec - self.exclude_properties = exclude_properties - self.engine = engine - - def validate(self): - if self.value is None or helpers.is_instance_of( - self.value, self.type.name, - self.version_spec or helpers.get_names_scope( - self.root_context)): - return self.value - if not isinstance(self.value, (dsl_types.MuranoObject, - dsl_types.MuranoObjectInterface)): - raise exceptions.ContractViolationException( - 'Value is not an object') - raise exceptions.ContractViolationException( - 'Object of type {0} is not compatible with ' - 'requested type {1}'.format(self.value.type, self.type)) - - def check_type(self): - if isinstance(self.value, utils.MappingType): - return self.value - return self.validate() - - def transform(self): - object_store = helpers.get_object_store() - if self.value is None: - return None - if isinstance(self.value, dsl_types.MuranoObject): - obj = self.value - elif isinstance(self.value, dsl_types.MuranoObjectInterface): - obj = self.value.object - elif isinstance(self.value, utils.MappingType): - passkey = utils.create_marker('') - if self.exclude_properties: - parsed = helpers.parse_object_definition( - self.value, self.calling_type, self.context) - props = dsl.to_mutable(parsed['properties'], self.engine) - for p in self.exclude_properties: - helpers.patch_dict(props, p, passkey) - parsed['properties'] = props - value = helpers.assemble_object_definition(parsed) - else: - value = self.value - with helpers.thread_local_attribute( - constants.TL_CONTRACT_PASSKEY, passkey): - with helpers.thread_local_attribute( - constants.TL_OBJECTS_DRY_RUN, True): - obj = object_store.load( - value, self.owner, context=self.context, - default_type=self.default_type, - scope_type=self.calling_type) - obj.__passkey__ = passkey - else: - raise exceptions.ContractViolationException( - 'Value {0} cannot be represented as class {1}'.format( - helpers.format_scalar(self.value), self.type)) - self.value = obj - return self.validate() - - def finalize(self): - if self.value is None: - return None - object_store = helpers.get_object_store() - if object_store.initializing: - return {} - passkey = getattr(self.value, '__passkey__', None) - with helpers.thread_local_attribute( - constants.TL_CONTRACT_PASSKEY, passkey): - result = serializer.serialize( - self.value.real_this, object_store.executor, - dsl_types.DumpTypes.Mixed) - if self.exclude_properties: - for p in self.exclude_properties: - helpers.patch_dict(result, p, utils.NO_VALUE) - return result - - def generate_schema(self): - result = Class.generate_class_schema(self.value, self.type) - result['owned'] = True - if self.exclude_properties: - result['excludedProperties'] = self.exclude_properties - return result - - -class Owned(contracts.ContractMethod): - name = 'owned' - - def validate(self): - if self.value is None or isinstance(self.value, contracts.ObjRef): - return self.value - if isinstance(self.value, dsl_types.MuranoObject): - if helpers.find_object_owner(self.value, lambda t: t is self.this): - return self.value - raise exceptions.ContractViolationException( - 'Object {0} violates owned() contract'.format(self.value)) - raise exceptions.ContractViolationException( - 'Value {0} is not an object'.format(self.value)) - - def transform(self): - return self.validate() - - def generate_schema(self): - self.value['owned'] = True - return self.value - - -class NotOwned(contracts.ContractMethod): - name = 'not_owned' - - def validate(self): - if self.value is None or isinstance(self.value, contracts.ObjRef): - return self.value - if isinstance(self.value, dsl_types.MuranoObject): - if helpers.find_object_owner(self.value, lambda t: t is self.this): - raise exceptions.ContractViolationException( - 'Object {0} violates notOwned() contract'.format( - self.value)) - return self.value - raise exceptions.ContractViolationException( - 'Value {0} is not an object'.format(self.value)) - - def transform(self): - return self.validate() - - def generate_schema(self): - self.value['owned'] = False - return self.value diff --git a/murano/dsl/dsl.py b/murano/dsl/dsl.py deleted file mode 100644 index 3b02a2131..000000000 --- a/murano/dsl/dsl.py +++ /dev/null @@ -1,391 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 inspect -import os.path - -import eventlet -from oslo_config import cfg -from yaql.language import expressions as yaql_expressions -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes -from yaql import yaql_interface - -from murano.dsl import constants -from murano.dsl import dsl_types -from murano.dsl import helpers - - -CONF = cfg.CONF -NO_VALUE = utils.create_marker('NO_VALUE') - - -def name(dsl_name): - def wrapper(cls): - cls.__murano_name = dsl_name - return cls - return wrapper - - -class MuranoObjectParameter(yaqltypes.PythonType): - def __init__(self, murano_class=None, nullable=False, version_spec=None, - decorate=True): - self.murano_class = murano_class - self.version_spec = version_spec - self.decorate = decorate - super(MuranoObjectParameter, self).__init__( - (dsl_types.MuranoObject, MuranoObjectInterface), nullable) - - def check(self, value, context, *args, **kwargs): - if not super(MuranoObjectParameter, self).check( - value, context, *args, **kwargs): - return False - if value is None or isinstance(value, yaql_expressions.Expression): - return True - if isinstance(value, MuranoObjectInterface): - value = value.object - if not isinstance(value, dsl_types.MuranoObject): - return False - if self.murano_class: - murano_class = self.murano_class - if isinstance(murano_class, str): - return helpers.is_instance_of( - value, murano_class, - self.version_spec or helpers.get_type(context)) - else: - return murano_class.is_compatible(value) - else: - return True - - def convert(self, value, sender, context, function_spec, engine, - *args, **kwargs): - result = super(MuranoObjectParameter, self).convert( - value, sender, context, function_spec, engine, *args, **kwargs) - if self.decorate: - return MuranoObjectInterface.create(result) - else: - if isinstance(result, dsl_types.MuranoObject): - return result - return None if result is None else result.object - - -class ThisParameter(yaqltypes.HiddenParameterType, yaqltypes.SmartType): - def __init__(self): - super(ThisParameter, self).__init__(False) - - def convert(self, value, sender, context, function_spec, engine, - *args, **kwargs): - return get_this(context) - - -class InterfacesParameter(yaqltypes.HiddenParameterType, - yaqltypes.SmartType): - def __init__(self): - super(InterfacesParameter, self).__init__(False) - - def convert(self, value, sender, context, function_spec, engine, - *args, **kwargs): - this = helpers.get_this(context) - return Interfaces(this) - - -class MuranoTypeParameter(yaqltypes.PythonType): - def __init__(self, base_type=None, nullable=False, context=None, - resolve_strings=True, lazy=False): - self._context = context - self._base_type = base_type - self._resolve_strings = resolve_strings - self._lazy = lazy - super(MuranoTypeParameter, self).__init__( - (dsl_types.MuranoTypeReference, str), nullable) - - def check(self, value, context, *args, **kwargs): - if not super(MuranoTypeParameter, self).check( - value, context, *args, **kwargs): - return False - if isinstance(value, str): - if not self._resolve_strings: - return False - value = helpers.get_class(value, context).get_reference() - if isinstance(value, dsl_types.MuranoTypeReference): - if not self._base_type: - return True - return self._base_type.is_compatible(value) - return True - - def convert(self, value, sender, context, function_spec, engine, - *args, **kwargs): - - def implementation(ctx=None): - value2 = value - if ctx is None: - ctx = self._context or context - if isinstance(value2, yaql_expressions.Expression): - value2 = value2(utils.NO_VALUE, ctx, engine) - value2 = super(MuranoTypeParameter, self).convert( - value2, sender, ctx, function_spec, engine) - if isinstance(value2, str): - value2 = helpers.get_class(value2, ctx).get_reference() - if self._base_type and not self._base_type.is_compatible(value): - raise ValueError('Value must be subtype of {0}'.format( - self._base_type.name - )) - return value2 - - if self._lazy: - return implementation - return implementation() - - -class MuranoObjectInterface(dsl_types.MuranoObjectInterface): - class DataInterface(object): - def __init__(self, object_interface): - object.__setattr__(self, '__object_interface', object_interface) - - def __getattr__(self, item): - oi = getattr(self, '__object_interface') - return oi[item] - - def __setattr__(self, key, value): - oi = getattr(self, '__object_interface') - oi[key] = value - - class CallInterface(object): - def __init__(self, object_interface): - self.__object_interface = object_interface - - def __getattr__(self, item): - def func(*args, **kwargs): - self._insert_instruction() - with helpers.with_object_store( - self.__object_interface._object_store): - context = helpers.get_context() - obj = self.__object_interface.object - return to_mutable(obj.type.invoke( - item, obj, args, kwargs, - context), helpers.get_yaql_engine(context)) - return func - - @staticmethod - def _insert_instruction(): - context = helpers.get_context() - if context: - frame = inspect.stack()[2] - location = dsl_types.ExpressionFilePosition( - os.path.abspath(frame[1]), frame[2], - -1, frame[2], -1) - context[constants.CTX_CURRENT_INSTRUCTION] = NativeInstruction( - frame[4][0].strip(), location) - - def __init__(self, mpl_object): - self._object = mpl_object - self._object_store = helpers.get_object_store() - - @staticmethod - def create(mpl_object): - if mpl_object is None or isinstance(mpl_object, MuranoObjectInterface): - return mpl_object - return MuranoObjectInterface(mpl_object) - - @property - def object(self): - return self._object - - @property - def id(self): - return self.object.object_id - - @property - def owner(self): - owner = self.object.owner - return MuranoObjectInterface.create(owner) - - def find_owner(self, type, optional=False): - if isinstance(type, str): - type = helpers.get_class(type) - elif isinstance(type, dsl_types.MuranoTypeReference): - type = type.type - owner = helpers.find_object_owner( - self.object, lambda t: type.is_compatible(t)) - if owner: - return MuranoObjectInterface(owner) - if not optional: - raise ValueError('Object is not owned by any instance of type ' - '{0}'.format(type.name)) - return None - - @property - def type(self): - return self.object.type - - @property - def package(self): - return self.type.package - - @property - def properties(self): - return MuranoObjectInterface.DataInterface(self) - - @property - def name(self): - return self.object.name - - @property - def extension(self): - return self.object.extension - - def cast(self, murano_class, version_spec=None): - return MuranoObjectInterface.create( - helpers.cast( - self.object, murano_class, - version_spec or helpers.get_type())) - - def is_instance_of(self, murano_class, version_spec=None): - return helpers.is_instance_of( - self.object, murano_class, - version_spec or helpers.get_type()) - - def ancestors(self): - return self.type.ancestors() - - def __getitem__(self, item): - context = helpers.get_context() - return to_mutable( - self.object.get_property(item, context), - helpers.get_yaql_engine(context)) - - def __setitem__(self, key, value): - context = helpers.get_context() - value = helpers.evaluate(value, context) - self.object.set_property(key, value, context) - - def __call__(self): - return MuranoObjectInterface.CallInterface(self) - - def __repr__(self): - return '<{0}>'.format(repr(self.object)) - - def __eq__(self, other): - if isinstance(other, MuranoObjectInterface): - return self.object == other.object - else: - return False - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - return hash(self.object) - - -class Interfaces(object): - def __init__(self, mpl_object): - self.__object = mpl_object - - def yaql(self, receiver=utils.NO_VALUE): - return yaql_interface.YaqlInterface( - helpers.get_context(), helpers.get_yaql_engine(), receiver) - - def this(self): - return self.methods(self.__object) - - def methods(self, mpl_object): - return MuranoObjectInterface.create(mpl_object) - - @property - def execution_session(self): - return helpers.get_execution_session() - - @property - def caller(self): - caller_context = helpers.get_caller_context() - if caller_context is None: - return None - return get_this(caller_context) - - @property - def attributes(self): - executor = helpers.get_executor() - return executor.attribute_store - - @property - def class_config(self): - return self.__object.type.package.get_class_config( - self.__object.type.name) - - @property - def package_loader(self): - return helpers.get_package_loader() - - -class NativeInstruction(object): - def __init__(self, instruction, location): - self.instruction = instruction - self.source_file_position = location - - def __str__(self): - return self.instruction - - -def to_mutable(obj, yaql_engine=None): - if yaql_engine is None: - yaql_engine = helpers.get_yaql_engine() - - def converter(value, limit_func, engine, rec): - if isinstance(value, dsl_types.MuranoObject): - return MuranoObjectInterface.create(value) - else: - return utils.convert_output_data(value, limit_func, engine, rec) - - def limiter(it): - return utils.limit_iterable(it, CONF.murano.dsl_iterators_limit) - - return converter(obj, limiter, yaql_engine, converter) - - -def meta(type_name, value): - def wrapper(func): - fd = specs.get_function_definition(func) - mpl_meta = fd.meta.get(constants.META_MPL_META, []) - mpl_meta.append({type_name: value}) - specs.meta(type_name, mpl_meta)(func) - return wrapper - - -def get_this(context=None): - this = helpers.get_this(context) - return MuranoObjectInterface.create(this) - - -def get_execution_session(): - return helpers.get_execution_session() - - -def spawn(func, *args, **kwargs): - context = helpers.get_context() - object_store = helpers.get_object_store() - - def wrapper(): - with helpers.with_object_store(object_store): - with helpers.contextual(context): - return func(*args, **kwargs) - - return eventlet.spawn(wrapper) - - -def new(properties, owner=None, type=None): - context = helpers.get_context() - return helpers.get_object_store().load( - properties, owner, type or get_this(context).type, context=context) diff --git a/murano/dsl/dsl_exception.py b/murano/dsl/dsl_exception.py deleted file mode 100644 index 6e0d69776..000000000 --- a/murano/dsl/dsl_exception.py +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 sys - -from murano.dsl.principal_objects import stack_trace - - -class MuranoPlException(Exception): - def __init__(self, names, message, stacktrace, extra=None, cause=None): - super(MuranoPlException, self).__init__( - u'[{0}]: {1}'.format(', '.join(names), message)) - if not isinstance(names, list): - names = [names] - self._names = names - self._message = message - self._stacktrace = stacktrace - self._extra = extra or {} - self._cause = cause - - @property - def names(self): - return self._names - - @property - def message(self): - return self._message - - @property - def stacktrace(self): - return self._stacktrace - - @property - def extra(self): - return self._extra - - @property - def cause(self): - return self._cause - - @staticmethod - def from_python_exception(exception, context): - stacktrace = stack_trace.create_stack_trace(context) - exception_type = type(exception) - builtins_module = 'builtins' - module = exception_type.__module__ - if module == builtins_module: - names = [exception_type.__name__] - else: - names = ['{0}.{1}'.format(exception_type.__module__, - exception_type.__name__)] - - result = MuranoPlException( - names, str(exception), stacktrace) - _, _, exc_traceback = sys.exc_info() - result.original_exception = exception - result.original_traceback = exc_traceback - return result - - def _format_name(self): - if not self._names: - return '' - elif len(self._names) == 1: - return self._names[0] - else: - return self._names - - def format(self, prefix=''): - text = ('{3}{0}: {1}\n' - '{3}Traceback (most recent call last):\n' - '{2}').format( - self._format_name(), self.message, - self.stacktrace().toString(prefix + ' '), prefix) - if self._cause is not None: - text += '\n\n{0} Caused by {1}'.format( - prefix, self._cause.format(prefix + ' ').lstrip()) - return text diff --git a/murano/dsl/dsl_types.py b/murano/dsl/dsl_types.py deleted file mode 100644 index af6bbf143..000000000 --- a/murano/dsl/dsl_types.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 weakref - - -class ClassUsages(object): - Class = 'Class' - Meta = 'Meta' - All = {Class, Meta} - - -class MetaCardinality(object): - One = 'One' - Many = 'Many' - All = {One, Many} - - -class MetaTargets(object): - Package = 'Package' - Type = 'Type' - Property = 'Property' - Method = 'Method' - Argument = 'Argument' - All = {Package, Type, Property, Method, Argument} - - -class PropertyUsages(object): - In = 'In' - Out = 'Out' - InOut = 'InOut' - Runtime = 'Runtime' - Const = 'Const' - Config = 'Config' - Static = 'Static' - All = {In, Out, InOut, Runtime, Const, Config, Static} - Writable = {Out, InOut, Runtime, Static, Config} - - -class MethodUsages(object): - Action = 'Action' - Runtime = 'Runtime' - Static = 'Static' - Extension = 'Extension' - - All = {Action, Runtime, Static, Extension} - InstanceMethods = {Runtime, Action} - StaticMethods = {Static, Extension} - - -class MethodScopes(object): - Session = 'Session' - Public = 'Public' - - All = {Session, Public} - - -class MethodArgumentUsages(object): - Standard = 'Standard' - VarArgs = 'VarArgs' - KwArgs = 'KwArgs' - All = {Standard, VarArgs, KwArgs} - - -class DumpTypes(object): - Serializable = 'Serializable' - Inline = 'Inline' - Mixed = 'Mixed' - All = {Serializable, Inline, Mixed} - - -class MuranoType(object): - pass - - -class MuranoClass(MuranoType): - pass - - -class MuranoMetaClass(MuranoClass): - pass - - -class MuranoObject(object): - pass - - -class MuranoMethod(object): - pass - - -class MuranoMethodArgument(object): - pass - - -class MuranoPackage(object): - pass - - -class MuranoProperty(object): - pass - - -class MuranoTypeReference(object): - def __init__(self, murano_type): - self.__murano_type = weakref.ref(murano_type) - - @property - def type(self): - return self.__murano_type() - - def __repr__(self): - return '*' + repr(self.type) - - def __eq__(self, other): - if not isinstance(other, MuranoTypeReference): - return False - return self.type == other.type - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return hash(self.type) - - -class YaqlExpression(object): - pass - - -class MuranoObjectInterface(object): - pass - - -class ExpressionFilePosition(object): - def __init__(self, file_path, start_line, start_column, - end_line, end_column): - self._file_path = file_path - self._start_line = start_line - self._start_column = start_column - self._end_line = end_line - self._end_column = end_column - - @property - def file_path(self): - return self._file_path - - @property - def start_line(self): - return self._start_line - - @property - def start_column(self): - return self._start_column - - @property - def end_line(self): - return self._end_line - - @property - def end_column(self): - return self._end_column diff --git a/murano/dsl/exceptions.py b/murano/dsl/exceptions.py deleted file mode 100644 index 8241b8c15..000000000 --- a/murano/dsl/exceptions.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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. - - -class InternalFlowException(Exception): - pass - - -class ReturnException(InternalFlowException): - def __init__(self, value): - self._value = value - - @property - def value(self): - return self._value - - -class BreakException(InternalFlowException): - pass - - -class ContinueException(InternalFlowException): - pass - - -class DslInvalidOperationError(Exception): - pass - - -class NoMethodFound(Exception): - def __init__(self, name): - super(NoMethodFound, self).__init__('Method "%s" is not found' % name) - - -class NoPropertyFound(Exception): - def __init__(self, name): - super(NoPropertyFound, self).__init__( - 'Property "%s" is not found' % name) - - -class NoClassFound(Exception): - def __init__(self, name, packages=None): - if packages is None: - packages = [] - packages = ', '.join("{0}/{1}".format(p.name, p.version) - for p in packages) - super(NoClassFound, self).__init__( - 'Class "{0}" is not found in {1}'.format(name, packages)) - - -class NoPackageFound(Exception): - def __init__(self, name): - super(NoPackageFound, self).__init__( - 'Package "%s" is not found' % name) - - -class NoPackageForClassFound(Exception): - def __init__(self, name): - super(NoPackageForClassFound, self).__init__('Package for class "%s" ' - 'is not found' % name) - - -class NoObjectFoundError(Exception): - def __init__(self, object_id): - super(NoObjectFoundError, self).__init__( - 'Object "%s" is not found in object store' % object_id) - - -class MethodNotExposed(Exception): - pass - - -class AmbiguousMethodName(Exception): - def __init__(self, name): - super(AmbiguousMethodName, self).__init__( - 'Found more than one method "%s"' % name) - - -class AmbiguousClassName(Exception): - def __init__(self, name): - super(AmbiguousClassName, self).__init__( - 'Found more than one version of class "%s"' % name) - - -class DslContractSyntaxError(Exception): - pass - - -class ContractViolationException(Exception): - def __init__(self, *args, **kwargs): - super(ContractViolationException, self).__init__(*args, **kwargs) - self._path = '' - - @property - def path(self): - return self._path - - @path.setter - def path(self, value): - self._path = value - - -class ValueIsMissingError(Exception): - pass - - -class DslSyntaxError(Exception): - pass - - -class PropertyAccessError(Exception): - pass - - -class AmbiguousPropertyNameError(PropertyAccessError): - def __init__(self, name): - super(AmbiguousPropertyNameError, self).__init__( - 'Found more than one property "%s"' % name) - - -class NoWriteAccess(PropertyAccessError): - def __init__(self, name): - super(NoWriteAccess, self).__init__( - 'Property "%s" is immutable to the caller' % name) - - -class NoWriteAccessError(PropertyAccessError): - def __init__(self, name): - super(NoWriteAccessError, self).__init__( - 'Property "%s" is immutable to the caller' % name) - - -class PropertyReadError(PropertyAccessError): - def __init__(self, name, murano_class): - super(PropertyAccessError, self).__init__( - 'Property "%s" in class "%s" cannot be read' % - (name, murano_class.name)) - - -class PropertyWriteError(PropertyAccessError): - def __init__(self, name, murano_class): - super(PropertyAccessError, self).__init__( - 'Property "%s" in class "%s" cannot be written' % - (name, murano_class.name)) - - -class UninitializedPropertyAccessError(PropertyAccessError): - def __init__(self, name, murano_class): - super(PropertyAccessError, self).__init__( - 'Access to uninitialized property ' - '"%s" in class "%s" is forbidden' % (name, murano_class.name)) - - -class CircularExpressionDependenciesError(Exception): - pass - - -class InvalidLhsTargetError(Exception): - def __init__(self, target): - super(InvalidLhsTargetError, self).__init__( - 'Invalid assignment target "%s"' % target) - - -class InvalidInheritanceError(Exception): - pass - - -class ObjectDestroyedError(Exception): - def __init__(self, obj): - super(ObjectDestroyedError, self).__init__( - 'Object {0} is already destroyed'.format(obj)) diff --git a/murano/dsl/executor.py b/murano/dsl/executor.py deleted file mode 100644 index 84cdc7c81..000000000 --- a/murano/dsl/executor.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 contextlib -import itertools -import traceback - -import eventlet -import eventlet.event -from oslo_log import log as logging -from yaql.language import exceptions as yaql_exceptions -from yaql.language import specs -from yaql.language import utils - -from murano.dsl import attribute_store -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_exception -from murano.dsl import dsl_types -from murano.dsl import exceptions as dsl_exceptions -from murano.dsl import helpers -from murano.dsl import object_store -from murano.dsl.principal_objects import stack_trace -from murano.dsl import serializer -from murano.dsl import yaql_integration - -LOG = logging.getLogger(__name__) - - -class MuranoDslExecutor(object): - def __init__(self, package_loader, context_manager, session=None): - self._package_loader = package_loader - self._context_manager = context_manager - self._session = session - self._attribute_store = attribute_store.AttributeStore() - self._object_store = object_store.ObjectStore(self) - self._locks = {} - self._root_context_cache = {} - self._static_properties = {} - - @property - def object_store(self): - return self._object_store - - @property - def execution_session(self): - return self._session - - @property - def attribute_store(self): - return self._attribute_store - - @property - def package_loader(self): - return self._package_loader - - @property - def context_manager(self): - return self._context_manager - - def invoke_method(self, method, this, context, args, kwargs, - skip_stub=False, invoke_action=True): - if isinstance(this, dsl.MuranoObjectInterface): - this = this.object - kwargs = utils.filter_parameters_dict(kwargs) - runtime_version = method.declaring_type.package.runtime_version - yaql_engine = yaql_integration.choose_yaql_engine(runtime_version) - if context is None or not skip_stub: - actions_only = (context is None and not method.name.startswith('.') - and invoke_action) - method_context = self.create_method_context( - self.create_object_context(this, context), method) - method_context[constants.CTX_SKIP_FRAME] = True - method_context[constants.CTX_ACTIONS_ONLY] = actions_only - - stub = method.static_stub if isinstance( - this, dsl_types.MuranoType) else method.instance_stub - if stub is None: - raise ValueError( - 'Method {0} cannot be called on receiver {1}'.format( - method, this)) - - real_this = this.real_this if isinstance( - this, dsl_types.MuranoObject) else this.get_reference() - return stub(yaql_engine, method_context, real_this)( - *args, **kwargs) - - if context[constants.CTX_ACTIONS_ONLY] and not method.is_action: - raise dsl_exceptions.MethodNotExposed( - '{0} is not an action'.format(method.name)) - - if method.is_static: - obj_context = self.create_object_context( - method.declaring_type, context) - else: - obj_context = self.create_object_context(this, context) - context = self.create_method_context(obj_context, method) - - if isinstance(this, dsl_types.MuranoObject): - if this.destroyed: - raise dsl_exceptions.ObjectDestroyedError(this) - this = this.real_this - - if method.arguments_scheme is not None: - args, kwargs = self._canonize_parameters( - method.arguments_scheme, args, kwargs, method.name, this) - - this_lock = this - arg_values_for_lock = {} - method_meta = [m for m in method.get_meta(context) - if m.type.name == ('io.murano.metadata.' - 'engine.Synchronize')] - if method_meta: - method_meta = method_meta[0] - - if method_meta: - if not method_meta.get_property('onThis', context): - this_lock = None - for arg_name in method_meta.get_property('onArgs', context): - arg_val = kwargs.get(arg_name) - if arg_val is not None: - arg_values_for_lock[arg_name] = arg_val - - arg_values_for_lock = utils.filter_parameters_dict(arg_values_for_lock) - - with self._acquire_method_lock(method, this_lock, arg_values_for_lock): - for i, arg in enumerate(args, 2): - context[str(i)] = arg - for key, value in kwargs.items(): - context[key] = value - - def call(): - if isinstance(method.body, specs.FunctionDefinition): - if isinstance(this, dsl_types.MuranoType): - native_this = this.get_reference() - else: - native_this = dsl.MuranoObjectInterface(this.cast( - method.declaring_type)) - return method.body( - yaql_engine, context, native_this)(*args, **kwargs) - else: - context[constants.CTX_NAMES_SCOPE] = \ - method.declaring_type - return (None if method.body is None - else method.body.execute(context)) - - if (not isinstance(method.body, specs.FunctionDefinition) or - not method.body.meta.get(constants.META_NO_TRACE)): - with self._log_method(context, args, kwargs) as log: - result = call() - log(result) - return result - else: - return call() - - @contextlib.contextmanager - def _acquire_method_lock(self, method, this, arg_val_dict): - if this is None: - if not arg_val_dict: - # if neither "this" nor argument values are set then no - # locking is needed - key = None - else: - # if only the argument values are passed then find the lock - # list only by the method - key = (None, id(method)) - else: - if method.is_static: - # find the lock list by the type and method - key = (id(method.declaring_type), id(method)) - else: - # find the lock list by the object and method - key = (this.object_id, id(method)) - thread_id = helpers.get_current_thread_id() - while True: - event, event_owner = None, None - if key is None: # no locking needed - break - - lock_list = self._locks.setdefault(key, []) - # lock list contains a list of tuples: - # first item of each tuple is a dict with the values of locking - # arguments (it is used for argument values comparison), - # second item is an event to wait on, - # third one is the owner thread id - - # If this lock list is empty it means no locks on this object and - # method at all. - for arg_vals, l_event, l_event_owner in lock_list: - if arg_vals == arg_val_dict: - event = l_event - event_owner = l_event_owner - break - - if event: - if event_owner == thread_id: - # this means a re-entrant lock: the tuple with the same - # value of the first element exists in the list, but it was - # acquired by the same green thread. We may proceed with - # the call in this case - event = None - break - else: - event.wait() - else: - # this means either the lock list was empty or didn't contain a - # tuple with the first element equal to arg_val_dict. - # Then let's acquire a lock, i.e. create a new tuple and place - # it into the list - event = eventlet.event.Event() - event_owner = thread_id - lock_list.append((arg_val_dict, event, event_owner)) - break - try: - yield - finally: - if event is not None: - lock_list.remove((arg_val_dict, event, event_owner)) - if len(lock_list) == 0: - del self._locks[key] - event.send() - - @contextlib.contextmanager - def _log_method(self, context, args, kwargs): - method = helpers.get_current_method(context) - param_gen = itertools.chain( - (str(arg) for arg in args), - (u'{0} => {1}'.format(name, value) - for name, value in kwargs.items())) - params_str = u', '.join(param_gen) - method_name = '::'.join((method.declaring_type.name, method.name)) - thread_id = helpers.get_current_thread_id() - caller_str = '' - caller_ctx = helpers.get_caller_context(context) - if caller_ctx is not None: - frame = stack_trace.compose_stack_frame(caller_ctx) - if frame['location']: - caller_str = ' called from ' + stack_trace.format_frame(frame) - - LOG.trace(u'{thread}: Begin execution {method}({params}){caller}' - .format(thread=thread_id, method=method_name, - params=params_str, caller=caller_str)) - try: - def log_result(result): - LOG.trace( - u'{thread}: End execution {method} with result ' - u'{result}'.format( - thread=thread_id, method=method_name, result=result)) - yield log_result - except Exception as e: - LOG.trace( - u'{thread}: End execution {method} with exception ' - u'{exc}'.format(thread=thread_id, method=method_name, exc=e)) - raise - - @staticmethod - def _canonize_parameters(arguments_scheme, args, kwargs, - method_name, receiver): - arg_names = list(arguments_scheme.keys()) - parameter_values = {} - varargs_arg = None - vararg_values = [] - kwargs_arg = None - kwarg_values = {} - for name, definition in arguments_scheme.items(): - if definition.usage == dsl_types.MethodArgumentUsages.VarArgs: - varargs_arg = name - parameter_values[name] = vararg_values - elif definition.usage == dsl_types.MethodArgumentUsages.KwArgs: - kwargs_arg = name - parameter_values[name] = kwarg_values - - for i, arg in enumerate(args): - name = None if i >= len(arg_names) else arg_names[i] - if name is None or name in (varargs_arg, kwargs_arg): - if varargs_arg: - vararg_values.append(arg) - else: - raise yaql_exceptions.NoMatchingMethodException( - method_name, receiver) - else: - parameter_values[name] = arg - - for name, value in utils.filter_parameters_dict(kwargs).items(): - if name in arguments_scheme and name not in ( - varargs_arg, kwargs_arg): - parameter_values[name] = value - elif kwargs_arg: - kwarg_values[name] = value - else: - raise yaql_exceptions.NoMatchingMethodException( - method_name, receiver) - return tuple(), parameter_values - - def load(self, data): - with helpers.with_object_store(self.object_store): - return self._load(data) - - def _load(self, data): - if not isinstance(data, dict): - raise TypeError() - self._attribute_store.load(data.get(constants.DM_ATTRIBUTES) or []) - model = data.get(constants.DM_OBJECTS) - if model is None: - result = None - else: - result = self._object_store.load(model, None, keep_ids=True) - model_copy = data.get(constants.DM_OBJECTS_COPY) - if model_copy: - self._object_store.load(model_copy, None, keep_ids=True) - return dsl.MuranoObjectInterface.create(result) - - def signal_destruction_dependencies(self, *objects): - if not objects: - return - elif len(objects) > 1: - return helpers.parallel_select( - objects, self.signal_destruction_dependencies) - - obj = objects[0] - if obj.destroyed: - return - for dependency in obj.destruction_dependencies: - try: - handler = dependency['handler'] - if handler: - subscriber = dependency['subscriber'] - if subscriber: - subscriber = subscriber() - if (subscriber and - subscriber.initialized and - not subscriber.destroyed): - method = subscriber.type.find_single_method(handler) - self.invoke_method( - method, subscriber, None, [obj], {}, - invoke_action=False) - except Exception as e: - LOG.warning('Muted exception during destruction dependency ' - 'execution in {0}: {1}'.format(obj, e), - exc_info=True) - obj.load_dependencies(None) - - def destroy_objects(self, *objects): - if not objects: - return - elif len(objects) > 1: - return helpers.parallel_select( - objects, self.destroy_objects) - - obj = objects[0] - if obj.destroyed: - return - methods = obj.type.find_methods(lambda m: m.name == '.destroy') - for method in methods: - try: - method.invoke(obj, (), {}, None) - except Exception as e: - if isinstance(e, dsl_exception.MuranoPlException): - tb = e.format(prefix=' ') - else: - tb = traceback.format_exc() - LOG.warning( - 'Muted exception during execution of .destroy ' - 'on {0}: {1}'.format(obj, tb), exc_info=True) - - def create_root_context(self, runtime_version): - context = self._root_context_cache.get(runtime_version) - if not context: - context = self.context_manager.create_root_context(runtime_version) - self._root_context_cache[runtime_version] = context - return context - - def create_package_context(self, package): - root_context = self.create_root_context(package.runtime_version) - context = helpers.link_contexts( - root_context, - self.context_manager.create_package_context(package)) - return context - - def create_type_context(self, murano_type, caller_context=None): - package_context = self.create_package_context( - murano_type.package) - context = helpers.link_contexts( - package_context, - self.context_manager.create_type_context( - murano_type)).create_child_context() - context[constants.CTX_TYPE] = murano_type - if caller_context: - context[constants.CTX_NAMES_SCOPE] = caller_context[ - constants.CTX_NAMES_SCOPE] - return context - - def create_object_context(self, obj, caller_context=None): - if isinstance(obj, dsl_types.MuranoClass): - obj_type = obj - obj = None - else: - obj_type = obj.type - class_context = self.create_type_context(obj_type) - if obj is not None: - context = helpers.link_contexts( - class_context, self.context_manager.create_object_context( - obj)).create_child_context() - context[constants.CTX_THIS] = obj.real_this - context['this'] = obj.real_this - context[''] = obj.real_this - else: - context = class_context.create_child_context() - type_ref = obj_type.get_reference() - context[constants.CTX_THIS] = type_ref - context['this'] = type_ref - context[''] = type_ref - - if caller_context is not None: - caller = caller_context - while caller is not None and caller[constants.CTX_SKIP_FRAME]: - caller = caller[constants.CTX_CALLER_CONTEXT] - context[constants.CTX_NAMES_SCOPE] = caller_context[ - constants.CTX_NAMES_SCOPE] - context[constants.CTX_CALLER_CONTEXT] = caller - context[constants.CTX_ALLOW_PROPERTY_WRITES] = caller_context[ - constants.CTX_ALLOW_PROPERTY_WRITES] - else: - context[constants.CTX_NAMES_SCOPE] = obj_type - return context - - @staticmethod - def create_method_context(object_context, method): - context = object_context.create_child_context() - context[constants.CTX_CURRENT_METHOD] = method - return context - - def run(self, cls, method_name, this, args, kwargs): - with helpers.with_object_store(self.object_store): - return cls.invoke(method_name, this, args, kwargs) - - def get_static_property(self, murano_type, name, context): - prop = murano_type.find_static_property(name) - cls = prop.declaring_type - value = self._static_properties.get(prop, prop.default) - return prop.transform(value, cls, None, context) - - def set_static_property(self, murano_type, name, value, - context, dry_run=False): - prop = murano_type.find_static_property(name) - cls = prop.declaring_type - value = prop.transform(value, cls, None, context) - if not dry_run: - self._static_properties[prop] = prop.finalize( - value, cls, context) - - def finalize(self, model_root=None): - # NOTE(ksnihyr): should be no-except - try: - if model_root: - used_objects = serializer.collect_objects(model_root) - self.object_store.prepare_finalize(used_objects) - model = serializer.serialize_model(model_root, self) - self.object_store.finalize() - else: - model = None - self.object_store.prepare_finalize(None) - self.object_store.finalize() - self._static_properties.clear() - return model - except Exception as e: - LOG.exception( - "Exception %s occurred" - " during MuranoDslExecutor finalization", e) - return None - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.finalize() diff --git a/murano/dsl/expressions.py b/murano/dsl/expressions.py deleted file mode 100644 index ef1f54f3e..000000000 --- a/murano/dsl/expressions.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl_exception -from murano.dsl import helpers -from murano.dsl import lhs_expression -from murano.dsl import yaql_expression - -_macros = [] - - -class InstructionStub(object): - def __init__(self, title, position): - self._title = title - self.source_file_position = position - - def __str__(self): - return self._title - - -def register_macro(cls): - _macros.append(cls) - - -class DslExpression(object): - def execute(self, context): - pass - - -class Statement(DslExpression): - def __init__(self, statement): - if isinstance(statement, yaql_expression.YaqlExpression): - key = None - value = statement - elif isinstance(statement, dict): - if len(statement) != 1: - raise SyntaxError() - key = list(statement.keys())[0] - value = statement[key] - else: - raise SyntaxError() - - self._destination = lhs_expression.LhsExpression(key) if key else None - self._expression = value - - @property - def destination(self): - return self._destination - - @property - def expression(self): - return self._expression - - def execute(self, context): - try: - result = helpers.evaluate(self.expression, context) - if self.destination: - self.destination(result, context) - return result - except dsl_exception.MuranoPlException: - raise - except Exception as e: - raise dsl_exception.MuranoPlException.from_python_exception( - e, context) - - -def parse_expression(expr): - result = None - if isinstance(expr, yaql_expression.YaqlExpression): - result = Statement(expr) - elif isinstance(expr, dict): - kwds = {} - for key, value in expr.items(): - if isinstance(key, yaql_expression.YaqlExpression): - if result is not None: - raise ValueError() - result = Statement(expr) - else: - kwds[key] = value - - if result is None: - for cls in _macros: - try: - macro = cls(**kwds) - position = None - title = 'block construct' - if hasattr(expr, 'source_file_position'): - position = expr.source_file_position - if '__str__' in cls.__dict__: - title = str(macro) - macro.virtual_instruction = InstructionStub( - title, position) - return macro - except TypeError: - continue - - if result is None: - raise SyntaxError( - 'Syntax is incorrect in expression: {0}'.format(expr)) - return result diff --git a/murano/dsl/helpers.py b/murano/dsl/helpers.py deleted file mode 100644 index 2270b437c..000000000 --- a/murano/dsl/helpers.py +++ /dev/null @@ -1,748 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 collections -import contextlib -import functools -import gc -import inspect -import itertools -import re -import sys -import uuid -import weakref - -import eventlet.greenpool -import eventlet.greenthread -from oslo_config import cfg -import semantic_version -from yaql.language import contexts -import yaql.language.exceptions -import yaql.language.expressions -from yaql.language import utils as yaqlutils - -from murano.common import utils -from murano.dsl import constants -from murano.dsl import dsl_types -from murano.dsl import exceptions - -_threads_sequencer = 0 -# type string: ns.something.MyApp[/1.2.3-alpha][@my.package.fqn] -TYPE_RE = re.compile(r'([a-zA-Z0-9_.]+)(?:/([^@]+))?(?:@([a-zA-Z0-9_.]+))?$') -CONF = cfg.CONF - - -def evaluate(value, context, freeze=True): - list_type = tuple if freeze else list - dict_type = yaqlutils.FrozenDict if freeze else dict - set_type = frozenset if freeze else set - - if isinstance(value, (dsl_types.YaqlExpression, - yaql.language.expressions.Statement)): - return value(context) - elif isinstance(value, yaqlutils.MappingType): - return dict_type( - (evaluate(d_key, context, freeze), - evaluate(d_value, context, freeze)) - for d_key, d_value in value.items()) - elif yaqlutils.is_sequence(value): - return list_type(evaluate(t, context, freeze) for t in value) - elif isinstance(value, yaqlutils.SetType): - return set_type(evaluate(t, context, freeze) for t in value) - elif yaqlutils.is_iterable(value): - return list_type( - evaluate(t, context, freeze) - for t in yaqlutils.limit_iterable( - value, CONF.murano.dsl_iterators_limit)) - elif isinstance(value, dsl_types.MuranoObjectInterface): - return value.object - else: - return value - - -def merge_lists(list1, list2): - result = [] - for item in list1 + list2: - if item not in result: - result.append(item) - return result - - -def merge_dicts(dict1, dict2, max_levels=0): - result = {} - for key, value1 in dict1.items(): - result[key] = value1 - if key in dict2: - value2 = dict2[key] - if type(value2) != type(value1): - if ((isinstance(value1, str) or value1 is None) and - (isinstance(value2, str) or value2 is None)): - continue - raise TypeError() - if max_levels != 1 and isinstance(value2, dict): - result[key] = merge_dicts( - value1, value2, - 0 if max_levels == 0 else max_levels - 1) - elif max_levels != 1 and isinstance(value2, list): - result[key] = merge_lists(value1, value2) - else: - result[key] = value2 - for key, value1 in dict2.items(): - if key not in result: - result[key] = value1 - return result - - -def generate_id(): - return uuid.uuid4().hex - - -def parallel_select(collection, func, limit=1000): - # workaround for eventlet issue 232 - # https://github.com/eventlet/eventlet/issues/232 - context = get_context() - object_store = get_object_store() - - def wrapper(element): - try: - with with_object_store(object_store), contextual(context): - return func(element), False, None - except Exception as e: - return e, True, sys.exc_info()[2] - - gpool = eventlet.greenpool.GreenPool(limit) - result = list(gpool.imap(wrapper, collection)) - try: - exception = next(t for t in result if t[1]) - except StopIteration: - return map(lambda t: t[0], result) - else: - utils.reraise(type(exception[0]), exception[0], exception[2]) - - -def enum(**enums): - return type('Enum', (), enums) - - -def get_context(): - current_thread = eventlet.greenthread.getcurrent() - return getattr(current_thread, constants.TL_CONTEXT, None) - - -def get_executor(): - store = get_object_store() - return None if store is None else store.executor - - -def get_type(context=None): - context = context or get_context() - return context[constants.CTX_TYPE] - - -def get_execution_session(): - executor = get_executor() - return None if executor is None else executor.execution_session - - -def get_object_store(): - current_thread = eventlet.greenthread.getcurrent() - return getattr(current_thread, constants.TL_OBJECT_STORE, None) - - -def get_package_loader(): - executor = get_executor() - return None if executor is None else executor.package_loader - - -def get_this(context=None): - context = context or get_context() - return context[constants.CTX_THIS] - - -def get_caller_context(context=None): - context = context or get_context() - return context[constants.CTX_CALLER_CONTEXT] - - -def get_attribute_store(): - executor = get_executor() - return None if executor is None else executor.attribute_store - - -def get_current_instruction(context=None): - context = context or get_context() - return context[constants.CTX_CURRENT_INSTRUCTION] - - -def get_current_method(context=None): - context = context or get_context() - return context[constants.CTX_CURRENT_METHOD] - - -def get_yaql_engine(context=None): - context = context or get_context() - return None if context is None else context[constants.CTX_YAQL_ENGINE] - - -def get_current_exception(context=None): - context = context or get_context() - return context[constants.CTX_CURRENT_EXCEPTION] - - -def are_property_modifications_allowed(context=None): - context = context or get_context() - return context[constants.CTX_ALLOW_PROPERTY_WRITES] or False - - -def get_names_scope(context=None): - context = context or get_context() - return context[constants.CTX_NAMES_SCOPE] - - -def get_class(name, context=None): - context = context or get_context() - murano_type = get_names_scope(context) - name = murano_type.namespace_resolver.resolve_name(name) - return murano_type.package.find_class(name) - - -def get_contract_passkey(): - current_thread = eventlet.greenthread.getcurrent() - return getattr(current_thread, constants.TL_CONTRACT_PASSKEY, None) - - -def is_objects_dry_run_mode(): - current_thread = eventlet.greenthread.getcurrent() - return bool(getattr(current_thread, constants.TL_OBJECTS_DRY_RUN, False)) - - -def get_current_thread_id(): - global _threads_sequencer - - current_thread = eventlet.greenthread.getcurrent() - thread_id = getattr(current_thread, constants.TL_ID, None) - if thread_id is None: - thread_id = 'T' + str(_threads_sequencer) - _threads_sequencer += 1 - setattr(current_thread, constants.TL_ID, thread_id) - return thread_id - - -@contextlib.contextmanager -def thread_local_attribute(name, value): - current_thread = eventlet.greenthread.getcurrent() - old_value = getattr(current_thread, name, None) - if value is not None: - setattr(current_thread, name, value) - elif hasattr(current_thread, name): - delattr(current_thread, name) - try: - yield - finally: - if old_value is not None: - setattr(current_thread, name, old_value) - elif hasattr(current_thread, name): - delattr(current_thread, name) - - -def contextual(ctx): - return thread_local_attribute(constants.TL_CONTEXT, ctx) - - -def with_object_store(object_store): - return thread_local_attribute(constants.TL_OBJECT_STORE, object_store) - - -def parse_version_spec(version_spec): - if isinstance(version_spec, semantic_version.Spec): - return normalize_version_spec(version_spec) - if isinstance(version_spec, semantic_version.Version): - return normalize_version_spec( - semantic_version.Spec('==' + str(version_spec))) - if not version_spec: - version_spec = '0' - version_spec = re.sub('\s+', '', str(version_spec)) - - # NOTE(kzaitsev): semantic_version 2.3.X thinks that '=0' is not - # a valid version spec and only accepts '==0', this regexp adds - # an extra '=' before such specs - version_spec = re.sub(r'^=(\d)', r'==\1', version_spec) - - if version_spec[0].isdigit(): - version_spec = '==' + str(version_spec) - version_spec = semantic_version.Spec(version_spec) - return normalize_version_spec(version_spec) - - -def parse_version(version): - if isinstance(version, semantic_version.Version): - return version - if not version: - version = '0' - return semantic_version.Version.coerce(str(version)) - - -def traverse(seed, producer=None, track_visited=True): - if not yaqlutils.is_iterable(seed): - seed = [seed] - visited = None if not track_visited else set() - queue = collections.deque(seed) - while queue: - item = queue.popleft() - if track_visited: - if item in visited: - continue - visited.add(item) - produced = (yield item) - if produced is None and producer: - produced = producer(item) - if produced: - queue.extend(produced) - - -def cast(obj, murano_class, pov_or_version_spec=None): - if isinstance(obj, dsl_types.MuranoObjectInterface): - obj = obj.object - if isinstance(pov_or_version_spec, dsl_types.MuranoType): - pov_or_version_spec = pov_or_version_spec.package - elif isinstance(pov_or_version_spec, str): - pov_or_version_spec = parse_version_spec(pov_or_version_spec) - - if isinstance(murano_class, dsl_types.MuranoTypeReference): - murano_class = murano_class.type - if isinstance(murano_class, dsl_types.MuranoType): - if pov_or_version_spec is None: - pov_or_version_spec = parse_version_spec(murano_class.version) - murano_class = murano_class.name - - candidates = [] - for cls in itertools.chain((obj.type,), obj.type.ancestors()): - if cls.name != murano_class: - continue - elif isinstance(pov_or_version_spec, semantic_version.Version): - if cls.version != pov_or_version_spec: - continue - elif isinstance(pov_or_version_spec, semantic_version.Spec): - if cls.version not in pov_or_version_spec: - continue - elif isinstance(pov_or_version_spec, dsl_types.MuranoPackage): - requirement = pov_or_version_spec.requirements.get( - cls.package.name) - if requirement is None: - raise exceptions.NoClassFound(murano_class) - if cls.version not in requirement: - continue - elif pov_or_version_spec is not None: - raise ValueError('pov_or_version_spec of unsupported ' - 'type {0}'.format(type(pov_or_version_spec))) - candidates.append(cls) - if not candidates: - raise exceptions.NoClassFound(murano_class) - elif len(candidates) > 1: - raise exceptions.AmbiguousClassName(murano_class) - return obj.cast(candidates[0]) - - -def is_instance_of(obj, class_name, pov_or_version_spec=None): - if not isinstance(obj, (dsl_types.MuranoObject, - dsl_types.MuranoObjectInterface)): - return False - try: - cast(obj, class_name, pov_or_version_spec) - return True - except (exceptions.NoClassFound, exceptions.AmbiguousClassName): - return False - - -def memoize(func): - cache = {} - return get_memoize_func(func, cache) - - -def get_memoize_func(func, cache): - @functools.wraps(func) - def wrap(*args): - if args not in cache: - result = func(*args) - cache[args] = result - return result - else: - return cache[args] - return wrap - - -def normalize_version_spec(version_spec): - def coerce(v): - return semantic_version.Version('{0}.{1}.{2}'.format( - v.major, v.minor or 0, v.patch or 0 - )) - - def increment(v): - # NOTE(ativelkov): replace these implementations with next_minor() and - # next_major() calls when the semantic_version is updated in global - # requirements. - if v.minor is None: - return semantic_version.Version( - '.'.join(str(x) for x in [v.major + 1, 0, 0])) - else: - return semantic_version.Version( - '.'.join(str(x) for x in [v.major, v.minor + 1, 0])) - - def extend(v): - return semantic_version.Version(str(v) + '-0') - - transformations = { - '>': [('>=', (increment, extend))], - '>=': [('>=', (coerce,))], - '<': [('<', (coerce, extend))], - '<=': [('<', (increment, extend))], - '!=': [('>=', (increment, extend))], - '==': [('>=', (coerce,)), ('<', (increment, coerce, extend))] - } - - new_parts = [] - for item in version_spec.specs: - if item.kind == '*': - continue - elif item.spec.patch is not None: - new_parts.append(str(item)) - else: - for op, funcs in transformations[item.kind]: - new_parts.append('{0}{1}'.format( - op, - functools.reduce(lambda v, f: f(v), funcs, item.spec) - )) - if not new_parts: - return semantic_version.Spec('*') - return semantic_version.Spec(*new_parts) - - -semver_to_api_map = { - '>': 'gt', - '>=': 'ge', - '<': 'lt', - '<=': 'le', - '!=': 'ne', - '==': 'eq' -} - - -def breakdown_spec_to_query(normalized_spec): - res = [] - for item in normalized_spec.specs: - if item.kind == '*': - continue - else: - res.append("%s:%s" % (semver_to_api_map[item.kind], - item.spec)) - return res - - -def link_contexts(parent_context, context): - if not context: - return parent_context - return contexts.LinkedContext(parent_context, context) - - -def inspect_is_static(cls, name): - m = cls.__dict__.get(name) - if m is None: - return False - return isinstance(m, staticmethod) - - -def inspect_is_classmethod(cls, name): - m = cls.__dict__.get(name) - if m is None: - return False - return isinstance(m, classmethod) - - -def inspect_is_method(cls, name): - m = getattr(cls, name, None) - if m is None: - return False - return ((inspect.isfunction(m) or inspect.ismethod(m)) and not - inspect_is_static(cls, name) and not - inspect_is_classmethod(cls, name)) - - -def inspect_is_property(cls, name): - m = getattr(cls, name, None) - if m is None: - return False - return inspect.isdatadescriptor(m) - - -def updated_dict(d, val): - if d is None: - d = {} - else: - d = d.copy() - if val is not None: - d.update(val) - return d - - -def resolve_type(value, scope_type, return_reference=False): - if value is None: - return None - if isinstance(scope_type, dsl_types.MuranoTypeReference): - scope_type = scope_type.type - if not isinstance(value, (dsl_types.MuranoType, - dsl_types.MuranoTypeReference)): - name = scope_type.namespace_resolver.resolve_name(value) - result = scope_type.package.find_class(name) - else: - result = value - - if isinstance(result, dsl_types.MuranoTypeReference): - if return_reference: - return result - return result.type - elif return_reference: - return result.get_reference() - return result - - -def parse_object_definition(spec, scope_type, context): - if not isinstance(spec, yaqlutils.MappingType): - return None - - if context: - spec = evaluate(spec, context, freeze=False) - else: - spec = spec.copy() - system_data = None - type_obj = None - props = {} - ns_resolver = scope_type.namespace_resolver if scope_type else None - for key in spec: - if (ns_resolver and ns_resolver.is_typename(key, False) or - isinstance(key, (dsl_types.MuranoTypeReference, - dsl_types.MuranoType))): - type_obj = resolve_type(key, scope_type) - props = spec.pop(key) or {} - system_data = spec - break - if system_data is None: - props = spec - if '?' in spec: - system_data = spec.pop('?') - obj_type = system_data.get('type') - if isinstance(obj_type, dsl_types.MuranoTypeReference): - type_obj = obj_type.type - elif isinstance(obj_type, dsl_types.MuranoType): - type_obj = obj_type - elif obj_type: - type_str, version_str, package_str = parse_type_string( - obj_type, - system_data.get('classVersion'), - system_data.get('package') - ) - version_spec = parse_version_spec(version_str) - package_loader = get_package_loader() - if package_str: - package = package_loader.load_package( - package_str, version_spec) - else: - package = package_loader.load_class_package( - type_str, version_spec) - type_obj = package.find_class(type_str, False) - else: - system_data = {} - - return { - 'type': type_obj, - 'properties': yaqlutils.filter_parameters_dict(props), - 'id': system_data.get('id'), - 'name': system_data.get('name'), - 'metadata': system_data.get('metadata'), - 'destroyed': system_data.get('destroyed', False), - 'dependencies': system_data.get('dependencies', {}), - 'extra': { - key: value for key, value in system_data.items() - if key.startswith('_') - } - } - - -def assemble_object_definition(parsed, model_format=dsl_types.DumpTypes.Mixed): - if model_format == dsl_types.DumpTypes.Inline: - result = { - parsed['type']: parsed['properties'], - 'id': parsed['id'], - 'name': parsed['name'], - 'metadata': parsed['metadata'], - 'dependencies': parsed['dependencies'], - 'destroyed': parsed['destroyed'] - } - result.update(parsed['extra']) - return result - result = parsed['properties'] - header = { - 'id': parsed['id'], - 'name': parsed['name'], - 'metadata': parsed['metadata'] - } - if parsed['destroyed']: - header['destroyed'] = True - header.update(parsed['extra']) - result['?'] = header - if model_format == dsl_types.DumpTypes.Mixed: - header['type'] = parsed['type'] - return result - elif model_format == dsl_types.DumpTypes.Serializable: - cls = parsed['type'] - if cls: - header['type'] = format_type_string(cls) - return result - else: - raise ValueError('Invalid Serialization Type') - - -def function(c): - if hasattr(c, '__func__'): - return c.__func__ - return c - - -def list_value(v): - if v is None: - return [] - if not yaqlutils.is_sequence(v): - v = [v] - return v - - -def weak_proxy(obj): - if obj is None or isinstance(obj, weakref.ProxyType): - return obj - if isinstance(obj, weakref.ReferenceType): - obj = obj() - return weakref.proxy(obj) - - -def weak_ref(obj): - class MuranoObjectWeakRef(weakref.ReferenceType): - def __init__(self, murano_object): - self.ref = weakref.ref(murano_object) - self.object_id = murano_object.object_id - - def __call__(self): - res = self.ref() - if not res: - object_store = get_object_store() - if object_store: - res = object_store.get(self.object_id) - if res: - self.ref = weakref.ref(res) - return res - - if obj is None or isinstance(obj, weakref.ReferenceType): - return obj - - if isinstance(obj, dsl_types.MuranoObject): - return MuranoObjectWeakRef(obj) - return weakref.ref(obj) - - -def parse_type_string(type_str, default_version, default_package): - res = TYPE_RE.match(type_str) - if res is None: - return None - parsed_type = res.group(1) - parsed_version = res.group(2) - parsed_package = res.group(3) - return ( - parsed_type, - default_version if parsed_version is None else parsed_version, - default_package if parsed_package is None else parsed_package - ) - - -def format_type_string(type_obj): - if isinstance(type_obj, dsl_types.MuranoTypeReference): - type_obj = type_obj.type - if isinstance(type_obj, dsl_types.MuranoType): - return '{0}/{1}@{2}'.format( - type_obj.name, type_obj.version, type_obj.package.name) - else: - raise ValueError('Invalid argument') - - -def patch_dict(dct, path, value): - parts = path.split('.') - for i in range(len(parts) - 1): - if not isinstance(dct, dict): - dct = None - break - dct = dct.get(parts[i]) - if isinstance(dct, dict): - if value is yaqlutils.NO_VALUE: - dct.pop(parts[-1]) - else: - dct[parts[-1]] = value - - -def format_scalar(value): - if isinstance(value, str): - return "'{0}'".format(value) - return str(value) - - -def is_passkey(value): - passkey = get_contract_passkey() - return passkey is not None and value is passkey - - -def find_object_owner(obj, predicate): - p = obj.owner - while p: - if predicate(p): - return p - p = p.owner - return None - - -# This function is not intended to be used in the code but is very useful -# for debugging object reference leaks -def walk_gc(obj, towards, handler): - visited = set() - queue = collections.deque([(obj, [])]) - while queue: - item, trace = queue.popleft() - if id(item) in visited: - continue - if handler(item): - if towards: - yield trace + [item] - else: - yield [item] + trace - - visited.add(id(item)) - if towards: - try: - queue.extend( - [(t, trace + [item]) for t in gc.get_referrers(item)] - ) - except StopIteration: - return - else: - try: - queue.extend( - [(t, [item] + trace) for t in gc.get_referents(item)] - ) - except StopIteration: - return diff --git a/murano/dsl/lhs_expression.py b/murano/dsl/lhs_expression.py deleted file mode 100644 index d1b47bf52..000000000 --- a/murano/dsl/lhs_expression.py +++ /dev/null @@ -1,191 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 - -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import yaql_functions -from murano.dsl import yaql_integration - - -def _prepare_context(): - @specs.parameter('name', yaqltypes.StringConstant()) - def get_context_data(context, name): - root_context = context['#root_context'] - - def set_data(value): - if not name or name == '$' or name == '$this': - raise ValueError('Cannot assign to {0}'.format(name)) - ctx = root_context - while constants.CTX_VARIABLE_SCOPE not in ctx: - ctx = ctx.parent - ctx[name] = value - - return _Property(lambda: root_context[name], set_data) - - @specs.parameter('this', _Property) - @specs.parameter('key', yaqltypes.Keyword()) - def attribution(context, this, key): - def setter(src_property, value): - src = src_property.get() - if isinstance(src, utils.MappingType): - src_property.set( - utils.FrozenDict( - itertools.chain( - src.items(), - ((key, value),)))) - elif isinstance(src, dsl_types.MuranoObject): - src.set_property(key, value, context['#root_context']) - elif isinstance(src, ( - dsl_types.MuranoTypeReference, - dsl_types.MuranoType)): - if isinstance(src, dsl_types.MuranoTypeReference): - mc = src.type - else: - mc = src - helpers.get_executor().set_static_property( - mc, key, value, context['#root_context']) - else: - raise ValueError( - 'attribution may only be applied to ' - 'objects and dictionaries') - - def getter(src): - if isinstance(src, utils.MappingType): - return src.get(key, {}) - elif isinstance(src, dsl_types.MuranoObject): - try: - return src.get_property(key, context['#root_context']) - except exceptions.UninitializedPropertyAccessError: - return {} - elif isinstance(src, ( - dsl_types.MuranoTypeReference, - dsl_types.MuranoType)): - if isinstance(src, dsl_types.MuranoTypeReference): - mc = src.type - else: - mc = src - return helpers.get_executor().get_static_property( - mc, key, context['#root_context']) - else: - raise ValueError( - 'attribution may only be applied to ' - 'objects and dictionaries') - - return _Property( - lambda: getter(this.get()), - lambda value: setter(this, value)) - - @specs.parameter('this', _Property) - @specs.parameter('index', yaqltypes.Lambda(with_context=True)) - def indexation(context, this, index): - index = index(context['#root_context']) - - def getter(src): - if utils.is_sequence(src): - return src[index] - else: - raise ValueError('indexation may only be applied to lists') - - def setter(src_property, value): - src = src_property.get() - if utils.is_sequence(src): - src_property.set(src[:index] + (value,) + src[index + 1:]) - elif isinstance(src, utils.MappingType): - attribution(context, src_property, index).set(value) - - if isinstance(index, int): - return _Property( - lambda: getter(this.get()), - lambda value: setter(this, value)) - else: - return attribution(context, this, index) - - def _wrap_type_reference(tr, context): - return _Property( - lambda: tr, context['#self']._invalid_target) - - @specs.parameter('prefix', yaqltypes.Keyword()) - @specs.parameter('name', yaqltypes.Keyword()) - @specs.name('#operator_:') - def ns_resolve(context, prefix, name): - return _wrap_type_reference( - yaql_functions.ns_resolve(context, prefix, name), context) - - @specs.parameter('name', yaqltypes.Keyword()) - @specs.name('#unary_operator_:') - def ns_resolve_unary(context, name): - return _wrap_type_reference( - yaql_functions.ns_resolve_unary(context, name), context) - - @specs.parameter('object_', dsl_types.MuranoObject) - def type_(context, object_): - return _wrap_type_reference(yaql_functions.type_(object_), context) - - @specs.name('type') - @specs.parameter('cls', dsl.MuranoTypeParameter()) - def type_from_name(context, cls): - return _wrap_type_reference(cls, context) - - res_context = yaql_integration.create_empty_context() - res_context.register_function(get_context_data, '#get_context_data') - res_context.register_function(attribution, '#operator_.') - res_context.register_function(indexation, '#indexer') - res_context.register_function(ns_resolve) - res_context.register_function(ns_resolve_unary) - res_context.register_function(type_) - res_context.register_function(type_from_name) - return res_context - - -class _Property(object): - def __init__(self, getter, setter): - self._getter = getter - self._setter = setter - - def get(self): - return self._getter() - - def set(self, value): - self._setter(value) - - -class LhsExpression(object): - lhs_context = _prepare_context() - - def __init__(self, expression): - self._expression = expression - - def _invalid_target(self, *args, **kwargs): - raise exceptions.InvalidLhsTargetError(self._expression) - - def __call__(self, value, context): - new_context = LhsExpression.lhs_context.create_child_context() - new_context[''] = context['$'] - new_context['#root_context'] = context - new_context['#self'] = self - for name in (constants.CTX_NAMES_SCOPE,): - new_context[name] = context[name] - prop = self._expression(context=new_context) - if not isinstance(prop, _Property): - self._invalid_target() - prop.set(value) diff --git a/murano/dsl/macros.py b/murano/dsl/macros.py deleted file mode 100644 index 7de749b1f..000000000 --- a/murano/dsl/macros.py +++ /dev/null @@ -1,235 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import constants -from murano.dsl import dsl_exception -from murano.dsl import exceptions -from murano.dsl import expressions -from murano.dsl import helpers -from murano.dsl import yaql_expression - - -class CodeBlock(expressions.DslExpression): - def __init__(self, body): - body = helpers.list_value(body) - self.code_block = list(map(expressions.parse_expression, body)) - - def execute(self, context): - for expr in self.code_block: - if hasattr(expr, 'virtual_instruction'): - instruction = expr.virtual_instruction - context[constants.CTX_CURRENT_INSTRUCTION] = instruction - - try: - expr.execute(context) - except (dsl_exception.MuranoPlException, - exceptions.InternalFlowException): - raise - except Exception as ex: - raise dsl_exception.MuranoPlException.from_python_exception( - ex, context) - - -class MethodBlock(CodeBlock): - def __init__(self, body, name=None): - super(MethodBlock, self).__init__(body) - self._name = name - - def execute(self, context): - new_context = context.create_child_context() - new_context[constants.CTX_VARIABLE_SCOPE] = True - try: - super(MethodBlock, self).execute(new_context) - except exceptions.ReturnException as e: - return e.value - except exceptions.BreakException: - raise exceptions.DslInvalidOperationError( - 'Break cannot be used on method level') - except exceptions.ContinueException: - raise exceptions.DslInvalidOperationError( - 'Continue cannot be used on method level') - else: - return None - - -class ReturnMacro(expressions.DslExpression): - def __init__(self, Return): - self._value = Return - - def execute(self, context): - raise exceptions.ReturnException( - helpers.evaluate(self._value, context)) - - -class BreakMacro(expressions.DslExpression): - def __init__(self, Break): - if Break: - raise exceptions.DslSyntaxError('Break cannot have value') - - def execute(self, context): - raise exceptions.BreakException() - - -class ContinueMacro(expressions.DslExpression): - def __init__(self, Continue): - if Continue: - raise exceptions.DslSyntaxError('Continue cannot have value') - - def execute(self, context): - raise exceptions.ContinueException() - - -class ParallelMacro(CodeBlock): - def __init__(self, Parallel, Limit=None): - super(ParallelMacro, self).__init__(Parallel) - self._limit = Limit or len(self.code_block) - - def execute(self, context): - if not self.code_block: - return - limit = helpers.evaluate(self._limit, context) - helpers.parallel_select( - self.code_block, - lambda expr: expr.execute(context.create_child_context()), - limit) - - -class IfMacro(expressions.DslExpression): - def __init__(self, If, Then, Else=None): - self._code1 = CodeBlock(Then) - self._code2 = None if Else is None else CodeBlock(Else) - self._condition = If - - def execute(self, context): - if helpers.evaluate(self._condition, context): - self._code1.execute(context) - elif self._code2 is not None: - self._code2.execute(context) - - -class WhileDoMacro(expressions.DslExpression): - def __init__(self, While, Do): - if not isinstance(While, yaql_expression.YaqlExpression): - raise TypeError() - self._code = CodeBlock(Do) - self._condition = While - - def execute(self, context): - while self._condition(context): - try: - self._code.execute(context) - except exceptions.BreakException: - break - except exceptions.ContinueException: - continue - - -class ForMacro(expressions.DslExpression): - def __init__(self, For, In, Do): - if not isinstance(For, str): - raise exceptions.DslSyntaxError( - 'For value must be of string type') - self._code = CodeBlock(Do) - self._var = For - self._collection = In - - def execute(self, context): - collection = helpers.evaluate(self._collection, context) - for t in collection: - context[self._var] = t - try: - self._code.execute(context) - except exceptions.BreakException: - break - except exceptions.ContinueException: - continue - - -class RepeatMacro(expressions.DslExpression): - def __init__(self, Repeat, Do): - if not isinstance(Repeat, (int, yaql_expression.YaqlExpression)): - raise exceptions.DslSyntaxError( - 'Repeat value must be either int or expression') - self._count = Repeat - self._code = CodeBlock(Do) - - def execute(self, context): - count = helpers.evaluate(self._count, context) - for _ in range(0, count): - try: - self._code.execute(context) - except exceptions.BreakException: - break - except exceptions.ContinueException: - continue - - -class MatchMacro(expressions.DslExpression): - def __init__(self, Match, Value, Default=None): - if not isinstance(Match, dict): - raise exceptions.DslSyntaxError( - 'Match value must be of dictionary type') - self._switch = Match - self._value = Value - self._default = None if Default is None else CodeBlock(Default) - - def execute(self, context): - match_value = helpers.evaluate(self._value, context) - for key, value in self._switch.items(): - if key == match_value: - CodeBlock(value).execute(context) - return - if self._default is not None: - self._default.execute(context) - - -class SwitchMacro(expressions.DslExpression): - def __init__(self, Switch, Default=None): - if not isinstance(Switch, dict): - raise exceptions.DslSyntaxError( - 'Switch value must be of dictionary type') - self._switch = Switch - self._default = None if Default is None else CodeBlock(Default) - - def execute(self, context): - matched = False - for key, value in self._switch.items(): - if helpers.evaluate(key, context): - matched = True - CodeBlock(value).execute(context) - - if self._default is not None and not matched: - self._default.execute(context) - - -class DoMacro(expressions.DslExpression): - def __init__(self, Do): - self._code = CodeBlock(Do) - - def execute(self, context): - self._code.execute(context) - - -def register(): - expressions.register_macro(DoMacro) - expressions.register_macro(ReturnMacro) - expressions.register_macro(BreakMacro) - expressions.register_macro(ContinueMacro) - expressions.register_macro(ParallelMacro) - expressions.register_macro(IfMacro) - expressions.register_macro(WhileDoMacro) - expressions.register_macro(ForMacro) - expressions.register_macro(RepeatMacro) - expressions.register_macro(MatchMacro) - expressions.register_macro(SwitchMacro) diff --git a/murano/dsl/meta.py b/murano/dsl/meta.py deleted file mode 100644 index 9d1ca53f5..000000000 --- a/murano/dsl/meta.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 abc -import operator -import weakref - -from murano.dsl import dsl_types -from murano.dsl import helpers - - -class MetaProvider(object): - @abc.abstractmethod - def get_meta(self, context): - raise NotImplementedError() - - -class MetaData(MetaProvider): - def __init__(self, definition, target, declaring_type): - declaring_type = weakref.proxy(declaring_type) - definition = helpers.list_value(definition) - factories = [] - used_types = set() - for d in definition: - if isinstance(d, dict): - if len(d) != 1: - raise ValueError('Invalid Meta format') - name = next(iter(d.keys())) - props = d[name] or {} - else: - name = d - props = {} - type_obj = helpers.resolve_type(name, declaring_type) - if type_obj.usage != dsl_types.ClassUsages.Meta: - raise ValueError('Only Meta classes can be attached') - if target not in type_obj.targets: - raise ValueError( - u'Meta class {} is not applicable here'.format( - type_obj.name)) - if type_obj in used_types and ( - type_obj.cardinality != dsl_types.MetaCardinality.Many): - raise ValueError('Cannot attach several Meta instances ' - 'with cardinality One') - - used_types.add(type_obj) - - def factory_maker(template): - def instantiate(context): - obj = helpers.get_object_store().load( - template, owner=None, - context=context, scope_type=declaring_type, - bypass_store=True) - obj.declaring_type = declaring_type - return obj - return instantiate - - factories.append(factory_maker({type_obj: props})) - self._meta_factories = factories - self._meta = None - - def get_meta(self, context): - if self._meta is None: - self._meta = list(map(lambda x: x(context), self._meta_factories)) - return self._meta - - -def merge_providers(initial_class, producer, context): - def merger(cls_list, skip_list): - result = set() - all_meta = [] - for cls in cls_list: - cls_skip_list = skip_list.copy() - provider = producer(cls) - meta = [] if provider is None else provider.get_meta(context) - for item in meta: - cardinality = item.type.cardinality - inherited = item.type.inherited - if cls != initial_class and ( - not inherited or item.type in skip_list): - continue - if cardinality == dsl_types.MetaCardinality.One: - cls_skip_list.add(item.type) - all_meta.append((cls, item)) - all_meta.extend(merger(cls.parents, cls_skip_list)) - meta_types = {} - for cls, item in all_meta: - entry = meta_types.get(item.type) - if entry is not None: - if entry != cls: - raise ValueError( - u'Found more than one instance of Meta {} ' - u'with Cardinality One'.format(item.type.name)) - else: - continue - - if item.type.cardinality == dsl_types.MetaCardinality.One: - meta_types[item.type] = cls - result.add((cls, item)) - return result - - meta = merger([initial_class], set()) - return list(map(operator.itemgetter(1), meta)) - - -def aggregate_meta(provider, context, group_by_name=True): - def key_func(m): - return m.type.name if group_by_name else m.type - meta = provider.get_meta(context) - result = {} - for item in meta: - if item.type.cardinality == dsl_types.MetaCardinality.One: - result[key_func(item)] = item - else: - result.setdefault(key_func(item), []).append(item) - return result diff --git a/murano/dsl/murano_method.py b/murano/dsl/murano_method.py deleted file mode 100644 index 405221362..000000000 --- a/murano/dsl/murano_method.py +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 collections -import sys -import weakref - -from oslo_log import log as logging -from yaql.language import specs - -from murano.common import utils -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import macros -from murano.dsl import meta -from murano.dsl import typespec -from murano.dsl import virtual_exceptions -from murano.dsl import yaql_integration - - -LOG = logging.getLogger(__name__) -macros.register() -virtual_exceptions.register() - - -class MuranoMethod(dsl_types.MuranoMethod, meta.MetaProvider): - def __init__(self, declaring_type, name, payload, original_name=None, - ephemeral=False): - self._name = name - original_name = original_name or name - self._declaring_type = weakref.ref(declaring_type) - self._meta_values = None - self_ref = self if ephemeral else weakref.proxy(self) - - if callable(payload): - if isinstance(payload, specs.FunctionDefinition): - self._body = payload - else: - self._body = yaql_integration.get_function_definition( - payload, self_ref, original_name) - self._arguments_scheme = None - self._scope = self._body.meta.get(constants.META_SCOPE) - if declaring_type.extension_class and any(( - helpers.inspect_is_static( - declaring_type.extension_class, original_name), - helpers.inspect_is_classmethod( - declaring_type.extension_class, original_name))): - self._usage = self._body.meta.get( - constants.META_USAGE, dsl_types.MethodUsages.Static) - if self._usage not in dsl_types.MethodUsages.StaticMethods: - raise ValueError( - 'Invalid Usage for static method ' + self.name) - else: - self._usage = (self._body.meta.get(constants.META_USAGE) or - dsl_types.MethodUsages.Runtime) - if self._usage not in dsl_types.MethodUsages.InstanceMethods: - raise ValueError( - 'Invalid Usage for instance method ' + self.name) - self._resolve_usage_and_scope() - if self._scope is None: - self._scope = dsl_types.MethodScopes.Session - if (self._body.name.startswith('#') or - self._body.name.startswith('*')): - raise ValueError( - 'Import of special yaql functions is forbidden') - self._meta = meta.MetaData( - self._body.meta.get(constants.META_MPL_META), - dsl_types.MetaTargets.Method, - declaring_type) - else: - payload = payload or {} - self._body = macros.MethodBlock(payload.get('Body'), name) - self._usage = payload.get( - 'Usage') or dsl_types.MethodUsages.Runtime - self._scope = payload.get('Scope') - self._resolve_usage_and_scope() - if self._scope is None: - self._scope = dsl_types.MethodScopes.Session - arguments_scheme = helpers.list_value(payload.get('Arguments')) - if isinstance(arguments_scheme, dict): - arguments_scheme = [{key: value} for key, value in - arguments_scheme.items()] - self._arguments_scheme = collections.OrderedDict() - seen_varargs = False - seen_kwargs = False - args_order_error = False - for record in arguments_scheme: - if not isinstance(record, dict) or len(record) > 1: - raise exceptions.DslSyntaxError( - 'Invalid arguments declaration') - name = list(record.keys())[0] - argument = MuranoMethodArgument( - self, self.name, name, record[name]) - usage = argument.usage - if (usage == dsl_types.MethodArgumentUsages.Standard and - (seen_kwargs or seen_varargs)): - args_order_error = True - elif usage == dsl_types.MethodArgumentUsages.VarArgs: - if seen_kwargs or seen_varargs: - args_order_error = True - seen_varargs = True - elif usage == dsl_types.MethodArgumentUsages.KwArgs: - if seen_kwargs: - args_order_error = True - seen_kwargs = True - - if args_order_error: - raise exceptions.DslSyntaxError( - 'Invalid argument order in method {0}'.format( - self.name)) - else: - self._arguments_scheme[name] = argument - - self._meta = meta.MetaData( - payload.get('Meta'), - dsl_types.MetaTargets.Method, - declaring_type) - - self._instance_stub, self._static_stub = \ - yaql_integration.build_stub_function_definitions(self_ref) - - def _resolve_usage_and_scope(self): - if self._usage == dsl_types.MethodUsages.Action: - runtime_version = self.declaring_type.package.runtime_version - if runtime_version > constants.RUNTIME_VERSION_1_3: - LOG.warning('"Usage: Action" is deprecated, ' - 'use "Scope: Public" instead') - if self._scope == dsl_types.MethodScopes.Session: - raise ValueError( - 'Both "Usage: Action" and "Scope: Session" are ' - 'provided for method ' + self.name) - self._scope = dsl_types.MethodScopes.Public - - @property - def name(self): - return self._name - - @property - def declaring_type(self): - return self._declaring_type() - - @property - def arguments_scheme(self): - return self._arguments_scheme - - @property - def instance_stub(self): - return self._instance_stub - - @property - def static_stub(self): - return self._static_stub - - @property - def usage(self): - return self._usage - - @usage.setter - def usage(self, value): - self._usage = value - - @property - def scope(self): - return self._scope - - @scope.setter - def scope(self, value): - self._scope = value - - @property - def body(self): - return self._body - - @property - def is_static(self): - return self.usage in dsl_types.MethodUsages.StaticMethods - - @property - def is_action(self): - return (self.scope == dsl_types.MethodScopes.Public or - self.usage == dsl_types.MethodUsages.Action) - - def get_meta(self, context): - def meta_producer(cls): - method = cls.methods.get(self.name) - if method is None: - return None - return method._meta - - if self._meta_values is None: - executor = helpers.get_executor() - context = executor.create_type_context( - self.declaring_type, caller_context=context) - self._meta_values = meta.merge_providers( - self.declaring_type, meta_producer, context) - return self._meta_values - - def __repr__(self): - return 'MuranoMethod({0}::{1})'.format( - self.declaring_type.name, self.name) - - def invoke(self, this, args, kwargs, context=None, skip_stub=False): - if isinstance(this, dsl.MuranoObjectInterface): - this = this.object - if this and not self.declaring_type.is_compatible(this): - raise Exception("'this' must be of compatible type") - if not this and not self.is_static: - raise Exception("A class instance is required") - - if isinstance(this, dsl_types.MuranoObject): - this = this.cast(self.declaring_type) - else: - this = self.declaring_type - executor = helpers.get_executor() - return executor.invoke_method( - self, this, context, args, kwargs, skip_stub) - - -class MuranoMethodArgument(dsl_types.MuranoMethodArgument, typespec.Spec, - meta.MetaProvider): - def __init__(self, murano_method, method_name, arg_name, declaration): - super(MuranoMethodArgument, self).__init__( - declaration, murano_method.declaring_type) - self._method_name = method_name - self._arg_name = arg_name - self._murano_method = weakref.ref(murano_method) - self._meta = meta.MetaData( - declaration.get('Meta'), - dsl_types.MetaTargets.Argument, self.murano_method.declaring_type) - self._usage = declaration.get('Usage') or \ - dsl_types.MethodArgumentUsages.Standard - - if self._usage not in dsl_types.MethodArgumentUsages.All: - raise exceptions.DslSyntaxError( - 'Unknown usage {0}. Must be one of ({1})'.format( - self._usage, ', '.join(dsl_types.MethodArgumentUsages.All) - )) - - def transform(self, value, this, *args, **kwargs): - try: - if self.murano_method.usage == dsl_types.MethodUsages.Extension: - this = self.murano_method.declaring_type - return super(MuranoMethodArgument, self).transform( - value, this, *args, **kwargs) - except exceptions.ContractViolationException as e: - msg = u'[{0}::{1}({2}{3})] {4}'.format( - self.murano_method.declaring_type.name, - self.murano_method.name, self.name, - e.path, str(e)) - utils.reraise(exceptions.ContractViolationException, - exceptions.ContractViolationException(msg), - sys.exc_info()[2]) - - @property - def murano_method(self): - return self._murano_method() - - @property - def name(self): - return self._arg_name - - @property - def usage(self): - return self._usage - - def get_meta(self, context): - executor = helpers.get_executor() - context = executor.create_type_context( - self.murano_method.declaring_type, caller_context=context) - - return self._meta.get_meta(context) - - def __repr__(self): - return 'MuranoMethodArgument({method}::{name})'.format( - method=self.murano_method.name, name=self.name) diff --git a/murano/dsl/murano_object.py b/murano/dsl/murano_object.py deleted file mode 100644 index d9709c9e5..000000000 --- a/murano/dsl/murano_object.py +++ /dev/null @@ -1,414 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl.principal_objects import garbage_collector -from murano.dsl import yaql_integration - - -class MuranoObject(dsl_types.MuranoObject): - def __init__(self, murano_class, owner, object_id=None, name=None, - known_classes=None, this=None, metadata=None): - self._initialized = False - self._destroyed = False - if known_classes is None: - known_classes = {} - if this is None: - self._owner = owner - self._object_id = object_id or helpers.generate_id() - self._type = murano_class - self._properties = {} - self._parents = {} - self._this = this - self._name = name - self._extension = None - self._executor = helpers.weak_ref(helpers.get_executor()) - self._config = murano_class.package.get_class_config( - murano_class.name) - self._metadata = metadata - if not isinstance(self._config, dict): - self._config = {} - known_classes[murano_class.name] = self - for parent_class in murano_class.parents: - name = parent_class.name - if name not in known_classes: - obj = MuranoObject( - parent_class, owner, - object_id=self._object_id, - known_classes=known_classes, this=self.real_this) - - self._parents[name] = known_classes[name] = obj - else: - self._parents[name] = known_classes[name] - self._destruction_dependencies = [] - - @property - def extension(self): - return self._extension - - @property - def name(self): - return self.real_this._name - - @property - def metadata(self): - return self.real_this._metadata - - @extension.setter - def extension(self, value): - self._extension = value - - def initialize(self, context, params, used_names=None): - context = context.create_child_context() - context[constants.CTX_ALLOW_PROPERTY_WRITES] = True - object_store = helpers.get_object_store() - for property_name in self.type.properties: - spec = self.type.properties[property_name] - if spec.usage == dsl_types.PropertyUsages.Config: - if property_name in self._config: - property_value = self._config[property_name] - else: - property_value = dsl.NO_VALUE - self.set_property(property_name, property_value, - dry_run=self._initialized) - - init = self.type.methods.get('.init') - used_names = used_names or set() - names = set(self.type.properties) - if init: - names.update(init.arguments_scheme.keys()) - last_errors = len(names) - init_args = {} - while True: - errors = 0 - for property_name in names: - if init and property_name in init.arguments_scheme: - spec = init.arguments_scheme[property_name] - is_init_arg = True - else: - spec = self.type.properties[property_name] - is_init_arg = False - - if property_name in used_names: - continue - if spec.usage in (dsl_types.PropertyUsages.Config, - dsl_types.PropertyUsages.Static): - used_names.add(property_name) - continue - if spec.usage == dsl_types.PropertyUsages.Runtime: - if not spec.has_default: - used_names.add(property_name) - continue - property_value = dsl.NO_VALUE - else: - property_value = params.get(property_name, dsl.NO_VALUE) - try: - if is_init_arg: - init_args[property_name] = property_value - else: - self.set_property( - property_name, property_value, context, - dry_run=self._initialized) - used_names.add(property_name) - except exceptions.UninitializedPropertyAccessError: - errors += 1 - except exceptions.ContractViolationException: - if spec.usage != dsl_types.PropertyUsages.Runtime: - raise - if not errors: - break - if errors >= last_errors: - raise exceptions.CircularExpressionDependenciesError() - last_errors = errors - - if (not object_store.initializing and - self._extension is None and - not self._initialized and - not self._destroyed and - not helpers.is_objects_dry_run_mode()): - method = self.type.methods.get('__init__') - if method: - filtered_params = yaql_integration.filter_parameters( - method.body, **params) - yield lambda: method.invoke( - self, filtered_params[0], filtered_params[1], context) - - for parent in self._parents.values(): - for t in parent.initialize(context, params, used_names): - yield t - - def run_init(): - if init: - context[constants.CTX_ARGUMENT_OWNER] = self.real_this - init.invoke(self.real_this, (), init_args, - context.create_child_context()) - self._initialized = True - - if (not object_store.initializing and - not helpers.is_objects_dry_run_mode() and - not self._initialized and - not self._destroyed): - yield run_init - - @property - def object_id(self): - return self._object_id - - @property - def type(self): - return self._type - - @property - def owner(self): - if self._this is None: - return self._owner - else: - return self.real_this.owner - - @property - def real_this(self): - return self._this or self - - @property - def executor(self): - return self._executor() - - @property - def initialized(self): - return self._initialized - - @property - def destruction_dependencies(self): - return self._destruction_dependencies - - def load_dependencies(self, dependencies): - self._destruction_dependencies = [] - if not dependencies: - return - destruction_dependencies = dependencies.get('onDestruction', []) - object_store = helpers.get_object_store() - for record in destruction_dependencies: - subscriber_id = record['subscriber'] - subscriber = object_store.get(subscriber_id) - if not subscriber: - continue - garbage_collector.GarbageCollector.subscribe_destruction( - self, subscriber, record.get('handler')) - - def get_property(self, name, context=None): - start_type, derived = self.type, False - caller_class = None if not context else helpers.get_type(context) - if caller_class is not None and caller_class.is_compatible(self): - start_type, derived = caller_class, True - - declared_properties = start_type.find_properties( - lambda p: p.name == name) - if len(declared_properties) > 0: - spec = self.real_this.type.find_single_property(name) - if spec.usage == dsl_types.PropertyUsages.Static: - return self.executor.get_static_property( - spec.declaring_type, name, context) - else: - return self.real_this._get_property_value(name) - elif derived: - return self.cast(caller_class)._get_property_value(name) - else: - raise exceptions.PropertyReadError(name, start_type) - - def _get_property_value(self, name): - try: - return self._properties[name] - except KeyError: - raise exceptions.UninitializedPropertyAccessError( - name, self.type) - - def set_property(self, name, value, context=None, dry_run=False): - start_type, derived = self.real_this.type, False - caller_class = None if not context else helpers.get_type(context) - if caller_class is not None and caller_class.is_compatible(self): - start_type, derived = caller_class, True - if context is None: - context = self.executor.create_object_context(self) - declared_properties = start_type.find_properties( - lambda p: p.name == name) - if len(declared_properties) > 0: - ultimate_spec = self.real_this.type.find_single_property(name) - property_list = list(self._list_properties(name)) - for spec in property_list: - if (caller_class is not None and not - helpers.are_property_modifications_allowed(context) and - (spec.usage not in dsl_types.PropertyUsages.Writable or - not derived)): - raise exceptions.NoWriteAccessError(name) - - if spec.usage == dsl_types.PropertyUsages.Static: - default = None - else: - default = self._config.get(name, spec.default) - - if spec is ultimate_spec: - value = spec.transform( - value, self.real_this, - self.real_this, context, default=default, - finalize=len(property_list) == 1) - else: - spec.validate(value, self.real_this, context, default) - if len(property_list) > 1: - value = ultimate_spec.finalize(value, self.real_this, context) - if ultimate_spec.usage == dsl_types.PropertyUsages.Static: - self.executor.set_static_property( - ultimate_spec.declaring_type, name, value, context, - dry_run=dry_run) - elif not dry_run: - self.real_this._properties[name] = value - elif derived: - if not dry_run: - obj = self.cast(caller_class) - obj._properties[name] = value - else: - raise exceptions.PropertyWriteError(name, start_type) - - def cast(self, cls): - for p in helpers.traverse(self, lambda t: t._parents.values()): - if p.type == cls: - return p - raise TypeError('Cannot cast {0} to {1}'.format(self.type, cls)) - - def _list_properties(self, name): - for p in helpers.traverse( - self.real_this, lambda t: t._parents.values()): - if name in p.type.properties: - yield p.type.properties[name] - - def __repr__(self): - return '<{0}/{1} {2} ({3})>'.format( - self.type.name, self.type.version, self.object_id, id(self)) - - def to_dictionary(self, include_hidden=False, - serialization_type=dsl_types.DumpTypes.Serializable, - allow_refs=False, with_destruction_dependencies=False): - context = helpers.get_context() - result = {} - for parent in self._parents.values(): - result.update(parent.to_dictionary( - include_hidden, dsl_types.DumpTypes.Serializable, - allow_refs)) - skip_usages = (dsl_types.PropertyUsages.Runtime, - dsl_types.PropertyUsages.Config) - for property_name in self.type.properties: - if property_name in self.real_this._properties: - spec = self.type.properties[property_name] - if spec.usage not in skip_usages or include_hidden: - prop_value = self.real_this._properties[property_name] - if isinstance(prop_value, MuranoObject) and allow_refs: - meta = [m for m in spec.get_meta(context) - if m.type.name == ('io.murano.metadata.' - 'engine.Serialize')] - if meta and meta[0].get_property( - 'as', context) == 'reference': - prop_value = prop_value.object_id - result[property_name] = prop_value - if serialization_type == dsl_types.DumpTypes.Inline: - result.pop('?') - result = { - self.type: result, - 'id': self.object_id, - 'name': self.name, - 'metadata': self.metadata - } - header = result - else: - if serialization_type == dsl_types.DumpTypes.Mixed: - result.update({'?': { - 'type': self.type, - 'id': self.object_id, - 'name': self.name, - 'metadata': self.metadata - }}) - else: - result.update({'?': { - 'type': helpers.format_type_string(self.type), - 'id': self.object_id, - 'name': self.name, - 'metadata': self.metadata - }}) - header = result['?'] - if self.destroyed: - header['destroyed'] = True - if with_destruction_dependencies: - dds = [] - for record in self.destruction_dependencies: - subscriber = record['subscriber']() - if not subscriber or self.executor.object_store.is_doomed( - subscriber): - continue - dds.append({ - 'subscriber': subscriber.object_id, - 'handler': record['handler'] - }) - if dds: - header.setdefault('dependencies', {})['onDestruction'] = dds - return result - - def mark_destroyed(self, clear_data=False): - self._destroyed = True - self._suppress__del__ = None - if clear_data or not self.initialized: - self._extension = None - self._properties = None - self._owner = None - self._destruction_dependencies = None - self._this = None - for p in self._parents.values(): - p.mark_destroyed(clear_data) - - @property - def destroyed(self): - return self._destroyed - - -class RecyclableMuranoObject(MuranoObject): - def __init__(self, *args, **kwargs): - # Create self-reference to prevent __del__ from being called - # automatically when there are no other objects referring to this one. - # Without this reference __del__ will get called immediately after - # reference counter will go to 0 and the object will put itself into - # pending list creating another reference to itself and thus preventing - # its child objects from being deleted. After the .destroy method - # child objects will become eligible for destruction but will be - # unable to use find() method since their owner will be destroyed - # and collected at that point. With this reference gc.collect() - # will collect the whole object graph at once and then we could - # sort it and destroy in the correct order so that child objects - # will be destroyed first. - - self._suppress__del__ = self - super(RecyclableMuranoObject, self).__init__(*args, **kwargs) - - def __del__(self): - # For Py2 the purpose of __del__ (in combination with _suppress__del__) - # method is just to prevent object from being released automatically. - # In Py3 the gc.collect list will be empty and __del__ will be called - # for objects that were not destroyed yet. - if self._this is None and self._initialized and not self._destroyed: - self.executor.object_store.schedule_object_destruction(self) - - def mark_destroyed(self, clear_data=False): - self.executor.attribute_store.forget_object(self) - super(RecyclableMuranoObject, self).mark_destroyed(clear_data) diff --git a/murano/dsl/murano_package.py b/murano/dsl/murano_package.py deleted file mode 100644 index 4012bd416..000000000 --- a/murano/dsl/murano_package.py +++ /dev/null @@ -1,233 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 inspect -import warnings -import weakref - -import debtcollector -import semantic_version -from yaql.language import specs -from yaql.language import utils - -from murano.dsl import constants -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import meta as dslmeta -from murano.dsl import murano_object -from murano.dsl import murano_type -from murano.dsl import namespace_resolver -from murano.dsl import principal_objects -from murano.dsl import yaql_integration - - -class MuranoPackage(dsl_types.MuranoPackage, dslmeta.MetaProvider): - def __init__(self, package_loader, name, version=None, - runtime_version=None, requirements=None, meta=None): - super(MuranoPackage, self).__init__() - self._package_loader = weakref.proxy(package_loader) - self._name = name - self._version = helpers.parse_version(version) - self._runtime_version = helpers.parse_version(runtime_version) - self._requirements = { - name: semantic_version.Spec('==' + str(self._version.major)) - } - if name != constants.CORE_LIBRARY: - self._requirements[constants.CORE_LIBRARY] = \ - semantic_version.Spec('==0') - self._classes = {} - self._imported_types = {object, murano_object.MuranoObject} - for key, value in (requirements or {}).items(): - self._requirements[key] = helpers.parse_version_spec(value) - - self._load_queue = {} - self._native_load_queue = {} - if self.name == constants.CORE_LIBRARY: - principal_objects.register(self) - self._package_class = self._create_package_class() - self._meta = lambda: dslmeta.MetaData( - meta, dsl_types.MetaTargets.Package, self._package_class) - - @property - def package_loader(self): - return self._package_loader - - @property - def name(self): - return self._name - - @property - def version(self): - return self._version - - @property - def runtime_version(self): - return self._runtime_version - - @property - def requirements(self): - return self._requirements - - @property - def classes(self): - return set(self._classes.keys()).union( - self._load_queue.keys()).union(self._native_load_queue.keys()) - - def get_resource(self, name): - raise NotImplementedError('resource API is not implemented') - - # noinspection PyMethodMayBeStatic - def get_class_config(self, name): - return {} - - def _register_mpl_classes(self, data, name=None): - type_obj = self._classes.get(name) - if type_obj is not None: - return type_obj - if callable(data): - data = data() - data = helpers.list_value(data) - unnamed_class = None - last_ns = {} - for cls_data in data: - last_ns = cls_data.setdefault('Namespaces', last_ns.copy()) - if len(cls_data) == 1: - continue - cls_name = cls_data.get('Name') - if not cls_name: - if unnamed_class: - raise exceptions.AmbiguousClassName(name) - unnamed_class = cls_data - else: - ns_resolver = namespace_resolver.NamespaceResolver(last_ns) - cls_name = ns_resolver.resolve_name(cls_name) - if cls_name == name: - type_obj = murano_type.create( - cls_data, self, cls_name, ns_resolver) - self._classes[name] = type_obj - else: - self._load_queue.setdefault(cls_name, cls_data) - if type_obj is None and unnamed_class: - unnamed_class['Name'] = name - return self._register_mpl_classes(unnamed_class, name) - return type_obj - - def _register_native_class(self, cls, name): - if cls in self._imported_types: - return self._classes[name] - - try: - m_class = self.find_class(name, False) - except exceptions.NoClassFound: - m_class = self._register_mpl_classes({'Name': name}, name) - - m_class.extension_class = cls - - for method_name in dir(cls): - if method_name.startswith('_'): - continue - method = getattr(cls, method_name) - if not any(( - helpers.inspect_is_method(cls, method_name), - helpers.inspect_is_static(cls, method_name), - helpers.inspect_is_classmethod(cls, method_name))): - continue - method_name_alias = (getattr( - method, '__murano_name', None) or - specs.convert_function_name( - method_name, yaql_integration.CONVENTION)) - m_class.add_method(method_name_alias, method, method_name) - self._imported_types.add(cls) - return m_class - - def register_class(self, cls, name=None): - if inspect.isclass(cls): - name = name or getattr(cls, '__murano_name', None) or cls.__name__ - if name in self._classes: - self._register_native_class(cls, name) - else: - self._native_load_queue.setdefault(name, cls) - elif isinstance(cls, dsl_types.MuranoType): - self._classes[cls.name] = cls - elif name not in self._classes: - self._load_queue[name] = cls - - def find_class(self, name, search_requirements=True): - payload = self._native_load_queue.pop(name, None) - if payload is not None: - return self._register_native_class(payload, name) - - payload = self._load_queue.pop(name, None) - if payload is not None: - result = self._register_mpl_classes(payload, name) - if result: - return result - - result = self._classes.get(name) - if result: - return result - if search_requirements: - pkgs_for_search = [] - for package_name, version_spec in self._requirements.items(): - if package_name == self.name: - continue - referenced_package = self._package_loader.load_package( - package_name, version_spec) - try: - return referenced_package.find_class(name, False) - except exceptions.NoClassFound: - if name.startswith('io.murano.extensions'): - try: - short_name = name.replace( - 'io.murano.extensions.', '', 1) - result = referenced_package.find_class( - short_name, False) - warnings.simplefilter("once") - msg = ("Plugin %(name)s was not found, but a " - "%(shorter_name)s was found instead and " - "will be used. This could be caused by " - "recent change in plugin naming scheme. If " - "you are developing applications targeting " - "this plugin consider changing its name" % - {'name': name, 'shorter_name': short_name}) - debtcollector.deprecate(msg) - return result - except exceptions.NoClassFound: - pass - pkgs_for_search.append(referenced_package) - continue - raise exceptions.NoClassFound( - name, packages=pkgs_for_search + [self]) - - raise exceptions.NoClassFound(name, packages=[self]) - - @property - def context(self): - return None - - def _create_package_class(self): - ns_resolver = namespace_resolver.NamespaceResolver(None) - return murano_type.MuranoClass( - ns_resolver, self.name, self, utils.NO_VALUE) - - def get_meta(self, context): - if callable(self._meta): - executor = helpers.get_executor() - context = executor.create_package_context(self) - self._meta = self._meta().get_meta(context) - return self._meta - - def __repr__(self): - return 'MuranoPackage({name})'.format(name=self.name) diff --git a/murano/dsl/murano_property.py b/murano/dsl/murano_property.py deleted file mode 100644 index 71ac3b138..000000000 --- a/murano/dsl/murano_property.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 sys -import weakref - -from murano.common import utils -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import meta -from murano.dsl import typespec - - -class MuranoProperty(dsl_types.MuranoProperty, typespec.Spec, - meta.MetaProvider): - def __init__(self, declaring_type, property_name, declaration): - super(MuranoProperty, self).__init__(declaration, declaring_type) - self._property_name = property_name - self._declaring_type = weakref.ref(declaring_type) - self._usage = declaration.get('Usage') or dsl_types.PropertyUsages.In - if self._usage not in dsl_types.PropertyUsages.All: - raise exceptions.DslSyntaxError( - 'Unknown usage {0}. Must be one of ({1})'.format( - self._usage, ', '.join(dsl_types.PropertyUsages.All))) - self._meta = meta.MetaData( - declaration.get('Meta'), - dsl_types.MetaTargets.Property, declaring_type) - self._meta_values = None - - def transform(self, *args, **kwargs): - try: - return super(MuranoProperty, self).transform(*args, **kwargs) - except exceptions.ContractViolationException as e: - msg = u'[{0}.{1}{2}] {3}'.format( - self.declaring_type.name, self.name, e.path, str(e)) - utils.reraise(exceptions.ContractViolationException, - exceptions.ContractViolationException(msg), - sys.exc_info()[2]) - - @property - def name(self): - return self._property_name - - @property - def usage(self): - return self._usage - - def get_meta(self, context): - def meta_producer(cls): - prop = cls.properties.get(self.name) - if prop is None: - return None - return prop._meta - - if self._meta_values is None: - executor = helpers.get_executor() - context = executor.create_type_context( - self.declaring_type, caller_context=context) - - self._meta_values = meta.merge_providers( - self.declaring_type, meta_producer, context) - return self._meta_values - - def __repr__(self): - return 'MuranoProperty({type}::{name})'.format( - type=self.declaring_type.name, name=self.name) diff --git a/murano/dsl/murano_type.py b/murano/dsl/murano_type.py deleted file mode 100644 index a15ab7a92..000000000 --- a/murano/dsl/murano_type.py +++ /dev/null @@ -1,570 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 abc -import collections -import copy -import weakref - -import semantic_version -from yaql.language import utils - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import meta as dslmeta -from murano.dsl import murano_method -from murano.dsl import murano_object -from murano.dsl import murano_property -from murano.dsl import yaql_integration - - -class MuranoType(dsl_types.MuranoType): - def __init__(self, ns_resolver, name, package): - self._namespace_resolver = ns_resolver - self._name = name - self._package = weakref.ref(package) - - @property - def name(self): - return self._name - - @property - def package(self): - return self._package() - - @property - def namespace_resolver(self): - return self._namespace_resolver - - @property - @abc.abstractmethod - def usage(self): - raise NotImplementedError() - - @property - def version(self): - return self.package.version - - def get_reference(self): - return dsl_types.MuranoTypeReference(self) - - -class MuranoClass(dsl_types.MuranoClass, MuranoType, dslmeta.MetaProvider): - _allowed_usages = {dsl_types.ClassUsages.Class} - - def __init__(self, ns_resolver, name, package, parents, meta=None, - imports=None): - super(MuranoClass, self).__init__(ns_resolver, name, package) - self._methods = {} - self._properties = {} - self._config = {} - self._extension_class = None - if (self._name == constants.CORE_LIBRARY_OBJECT or - parents is utils.NO_VALUE): - self._parents = [] - else: - self._parents = parents or [ - package.find_class(constants.CORE_LIBRARY_OBJECT)] - for p in self._parents: - if p.usage not in self._allowed_usages: - raise exceptions.InvalidInheritanceError( - u'Type {0} cannot have parent with Usage {1}'.format( - self.name, p.usage)) - remappings = self._build_parent_remappings() - self._parents = self._adjusted_parents(remappings) - self._context = None - self._exported_context = None - self._meta = dslmeta.MetaData(meta, dsl_types.MetaTargets.Type, self) - self._meta_values = None - self._imports = list(self._resolve_imports(imports)) - - def _adjusted_parents(self, remappings): - seen = {} - - def altered_clone(class_): - seen_class = seen.get(class_) - if seen_class is not None: - return seen_class - - cls_remapping = remappings.get(class_) - - if cls_remapping is not None: - return altered_clone(cls_remapping) - - new_parents = [altered_clone(p) for p in class_._parents] - if all(a is b for a, b in zip(class_._parents, new_parents)): - return class_ - res = copy.copy(class_) - res._parents = new_parents - res._meta_values = None - res._context = None - res._exported_context = None - seen[class_] = res - return res - return [altered_clone(p) for p in self._parents] - - def __eq__(self, other): - if not isinstance(other, MuranoType): - return False - return self.name == other.name and self.version == other.version - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.name, self.version)) - - @property - def usage(self): - return dsl_types.ClassUsages.Class - - @property - def parents(self): - return self._parents - - @property - def methods(self): - return self._methods - - @property - def all_method_names(self): - names = set(self.methods.keys()) - for c in self.ancestors(): - names.update(c.methods.keys()) - return tuple(names) - - @property - def extension_class(self): - return self._extension_class - - @extension_class.setter - def extension_class(self, cls): - self._extension_class = cls - ctor = yaql_integration.get_class_factory_definition(cls, self) - self.add_method('__init__', ctor) - - def add_method(self, name, payload, original_name=None): - method = murano_method.MuranoMethod(self, name, payload, original_name) - self._methods[name] = method - self._context = None - self._exported_context = None - return method - - @property - def properties(self): - return self._properties - - @property - def all_property_names(self): - names = set(self.properties.keys()) - for c in self.ancestors(): - names.update(c.properties.keys()) - return tuple(names) - - def add_property(self, property_typespec): - if not isinstance(property_typespec, murano_property.MuranoProperty): - raise TypeError('property_typespec') - self._properties[property_typespec.name] = property_typespec - - def _find_symbol_chains(self, func): - queue = collections.deque([(self, ())]) - while queue: - cls, path = queue.popleft() - symbol = func(cls) - segment = (symbol,) if symbol is not None else () - leaf = True - for p in cls.parents: - leaf = False - queue.append((p, path + segment)) - if leaf: - path += segment - if path: - yield path - - def _resolve_imports(self, imports): - seen = {self.name} - for imp in helpers.list_value(imports): - if imp in seen: - continue - type = helpers.resolve_type(imp, self) - if type in seen: - continue - seen.add(imp) - seen.add(type) - yield type - - def _choose_symbol(self, func): - chains = sorted( - self._find_symbol_chains(func), - key=lambda t: len(t)) - result = [] - for i in range(len(chains)): - if chains[i][0] in result: - continue - add = True - for j in range(i + 1, len(chains)): - common = 0 - if not add: - break - for p in range(len(chains[i])): - if chains[i][-p - 1] is chains[j][-p - 1]: - common += 1 - else: - break - if common == len(chains[i]): - add = False - break - if add: - result.append(chains[i][0]) - return result - - def find_method(self, name): - return self._choose_symbol(lambda cls: cls.methods.get(name)) - - def find_property(self, name): - return self._choose_symbol( - lambda cls: cls.properties.get(name)) - - def find_static_property(self, name): - def prop_func(cls): - prop = cls.properties.get(name) - if prop is not None and prop.usage == 'Static': - return prop - - result = self._choose_symbol(prop_func) - if len(result) < 1: - raise exceptions.NoPropertyFound(name) - elif len(result) > 1: - raise exceptions.AmbiguousPropertyNameError(name) - return result[0] - - def find_single_method(self, name): - result = self.find_method(name) - if len(result) < 1: - raise exceptions.NoMethodFound(name) - elif len(result) > 1: - raise exceptions.AmbiguousMethodName(name) - return result[0] - - def find_methods(self, predicate): - result = list(filter(predicate, self.methods.values())) - for c in self.ancestors(): - for method in c.methods.values(): - if predicate(method) and method not in result: - result.append(method) - return result - - def find_properties(self, predicate): - result = list(filter(predicate, self.properties.values())) - for c in self.ancestors(): - for prop in c.properties.values(): - if predicate(prop) and prop not in result: - result.append(prop) - return result - - def _iterate_unique_methods(self): - for name in self.all_method_names: - try: - yield self.find_single_method(name) - except exceptions.AmbiguousMethodName: - def func(*args, **kwargs): - raise - yield murano_method.MuranoMethod( - self, name, func, ephemeral=True) - - def find_single_property(self, name): - result = self.find_property(name) - if len(result) < 1: - raise exceptions.NoPropertyFound(name) - elif len(result) > 1: - raise exceptions.AmbiguousPropertyNameError(name) - return result[0] - - def invoke(self, name, this, args, kwargs, context=None): - method = self.find_single_method(name) - return method.invoke(this, args, kwargs, context) - - def is_compatible(self, obj): - if isinstance(obj, (murano_object.MuranoObject, - dsl.MuranoObjectInterface, - dsl_types.MuranoTypeReference)): - obj = obj.type - if obj == self: - return True - return any(cls == self for cls in obj.ancestors()) - - def __repr__(self): - return 'MuranoClass({0}/{1})'.format(self.name, self.version) - - def _build_parent_remappings(self): - """Remaps class parents. - - In case of multiple inheritance class may indirectly get several - versions of the same class. It is reasonable to try to replace them - with single version to avoid conflicts. We can do that when within - versions that satisfy our class package requirements. - But in order to merge several classes that are not our parents but - grand parents we will need to modify classes that may be used - somewhere else (with another set of requirements). We cannot do this. - So instead we build translation table that will tell which ancestor - class need to be replaced with which so that we minimize number of - versions used for single class (or technically packages since version - is a package attribute). For translation table to work there should - be a method that returns all class virtual ancestors so that everybody - will see them instead of accessing class parents directly and getting - declared ancestors. - """ - result = {} - - aggregation = { - self.package.name: {( - self.package, - semantic_version.Spec('==' + str(self.package.version)) - )} - } - for cls, parent in helpers.traverse( - ((self, parent) for parent in self._parents), - lambda cp: ((cp[1], anc) for anc in cp[1].parents)): - if cls.package != parent.package: - requirement = cls.package.requirements[parent.package.name] - aggregation.setdefault(parent.package.name, set()).add( - (parent.package, requirement)) - - package_bindings = {} - for versions in aggregation.values(): - mappings = self._remap_package(versions) - package_bindings.update(mappings) - - for cls in helpers.traverse(self.parents, lambda c: c.parents): - if cls.package in package_bindings: - package2 = package_bindings[cls.package] - cls2 = package2.classes[cls.name] - result[cls] = cls2 - return result - - @staticmethod - def _remap_package(versions): - result = {} - reverse_mappings = {} - versions_list = sorted(versions, key=lambda x: x[0].version) - i = 0 - while i < len(versions_list): - package1, requirement1 = versions_list[i] - dst_package = None - for j, (package2, _) in enumerate(versions_list): - if i == j: - continue - if package2.version in requirement1 and ( - dst_package is None or - dst_package.version < package2.version): - dst_package = package2 - if dst_package: - result[package1] = dst_package - reverse_mappings.setdefault(dst_package, []).append(package1) - for package in reverse_mappings.get(package1, []): - result[package] = dst_package - del versions_list[i] - else: - i += 1 - return result - - def ancestors(self): - for c in helpers.traverse(self, lambda t: t.parents): - if c is not self: - yield c - - @property - def context(self): - if not self._context: - ctx = None - for imp in reversed(self._imports): - if ctx is None: - ctx = imp.exported_context - else: - ctx = helpers.link_contexts(ctx, imp.exported_context) - - if ctx is None: - self._context = yaql_integration.create_empty_context() - else: - self._context = ctx.create_child_context() - - for m in self._iterate_unique_methods(): - if m.instance_stub: - self._context.register_function( - m.instance_stub, name=m.instance_stub.name) - if m.static_stub: - self._context.register_function( - m.static_stub, name=m.static_stub.name) - return self._context - - @property - def exported_context(self): - if not self._exported_context: - self._exported_context = yaql_integration.create_empty_context() - for m in self._iterate_unique_methods(): - if m.usage == dsl_types.MethodUsages.Extension: - if m.instance_stub: - self._exported_context.register_function( - m.instance_stub, name=m.instance_stub.name) - if m.static_stub: - self._exported_context.register_function( - m.static_stub, name=m.static_stub.name) - return self._exported_context - - def get_meta(self, context): - if self._meta_values is None: - executor = helpers.get_executor() - context = executor.create_type_context( - self, caller_context=context) - self._meta_values = dslmeta.merge_providers( - self, lambda cls: cls._meta, context) - return self._meta_values - - -class MuranoMetaClass(dsl_types.MuranoMetaClass, MuranoClass): - _allowed_usages = {dsl_types.ClassUsages.Meta, dsl_types.ClassUsages.Class} - - def __init__(self, ns_resolver, name, package, parents, meta=None, - imports=None): - super(MuranoMetaClass, self).__init__( - ns_resolver, name, package, parents, meta, imports) - self._cardinality = dsl_types.MetaCardinality.One - self._targets = list(dsl_types.MetaCardinality.All) - self._inherited = False - - @property - def usage(self): - return dsl_types.ClassUsages.Meta - - @property - def cardinality(self): - return self._cardinality - - @cardinality.setter - def cardinality(self, value): - self._cardinality = value - - @property - def targets(self): - return self._targets - - @targets.setter - def targets(self, value): - self._targets = value - - @property - def inherited(self): - return self._inherited - - @inherited.setter - def inherited(self, value): - self._inherited = value - - def __repr__(self): - return 'MuranoMetaClass({0}/{1})'.format(self.name, self.version) - - -def create(data, package, name, ns_resolver): - usage = data.get('Usage', dsl_types.ClassUsages.Class) - if usage == dsl_types.ClassUsages.Class: - return _create_class(MuranoClass, name, ns_resolver, data, package) - elif usage == dsl_types.ClassUsages.Meta: - return _create_meta_class( - MuranoMetaClass, name, ns_resolver, data, package) - else: - raise ValueError(u'Invalid type Usage: "{}"'.format(usage)) - - -def _create_class(cls, name, ns_resolver, data, package, *args, **kwargs): - parent_class_names = data.get('Extends') - parent_classes = [] - if parent_class_names: - if not utils.is_sequence(parent_class_names): - parent_class_names = [parent_class_names] - for parent_name in parent_class_names: - full_name = ns_resolver.resolve_name(str(parent_name)) - parent_classes.append(package.find_class(full_name)) - - type_obj = cls( - ns_resolver, name, package, parent_classes, data.get('Meta'), - data.get('Import'), *args, **kwargs) - - properties = data.get('Properties') or {} - for property_name, property_spec in properties.items(): - spec = murano_property.MuranoProperty( - type_obj, property_name, property_spec) - type_obj.add_property(spec) - - methods = data.get('Methods') or data.get('Workflow') or {} - - method_mappings = { - 'initialize': '.init', - 'destroy': '.destroy' - } - - for method_name, payload in methods.items(): - type_obj.add_method( - method_mappings.get(method_name, method_name), payload) - - return type_obj - - -def _create_meta_class(cls, name, ns_resolver, data, package, *args, **kwargs): - cardinality = data.get('Cardinality', dsl_types.MetaCardinality.One) - if cardinality not in dsl_types.MetaCardinality.All: - raise ValueError(u'Invalid MetaClass Cardinality "{}"'.format( - cardinality)) - applies_to = data.get('Applies', dsl_types.MetaTargets.All) - if isinstance(applies_to, str): - applies_to = [applies_to] - if isinstance(applies_to, list): - applies_to = set(applies_to) - delta = applies_to - dsl_types.MetaTargets.All - {'All'} - if delta: - raise ValueError(u'Invalid MetaClass target(s) {}:'.format( - ', '.join(map(u'"{}"'.format, delta))) - ) - if 'All' in applies_to: - applies_to = dsl_types.MetaTargets.All - inherited = data.get('Inherited', False) - if not isinstance(inherited, bool): - raise ValueError('Invalid Inherited value. Must be true or false') - - meta_cls = _create_class( - cls, name, ns_resolver, data, package, *args, **kwargs) - meta_cls.targets = list(applies_to) - meta_cls.cardinality = cardinality - meta_cls.inherited = inherited - return meta_cls - - -def weigh_type_hierarchy(cls): - """Weighs classes in type hierarchy by their distance from the root - - :param cls: root of hierarchy - :return: dictionary that has class name as keys and distance from the root - a values. Root class has always a distance of 0. If the class - (or different versions of that class) is achievable through - several paths the shortest distance is used. - """ - - result = {} - for c, w in helpers.traverse( - [(cls, 0)], lambda t: map( - lambda p: (p, t[1] + 1), t[0].parents)): - result.setdefault(c.name, w) - return result diff --git a/murano/dsl/namespace_resolver.py b/murano/dsl/namespace_resolver.py deleted file mode 100644 index 094caddad..000000000 --- a/murano/dsl/namespace_resolver.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 re - -TYPE_NAME_RE = re.compile(r'^([a-zA-Z_]\w*:|:)?[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$') -NS_RE = re.compile(r'^([a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*)?$') -PREFIX_RE = re.compile(r'^([a-zA-Z_]\w*|=)$') - - -class NamespaceResolver(object): - def __init__(self, namespaces): - if namespaces is None: - namespaces = {} - for prefix, ns in namespaces.items(): - if ns is None: - ns = '' - if PREFIX_RE.match(prefix) is None: - raise ValueError( - 'Invalid namespace prefix "{0}"'.format(prefix)) - if NS_RE.match(ns) is None: - raise ValueError('Invalid namespace "{0}"'.format(ns)) - self._namespaces = namespaces.copy() - self._namespaces.setdefault('=', '') - self._namespaces[''] = '' - - def resolve_name(self, name): - if not self.is_typename(name, True): - raise ValueError('Invalid type name "{0}"'.format(name)) - name = str(name) - if ':' not in name: - if '.' in name: - parts = ['', name] - else: - parts = ['=', name] - else: - parts = name.split(':') - if not parts[0]: - parts[0] = '=' - - if parts[0] not in self._namespaces: - raise KeyError('Unknown namespace prefix ' + parts[0]) - - ns = self._namespaces[parts[0]] - if not ns: - return parts[1] - return '.'.join((ns, parts[1])) - - @staticmethod - def is_typename(name, relaxed): - if not name: - return False - name = str(name) - if not relaxed and ':' not in name: - return False - return TYPE_NAME_RE.match(name) is not None diff --git a/murano/dsl/object_store.py b/murano/dsl/object_store.py deleted file mode 100644 index ae4b985cd..000000000 --- a/murano/dsl/object_store.py +++ /dev/null @@ -1,303 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 collections -import gc -import weakref - -from oslo_log import log as logging - -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import murano_object - - -LOG = logging.getLogger(__name__) - - -class ObjectStore(object): - def __init__(self, executor, parent_store=None, weak_store=True): - self._parent_store = parent_store - self._store = weakref.WeakValueDictionary() if weak_store else {} - self._designer_attributes_store = {} - self._executor = weakref.ref(executor) - self._pending_destruction = set() - - @property - def executor(self): - return self._executor() - - def get(self, object_id, check_parent_store=True): - result = self._store.get(object_id) - if not result and self._parent_store and check_parent_store: - return self._parent_store.get(object_id, check_parent_store) - return result - - def has(self, object_id, check_parent_store=True): - if object_id in self._store: - return True - if check_parent_store and self._parent_store: - return self._parent_store.has(object_id, check_parent_store) - return False - - def put(self, murano_object, object_id=None): - self._store[object_id or murano_object.object_id] = murano_object - - def schedule_object_destruction(self, murano_object): - self._pending_destruction.add(murano_object) - self._store[murano_object.object_id] = murano_object - - def iterate(self): - return self._store.keys() - - def remove(self, object_id): - self._store.pop(object_id) - - def load(self, value, owner, default_type=None, - scope_type=None, context=None, keep_ids=False, - bypass_store=False): - # do the object model load in a temporary object store and copy - # loaded objects here after that - model_store = InitializationObjectStore( - owner, self, keep_ids) - with helpers.with_object_store(model_store): - result = model_store.load( - value, owner, scope_type=scope_type, - default_type=default_type, context=context) - for obj_id in model_store.iterate(): - obj = model_store.get(obj_id) - if obj.initialized: - if not bypass_store: - self.put(obj) - return result - - @staticmethod - def _get_designer_attributes(header): - return dict((k, v) for k, v in header.items() - if str(k).startswith('_')) - - def designer_attributes(self, object_id): - return self._designer_attributes_store.get(object_id, {}) - - @property - def initializing(self): - return False - - @property - def parent_store(self): - return self._parent_store - - def cleanup(self): - LOG.debug('Cleaning up orphan objects') - with helpers.with_object_store(self): - n = self._collect_garbage() - LOG.debug('{} orphan objects were destroyed'.format(n)) - return n - - def prepare_finalize(self, used_objects): - used_objects = set(used_objects) if used_objects else [] - sentenced_objects = [ - obj for obj in self._store.values() - if obj not in used_objects - ] - with helpers.with_object_store(self): - if sentenced_objects: - self._pending_destruction.update(sentenced_objects) - for __ in self._destroy_garbage(sentenced_objects): - pass - - def finalize(self): - with helpers.with_object_store(self): - for t in self._store.values(): - t.mark_destroyed(True) - self._pending_destruction.clear() - self._store.clear() - - def _collect_garbage(self): - repeat = True - count = 0 - while repeat: - repeat = False - gc.collect() - for obj in gc.garbage: - if (isinstance(obj, murano_object.RecyclableMuranoObject) and - obj.executor is self._executor()): - repeat = True - if obj.initialized and not obj.destroyed: - self.schedule_object_destruction(obj) - else: - obj.mark_destroyed(True) - obj = None - del gc.garbage[:] - if self._pending_destruction: - for obj in self._destroy_garbage(self._pending_destruction): - if obj in self._pending_destruction: - repeat = True - obj.mark_destroyed() - self._pending_destruction.remove(obj) - count += 1 - return count - - def is_doomed(self, obj): - return obj.destroyed or obj in self._pending_destruction - - def _destroy_garbage(self, sentenced_objects): - dd_graph = {} - - # NOTE(starodubcevna): construct a graph which looks like: - # { - # obj1: [subscriber1, subscriber2], - # obj2: [subscriber2, subscriber3] - # } - for obj in sentenced_objects: - obj_subscribers = [obj.owner] - - for dd in obj.destruction_dependencies: - subscriber = dd['subscriber'] - if subscriber: - subscriber = subscriber() - if subscriber and subscriber not in obj_subscribers: - obj_subscribers.append(subscriber) - - dd_graph[obj] = obj_subscribers - - def topological(graph): - """Topological sort implementation - - This implementation will work even if we have cycle dependencies, - e.g. [a->b, b->c, c->a]. In this case the order of deletion will be - undefined and it's okay. - """ - - visited = collections.defaultdict(int) - indexes = collections.defaultdict(int) - - def dfs(obj): - visited[obj] += 1 - subscribers = graph.get(obj) - if subscribers is not None: - m = 0 - for i, subscriber in enumerate(subscribers): - if i == 0 or not visited[subscriber]: - for t in dfs(subscriber): - yield t - m = max(m, indexes[subscriber]) - - if visited.get(obj, 0) <= 2: - visited[obj] += 1 - indexes[obj] = m + 1 - yield obj, m + 1 - - for i, obj in enumerate(graph.keys()): - if not visited[obj]: - for t in dfs(obj): - yield t - - visited.clear() - indexes.clear() - - order = collections.defaultdict(list) - for obj, index in topological(dd_graph): - order[index].append(obj) - - for key in sorted(order): - group = order[key] - self.executor.signal_destruction_dependencies(*group) - - for key in sorted(order, reverse=True): - group = order[key] - self.executor.destroy_objects(*group) - for t in group: - yield t - - -# Temporary ObjectStore to load object graphs. Does 2-phase load -# and maintains internal state on what phase is currently running -# as well as objects that are in the middle of initialization. -# Required in order to isolate semi-initialized objects from regular -# objects in main ObjectStore and internal state between graph loads -# in different threads. Once the load is done all objects are copied -# to the parent ObjectStore -class InitializationObjectStore(ObjectStore): - def __init__(self, root_owner, parent_store, keep_ids): - super(InitializationObjectStore, self).__init__( - parent_store.executor, parent_store, weak_store=False) - self._initializing = False - self._root_owner = root_owner - self._keep_ids = keep_ids - self._initializers = [] - - @property - def initializing(self): - return self._initializing - - def load(self, value, owner, default_type=None, - scope_type=None, context=None, **kwargs): - parsed = helpers.parse_object_definition(value, scope_type, context) - if not parsed: - raise ValueError('Invalid object representation format') - - if owner is self._root_owner: - self._initializing = True - - class_obj = parsed['type'] or default_type - if not class_obj: - raise ValueError( - 'Invalid object representation: ' - 'no type information was provided') - if isinstance(class_obj, dsl_types.MuranoTypeReference): - class_obj = class_obj.type - object_id = parsed['id'] - is_tmp_object = (object_id is None and - owner is not self._root_owner and - self._initializing) - obj = None if object_id is None else self.get( - object_id, self._keep_ids) - if not obj: - if is_tmp_object or helpers.is_objects_dry_run_mode(): - mo_type = murano_object.MuranoObject - else: - mo_type = murano_object.RecyclableMuranoObject - obj = mo_type( - class_obj, owner, - name=parsed['name'], - metadata=parsed['metadata'], - object_id=object_id if self._keep_ids else None) - obj.load_dependencies(parsed['dependencies']) - if parsed['destroyed']: - obj.mark_destroyed() - self.put(obj, object_id or obj.object_id) - - system_value = ObjectStore._get_designer_attributes( - parsed['extra']) - self._designer_attributes_store[object_id] = system_value - - if context is None: - context = self.executor.create_object_context(obj) - - def run_initialize(): - self._initializers.extend( - obj.initialize(context, parsed['properties'])) - - run_initialize() - if owner is self._root_owner: - self._initializing = False - run_initialize() - - if owner is self._root_owner: - with helpers.with_object_store(self.parent_store): - for fn in self._initializers: - fn() - - return obj diff --git a/murano/dsl/package_loader.py b/murano/dsl/package_loader.py deleted file mode 100644 index fc231dbb0..000000000 --- a/murano/dsl/package_loader.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 abc - - -class MuranoPackageLoader(object, metaclass=abc.ABCMeta): - @abc.abstractmethod - def load_package(self, package_name, version_spec): - pass - - @abc.abstractmethod - def load_class_package(self, class_name, version_spec): - pass - - @abc.abstractmethod - def register_package(self, package): - pass - - @abc.abstractmethod - def import_fixation_table(self, fixations): - pass - - @abc.abstractmethod - def export_fixation_table(self): - pass - - @abc.abstractmethod - def compact_fixation_table(self): - pass diff --git a/murano/dsl/principal_objects/__init__.py b/murano/dsl/principal_objects/__init__.py deleted file mode 100644 index 6c1c5b4f4..000000000 --- a/murano/dsl/principal_objects/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl.principal_objects import exception -from murano.dsl.principal_objects import garbage_collector -from murano.dsl.principal_objects import stack_trace -from murano.dsl.principal_objects import sys_object - - -def register(package): - package.register_class(sys_object.SysObject) - package.register_class(stack_trace.StackTrace) - package.register_class(exception.DslException) - package.register_class(garbage_collector.GarbageCollector) diff --git a/murano/dsl/principal_objects/exception.py b/murano/dsl/principal_objects/exception.py deleted file mode 100644 index 79dcd8c03..000000000 --- a/murano/dsl/principal_objects/exception.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl - - -@dsl.name('io.murano.Exception') -class DslException(object): - def to_string(self): - return self.get_property('nativeException').format() diff --git a/murano/dsl/principal_objects/garbage_collector.py b/murano/dsl/principal_objects/garbage_collector.py deleted file mode 100644 index eb67b7d42..000000000 --- a/murano/dsl/principal_objects/garbage_collector.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 yaql.language import specs -from yaql.language import yaqltypes - -from murano.dsl import dsl -from murano.dsl import helpers - - -@dsl.name('io.murano.system.GC') -class GarbageCollector(object): - @staticmethod - @specs.parameter('publisher', dsl.MuranoObjectParameter(decorate=False)) - @specs.parameter('subscriber', dsl.MuranoObjectParameter(decorate=False)) - @specs.parameter('handler', yaqltypes.String(nullable=True)) - def subscribe_destruction(publisher, subscriber, handler=None): - publisher_this = publisher.real_this - subscriber_this = subscriber.real_this - - if handler: - subscriber.type.find_single_method(handler) - - dependency = GarbageCollector._find_dependency( - publisher_this, subscriber_this, handler) - - if not dependency: - dependency = {'subscriber': helpers.weak_ref(subscriber_this), - 'handler': handler} - publisher_this.destruction_dependencies.append(dependency) - - @staticmethod - @specs.parameter('publisher', dsl.MuranoObjectParameter(decorate=False)) - @specs.parameter('subscriber', dsl.MuranoObjectParameter(decorate=False)) - @specs.parameter('handler', yaqltypes.String(nullable=True)) - def unsubscribe_destruction(publisher, subscriber, handler=None): - publisher_this = publisher.real_this - subscriber_this = subscriber.real_this - - if handler: - subscriber.type.find_single_method(handler) - - dds = publisher_this.destruction_dependencies - dependency = GarbageCollector._find_dependency( - publisher_this, subscriber_this, handler) - - if dependency: - dds.remove(dependency) - - @staticmethod - def _find_dependency(publisher, subscriber, handler): - dds = publisher.destruction_dependencies - for dd in dds: - if dd['handler'] != handler: - continue - d_subscriber = dd['subscriber'] - if d_subscriber: - d_subscriber = d_subscriber() - if d_subscriber == subscriber: - return dd - - @staticmethod - def collect(): - helpers.get_executor().object_store.cleanup() - - @staticmethod - @specs.parameter('object_', dsl.MuranoObjectParameter(decorate=False)) - def is_doomed(object_): - return helpers.get_object_store().is_doomed(object_) - - @staticmethod - @specs.parameter('object_', dsl.MuranoObjectParameter(decorate=False)) - def is_destroyed(object_): - return object_.destroyed diff --git a/murano/dsl/principal_objects/stack_trace.py b/murano/dsl/principal_objects/stack_trace.py deleted file mode 100644 index f1a03e071..000000000 --- a/murano/dsl/principal_objects/stack_trace.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 inspect -import os.path - -from yaql import specs - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import yaql_integration - - -@dsl.name('io.murano.StackTrace') -class StackTrace(object): - def __init__(self, this, context, include_native_frames=True): - frames = [] - caller_context = context - while True: - if not caller_context: - break - frame = compose_stack_frame(caller_context) - frames.append(frame) - caller_context = helpers.get_caller_context(caller_context) - frames.reverse() - frames.pop() - - if include_native_frames: - native_frames = [] - for frame in inspect.trace()[1:]: - location = dsl_types.ExpressionFilePosition( - os.path.abspath(frame[1]), frame[2], - -1, frame[2], -1) - method = frame[3] - native_frames.append({ - 'instruction': frame[4][0].strip(), - 'location': location, - 'methodName': method, - 'typeName': None - }) - frames.extend(native_frames) - - this.properties.frames = frames - - @specs.meta(constants.META_NO_TRACE, True) - @specs.meta('Usage', 'Action') - def to_string(self, this, prefix=''): - return '\n'.join([format_frame(t, prefix)for t in this['frames']]) - - -def compose_stack_frame(context): - instruction = helpers.get_current_instruction(context) - method = helpers.get_current_method(context) - return { - 'instruction': None if instruction is None - else str(instruction), - - 'location': None if instruction is None - else instruction.source_file_position, - - 'methodName': None if method is None else method.name, - 'typeName': None if method is None else method.declaring_type.name - } - - -def format_frame(frame, prefix=''): - instruction = frame['instruction'] - method_name = frame['methodName'] - type_name = frame['typeName'] - location = frame['location'] - if type_name: - method_name += ' of type ' + type_name - - if location: - args = ( - os.path.abspath(location.file_path), - location.start_line, - ':' + str(location.start_column) - if location.start_column >= 0 else '', - method_name, - instruction, - prefix - ) - return (u'{5}File "{0}", line {1}{2} in method {3}\n' - u'{5} {4}').format(*args) - else: - return u'{2}File in method {0}\n{2} {1}'.format( - method_name, instruction, prefix) - - -def create_stack_trace(context, include_native_frames=True): - stacktrace = yaql_integration.call_func( - context, 'new', 'io.murano.StackTrace', - includeNativeFrames=include_native_frames) - return dsl.MuranoObjectInterface.create(stacktrace) diff --git a/murano/dsl/principal_objects/sys_object.py b/murano/dsl/principal_objects/sys_object.py deleted file mode 100644 index b126a2be1..000000000 --- a/murano/dsl/principal_objects/sys_object.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 yaql import specs - -from murano.dsl import dsl -from murano.dsl import helpers - - -@dsl.name('io.murano.Object') -class SysObject(object): - @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) - def set_attr(self, this, context, name, value, owner=None): - if owner is None: - owner = helpers.get_type(helpers.get_caller_context(context)) - - attribute_store = helpers.get_attribute_store() - attribute_store.set(this.object, owner, name, value) - - @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) - def get_attr(self, this, context, name, default=None, owner=None): - if owner is None: - owner = helpers.get_type(helpers.get_caller_context(context)) - - attribute_store = helpers.get_attribute_store() - - result = attribute_store.get(this.object, owner, name) - return default if result is None else result diff --git a/murano/dsl/reflection.py b/murano/dsl/reflection.py deleted file mode 100644 index 7d8a926f2..000000000 --- a/murano/dsl/reflection.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 semantic_version -from yaql.language import specs -from yaql import yaqlization - -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import meta - - -@specs.yaql_property(dsl_types.MuranoType) -@specs.name('name') -def type_name(murano_type): - return murano_type.name - - -@specs.yaql_property(dsl_types.MuranoType) -@specs.name('usage') -def type_usage(murano_type): - return murano_type.usage - - -@specs.yaql_property(dsl_types.MuranoClass) -def methods(murano_class): - all_method_names = murano_class.all_method_names - return tuple( - murano_method - for name in all_method_names - if not name.startswith('__') and not name.startswith('.') - for murano_method in murano_class.find_method(name) - ) - - -@specs.yaql_property(dsl_types.MuranoClass) -def properties(murano_class): - all_property_names = murano_class.all_property_names - return tuple( - prop - for prop_name in all_property_names - if not prop_name.startswith('__') and not prop_name.startswith('.') - for prop in murano_class.find_property(prop_name) - ) - - -@specs.yaql_property(dsl_types.MuranoClass) -def ancestors(murano_class): - return tuple(murano_class.ancestors()) - - -@specs.yaql_property(dsl_types.MuranoType) -def package(murano_type): - return murano_type.package - - -@specs.yaql_property(dsl_types.MuranoClass) -@specs.name('version') -def type_version(murano_type): - return murano_type.version - - -@specs.yaql_property(dsl_types.MuranoProperty) -@specs.name('name') -def property_name(murano_property): - return murano_property.name - - -# TODO(ativelkov): add 'default' to return some wrapped YAQL expression -# @specs.yaql_property(dsl_types.MuranoProperty) -# @specs.name('default') -# def property_default(murano_property): -# return murano_property.default - - -@specs.yaql_property(dsl_types.MuranoProperty) -@specs.name('has_default') -def property_has_default(murano_property): - return murano_property.has_default - - -@specs.yaql_property(dsl_types.MuranoProperty) -@specs.name('usage') -def property_usage(murano_property): - return murano_property.usage - - -@specs.yaql_property(dsl_types.MuranoProperty) -@specs.name('declaring_type') -def property_owner(murano_property): - return murano_property.declaring_type - - -@specs.name('get_value') -@specs.parameter('property_', dsl_types.MuranoProperty) -@specs.parameter('object_', dsl.MuranoObjectParameter( - nullable=True, decorate=False)) -@specs.method -def property_get_value(context, property_, object_): - if object_ is None: - return helpers.get_executor().get_static_property( - property_.declaring_type, name=property_.name, context=context) - return object_.cast(property_.declaring_type).get_property( - name=property_.name, context=context) - - -@specs.name('set_value') -@specs.parameter('property_', dsl_types.MuranoProperty) -@specs.parameter('object_', dsl.MuranoObjectParameter( - nullable=True, decorate=False)) -@specs.method -def property_set_value(context, property_, object_, value): - if object_ is None: - helpers.get_executor().set_static_property( - property_.declaring_type, - name=property_.name, value=value, context=context) - else: - object_.cast(property_.declaring_type).set_property( - name=property_.name, value=value, context=context) - - -@specs.yaql_property(dsl_types.MuranoMethod) -@specs.name('name') -def method_name(murano_method): - return murano_method.name - - -@specs.yaql_property(dsl_types.MuranoMethod) -def arguments(murano_method): - if murano_method.arguments_scheme is None: - return None - return tuple(murano_method.arguments_scheme.values()) - - -@specs.yaql_property(dsl_types.MuranoMethod) -@specs.name('declaring_type') -def method_owner(murano_method): - return murano_method.declaring_type - - -@specs.parameter('method', dsl_types.MuranoMethod) -@specs.parameter('__object', dsl.MuranoObjectParameter(nullable=True)) -@specs.name('invoke') -@specs.method -def method_invoke(context, method, __object, *args, **kwargs): - return method.invoke(__object, args, kwargs, context) - - -@specs.yaql_property(dsl_types.MuranoPackage) -def types(murano_package): - return tuple( - murano_package.find_class(cls, False) - for cls in murano_package.classes - ) - - -@specs.yaql_property(dsl_types.MuranoPackage) -@specs.name('name') -def package_name(murano_package): - return murano_package.name - - -@specs.yaql_property(dsl_types.MuranoPackage) -@specs.name('version') -def package_version(murano_package): - return murano_package.version - - -@specs.yaql_property(dsl_types.MuranoMethodArgument) -@specs.name('name') -def argument_name(method_argument): - return method_argument.name - -# TODO(ativelkov): add 'default' to return some wrapped YAQL expression -# @specs.yaql_property(dsl_types.MuranoMethodArgument) -# @specs.name('default') -# def argument_default(method_argument): -# return method_argument.default - - -@specs.yaql_property(dsl_types.MuranoMethodArgument) -@specs.name('has_default') -def argument_has_default(method_argument): - return method_argument.has_default - - -@specs.yaql_property(dsl_types.MuranoMethodArgument) -@specs.name('usage') -def argument_usage(method_argument): - return method_argument.usage - - -@specs.yaql_property(dsl_types.MuranoMethodArgument) -@specs.name('declaring_method') -def argument_owner(method_argument): - return method_argument.murano_method - - -@specs.yaql_property(dsl_types.MuranoType) -@specs.name('type') -def type_to_type_ref(murano_type): - return murano_type.get_reference() - - -@specs.parameter('provider', meta.MetaProvider) -@specs.name('#property#meta') -def get_meta(context, provider): - return provider.get_meta(context) - - -@specs.yaql_property(dsl_types.MuranoMetaClass) -def cardinality(murano_meta_class): - return murano_meta_class.cardinality - - -@specs.yaql_property(dsl_types.MuranoMetaClass) -def targets(murano_meta_class): - return murano_meta_class.targets - - -@specs.yaql_property(dsl_types.MuranoMetaClass) -def inherited(murano_meta_class): - return murano_meta_class.inherited - - -def register(context): - funcs = ( - type_name, type_usage, type_version, type_to_type_ref, - methods, properties, ancestors, package, - property_name, property_has_default, property_owner, - property_usage, property_get_value, property_set_value, - method_name, arguments, method_owner, method_invoke, - types, package_name, package_version, - argument_name, argument_has_default, argument_owner, - argument_usage, - cardinality, targets, inherited, - get_meta - ) - for f in funcs: - context.register_function(f) - - -yaqlization.yaqlize(semantic_version.Version, whitelist=[ - 'major', 'minor', 'patch', 'prerelease', 'build' -]) diff --git a/murano/dsl/schema_generator.py b/murano/dsl/schema_generator.py deleted file mode 100644 index 19732cf60..000000000 --- a/murano/dsl/schema_generator.py +++ /dev/null @@ -1,281 +0,0 @@ -# Copyright (c) 2016 Mirantis Inc. -# -# Licensed 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 murano.dsl.contracts import contracts -from murano.dsl import dsl_types -from murano.dsl import executor -from murano.dsl import helpers -from murano.dsl import meta as meta_module -from murano.dsl import murano_type - - -def generate_schema(pkg_loader, context_manager, - class_name, method_names=None, - class_version=None, package_name=None): - """Generate JSON schema - - JSON Schema is generated either for the class with all model builders - or for specified model builders only. The return value is a dictionary - with keys being model builder names and the values are JSON schemas for - them. The class itself is represented by an empty string key. - """ - - if method_names and not isinstance(method_names, (list, tuple)): - method_names = (method_names,) - version = helpers.parse_version_spec(class_version) - if package_name: - package = pkg_loader.load_package(package_name, version) - else: - package = pkg_loader.load_class_package(class_name, version) - - cls = package.find_class(class_name, search_requirements=False) - exc = executor.MuranoDslExecutor(pkg_loader, context_manager) - with helpers.with_object_store(exc.object_store): - context = exc.create_object_context(cls) - model_builders = set(list_model_builders(cls, context)) - method_names = model_builders.intersection( - method_names or model_builders) - - result = { - name: generate_entity_schema( - get_entity(cls, name), context, cls, - get_meta(cls, name, context)) - for name in method_names - } - return result - - -def list_model_builders(cls, context): - """List model builder names of the class - - Yield names of all model builders (static actions marked with appropriate - metadata) plus empty string for the class itself. - """ - - yield '' - for method_name in cls.all_method_names: - try: - method = cls.find_single_method(method_name) - if not method.is_action or not method.is_static: - continue - meta = meta_module.aggregate_meta(method, context) - is_builder = meta.get('io.murano.metadata.ModelBuilder') - if is_builder and is_builder.get_property('enabled'): - yield method.name - except Exception: - pass - - -def get_meta(cls, method_name, context): - """Get metadata dictionary for the method or class""" - if not method_name: - return meta_module.aggregate_meta(cls, context) - - method = cls.find_single_method(method_name) - return meta_module.aggregate_meta(method, context) - - -def get_entity(cls, method_name): - """Get MuranoMethod of the class by its name""" - if not method_name: - return cls - method = cls.find_single_method(method_name) - return method - - -def get_properties(entity): - """Get properties/arg scheme of the class/method""" - if isinstance(entity, dsl_types.MuranoType): - properties = entity.all_property_names - result = {} - for prop_name in properties: - prop = entity.find_single_property(prop_name) - - if prop.usage not in (dsl_types.PropertyUsages.In, - dsl_types.PropertyUsages.InOut): - continue - result[prop_name] = prop - return result - return entity.arguments_scheme - - -def generate_entity_schema(entity, context, declaring_type, meta): - """Generate schema for single class or method by it DSL entity""" - properties = get_properties(entity) - type_weights = murano_type.weigh_type_hierarchy(declaring_type) - schema = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'type': 'object', - 'properties': { - name: generate_property_schema(prop, context, type_weights) - for name, prop in properties.items() - }, - 'additionalProperties': False, - 'formSections': generate_sections(meta, type_weights) - } - schema.update(generate_ui_hints(entity, context, type_weights)) - return schema - - -def generate_sections(meta, type_weights): - """Builds sections definitions for the schema - - Sections are UI hint for UI for grouping inputs into tabs/group-boxes. - The code collects section definitions from type hierarchy considering that - the section might be redefined in ancestor with the different section - index and then re-enumerates them in a way that sections from the most - base classes in hierarchy will get lower index values and there be no - two sections with the same index. - """ - section_list = meta.get('io.murano.metadata.forms.Section', []) - sections_map = {} - for section in section_list: - name = section.get_property('name') - ex_section = sections_map.get(name) - if not ex_section: - pass - elif (type_weights[ex_section.declaring_type.name] < - type_weights[section.declaring_type.name]): - continue - elif (type_weights[ex_section.declaring_type.name] == - type_weights[section.declaring_type.name]): - index = section.get_property('index') - if index is None: - continue - ex_index = ex_section.get_property('index') - if ex_index is not None and ex_index <= index: - continue - sections_map[name] = section - - ordered_sections, unordered_sections = sort_by_index( - sections_map.values(), type_weights) - sections = {} - index = 0 - for section in ordered_sections: - name = section.get_property('name') - if name not in sections: - sections[name] = { - 'title': section.get_property('title'), - 'index': index - } - index += 1 - for section in unordered_sections: - name = section.get_property('name') - if name not in sections: - sections[name] = { - 'title': section.get_property('title') - } - return sections - - -def generate_property_schema(prop, context, type_weights): - """Generate schema for single property/argument""" - schema = translate(prop.contract.spec, context, - prop.declaring_type.package.runtime_version) - if prop.has_default: - schema['default'] = prop.default - schema.update(generate_ui_hints(prop, context, type_weights)) - return schema - - -def generate_ui_hints(entity, context, type_weights): - """Translate know property/arg meta into json-schema UI hints""" - schema = {} - meta = meta_module.aggregate_meta(entity, context) - for cls_name, schema_prop, meta_prop in ( - ('io.murano.metadata.Title', 'title', 'text'), - ('io.murano.metadata.Description', 'description', 'text'), - ('io.murano.metadata.HelpText', 'helpText', 'text'), - ('io.murano.metadata.forms.Hidden', 'visible', 'visible')): - value = meta.get(cls_name) - if value is not None: - schema[schema_prop] = value.get_property(meta_prop) - - position = meta.get('io.murano.metadata.forms.Position') - if position: - schema['formSection'] = position.get_property('section') - index = position.get_property('index') - if index is not None: - schema['formIndex'] = ( - (position.get_property('index') + 1) * len(type_weights) - - type_weights[position.declaring_type.name]) - - return schema - - -def sort_by_index(meta, type_weights, property_name='index'): - """Sorts meta definitions by its distance in the class hierarchy""" - has_index = filter( - lambda m: m.get_property(property_name) is not None, meta) - has_no_index = filter( - lambda m: m.get_property(property_name) is None, meta) - - return ( - sorted(has_index, - key=lambda m: ( - (m.get_property(property_name) + 1) * - len(type_weights) - - type_weights[m.declaring_type.name])), - has_no_index) - - -def translate(contract, context, runtime_version): - """Translates contracts into json-schema equivalents""" - if isinstance(contract, dict): - return translate_dict(contract, context, runtime_version) - elif isinstance(contract, list): - return translate_list(contract, context, runtime_version) - elif isinstance(contract, dsl_types.YaqlExpression): - return contracts.Contract.generate_expression_schema( - contract, context, runtime_version) - - -def translate_dict(contract, context, runtime_version): - """Translates dictionary contracts into json-schema objects""" - properties = {} - additional_properties = False - for key, value in contract.items(): - if isinstance(key, dsl_types.YaqlExpression): - additional_properties = translate(value, context, runtime_version) - else: - properties[key] = translate(value, context, runtime_version) - return { - 'type': 'object', - 'properties': properties, - 'additionalProperties': additional_properties - } - - -def translate_list(contract, context, runtime_version): - """Translates list contracts into json-schema arrays""" - items = [] - for value in contract: - if isinstance(value, int): - pass - else: - items.append(translate(value, context, runtime_version)) - if len(items) == 0: - return {'type': 'array'} - elif len(items) == 1: - return { - 'type': 'array', - 'items': items[0], - } - else: - return { - 'type': 'array', - 'items': items, - 'additionalItems': items[-1] - } diff --git a/murano/dsl/serializer.py b/murano/dsl/serializer.py deleted file mode 100644 index 37bb0d629..000000000 --- a/murano/dsl/serializer.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 yaql import utils - -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers - - -class ObjRef(object): - def __init__(self, obj): - self.ref_obj = obj - - -def serialize(obj, executor, - serialization_type=dsl_types.DumpTypes.Serializable, - allow_refs=True): - with helpers.with_object_store(executor.object_store): - return serialize_model( - obj, executor, allow_refs, - make_copy=False, - serialize_attributes=False, - serialize_actions=False, - serialization_type=serialization_type, - with_destruction_dependencies=False)['Objects'] - - -def _serialize_object(root_object, designer_attributes, allow_refs, - executor, serialize_actions=True, - serialization_type=dsl_types.DumpTypes.Serializable, - with_destruction_dependencies=True): - serialized_objects = set() - - obj = root_object - if isinstance(obj, dsl.MuranoObjectInterface): - obj = obj.object - parent = obj.owner if isinstance(obj, dsl_types.MuranoObject) else None - while True: - obj, need_another_pass = _pass12_serialize( - obj, parent, serialized_objects, designer_attributes, executor, - serialize_actions, serialization_type, allow_refs, - with_destruction_dependencies) - if not need_another_pass: - break - tree = [obj] - _pass3_serialize(tree, serialized_objects, allow_refs) - return tree[0], serialized_objects - - -def serialize_model(root_object, executor, - allow_refs=False, - make_copy=True, - serialize_attributes=True, - serialize_actions=True, - serialization_type=dsl_types.DumpTypes.Serializable, - with_destruction_dependencies=True): - designer_attributes = executor.object_store.designer_attributes - - if root_object is None: - tree = None - tree_copy = None - attributes = [] - else: - with helpers.with_object_store(executor.object_store): - tree, serialized_objects = _serialize_object( - root_object, designer_attributes, allow_refs, executor, - serialize_actions, serialization_type, - with_destruction_dependencies) - - tree_copy = _serialize_object( - root_object, None, allow_refs, executor, serialize_actions, - serialization_type, - with_destruction_dependencies)[0] if make_copy else None - - attributes = executor.attribute_store.serialize( - serialized_objects) if serialize_attributes else None - - return { - 'Objects': tree, - 'ObjectsCopy': tree_copy, - 'Attributes': attributes - } - - -def _serialize_available_action(obj, current_actions, executor): - result = {} - actions = obj.type.find_methods(lambda m: m.is_action) - for action in actions: - action_id = '{0}_{1}'.format(obj.object_id, action.name) - entry = current_actions.get(action_id, {'enabled': True}) - entry['name'] = action.name - context = executor.create_type_context(action.declaring_type) - meta = action.get_meta(context) - meta_dict = {item.type.name: item for item in meta} - title = meta_dict.get('io.murano.metadata.Title') - if title: - entry['title'] = title.get_property('text') - else: - entry['title'] = action.name - description = meta_dict.get('io.murano.metadata.Description') - if description: - entry['description'] = description.get_property('text') - help_text = meta_dict.get('io.murano.metadata.HelpText') - if help_text: - entry['helpText'] = help_text.get_property('text') - result[action_id] = entry - return result - - -def _pass12_serialize(value, parent, serialized_objects, - designer_attributes_getter, executor, - serialize_actions, serialization_type, allow_refs, - with_destruction_dependencies): - if isinstance(value, dsl.MuranoObjectInterface): - value = value.object - if isinstance(value, (str, int, float, bool)) or value is None: - return value, False - if isinstance(value, dsl_types.MuranoObject): - if value.owner is not parent or value.object_id in serialized_objects: - return ObjRef(value), True - elif isinstance(value, ObjRef): - can_move = value.ref_obj.object_id not in serialized_objects - if can_move and allow_refs and value.ref_obj.owner is not None: - can_move = (is_nested_in(parent, value.ref_obj.owner) and - value.ref_obj.owner.object_id in serialized_objects) - if can_move: - value = value.ref_obj - else: - return value, False - if isinstance(value, (dsl_types.MuranoType, - dsl_types.MuranoTypeReference)): - return helpers.format_type_string(value), False - if helpers.is_passkey(value): - return value, False - if isinstance(value, dsl_types.MuranoObject): - result = value.to_dictionary( - serialization_type=serialization_type, allow_refs=allow_refs, - with_destruction_dependencies=with_destruction_dependencies) - if designer_attributes_getter is not None: - if serialization_type == dsl_types.DumpTypes.Inline: - system_data = result - else: - system_data = result['?'] - system_data.update(designer_attributes_getter(value.object_id)) - if serialize_actions: - # deserialize and merge list of actions - system_data['_actions'] = _serialize_available_action( - value, system_data.get('_actions', {}), executor) - serialized_objects.add(value.object_id) - return _pass12_serialize( - result, value, serialized_objects, designer_attributes_getter, - executor, serialize_actions, serialization_type, allow_refs, - with_destruction_dependencies) - elif isinstance(value, utils.MappingType): - result = {} - need_another_pass = False - - for d_key, d_value in value.items(): - if (isinstance(d_key, dsl_types.MuranoType) and - serialization_type == dsl_types.DumpTypes.Serializable): - result_key = str(d_key) - else: - result_key = d_key - if (result_key == 'type' and - isinstance(d_value, dsl_types.MuranoType) and - serialization_type == dsl_types.DumpTypes.Mixed): - result_value = d_value, False - else: - result_value = _pass12_serialize( - d_value, parent, serialized_objects, - designer_attributes_getter, executor, serialize_actions, - serialization_type, allow_refs, - with_destruction_dependencies) - result[result_key] = result_value[0] - if result_value[1]: - need_another_pass = True - return result, need_another_pass - elif utils.is_sequence(value) or isinstance(value, utils.SetType): - need_another_pass = False - result = [] - for t in value: - v, nmp = _pass12_serialize( - t, parent, serialized_objects, designer_attributes_getter, - executor, serialize_actions, serialization_type, allow_refs, - with_destruction_dependencies) - if nmp: - need_another_pass = True - result.append(v) - return result, need_another_pass - else: - raise ValueError() - - -def _pass3_serialize(value, serialized_objects, allow_refs=False): - if isinstance(value, dict): - for d_key, d_value in value.items(): - if isinstance(d_value, ObjRef): - if (d_value.ref_obj.object_id in serialized_objects or - allow_refs): - value[d_key] = d_value.ref_obj.object_id - else: - del value[d_key] - else: - _pass3_serialize(d_value, serialized_objects, allow_refs) - elif isinstance(value, list): - index = 0 - while index < len(value): - item = value[index] - if isinstance(item, ObjRef): - if item.ref_obj.object_id in serialized_objects or allow_refs: - value[index] = item.ref_obj.object_id - else: - value.pop(index) - index -= 1 - else: - _pass3_serialize(item, serialized_objects, allow_refs) - index += 1 - return value - - -def is_nested_in(obj, ancestor): - while True: - if obj is ancestor: - return True - if obj is None: - return False - obj = obj.owner - - -def collect_objects(root_object): - visited = set() - - def rec(obj): - if utils.is_sequence(obj) or isinstance(obj, utils.SetType): - for value in obj: - for t in rec(value): - yield t - elif isinstance(obj, utils.MappingType): - for value in obj.values(): - for t in rec(value): - yield t - elif isinstance(obj, dsl_types.MuranoObjectInterface): - for t in rec(obj.object): - yield t - elif isinstance(obj, dsl_types.MuranoObject) and obj not in visited: - visited.add(obj) - yield obj - for t in rec(obj.to_dictionary()): - yield t - - return rec(root_object) diff --git a/murano/dsl/session_local_storage.py b/murano/dsl/session_local_storage.py deleted file mode 100644 index 62eb65f64..000000000 --- a/murano/dsl/session_local_storage.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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. - -# This code is almost a complete copy of eventlet.corolocal with only -# the concept of current thread replaced with current session - -import collections -import weakref - -from murano.dsl import helpers - - -# the entire purpose of this class is to store off the constructor -# arguments in a local variable without calling __init__ directly -class _localbase(object): - __slots__ = '_local__args', '_local__sessions' - - def __new__(cls, *args, **kw): - self = object.__new__(cls) - object.__setattr__(self, '_local__args', (args, kw)) - object.__setattr__( - self, '_local__sessions', weakref.WeakKeyDictionary()) - if (args or kw) and (cls.__init__ is object.__init__): - raise TypeError('Initialization arguments are not supported') - return self - - -def _patch(session_local): - sessions_dict = object.__getattribute__(session_local, '_local__sessions') - session = helpers.get_execution_session() - localdict = sessions_dict.get(session) - if localdict is None: - # must be the first time we've seen this session, call __init__ - localdict = {} - sessions_dict[session] = localdict - cls = type(session_local) - if cls.__init__ is not object.__init__: - args, kw = object.__getattribute__(session_local, '_local__args') - session_local.__init__(*args, **kw) - object.__setattr__(session_local, '__dict__', localdict) - - -class _local(_localbase): - def __getattribute__(self, attr): - _patch(self) - return object.__getattribute__(self, attr) - - def __setattr__(self, attr, value): - _patch(self) - return object.__setattr__(self, attr, value) - - def __delattr__(self, attr): - _patch(self) - return object.__delattr__(self, attr) - - -def session_local(cls): - return type(cls.__name__, (cls, _local), {}) - - -class SessionLocalDict(collections.UserDict, object): - def __init__(self, **kwargs): - self.__session_data = weakref.WeakKeyDictionary() - self.__default = {} - super(SessionLocalDict, self).__init__(**kwargs) - - @property - def data(self): - session = helpers.get_execution_session() - if session is None: - return self.__default - return self.__session_data.setdefault(session, {}) - - @data.setter - def data(self, value): - session = helpers.get_execution_session() - if session is None: - self.__default = value - else: - self.__session_data[session] = value - - -def execution_session_memoize(func): - cache = SessionLocalDict() - return helpers.get_memoize_func(func, cache) diff --git a/murano/dsl/typespec.py b/murano/dsl/typespec.py deleted file mode 100644 index 2fa85fc3b..000000000 --- a/murano/dsl/typespec.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 weakref - -from murano.dsl.contracts import contracts -from murano.dsl import dsl_types -from murano.dsl import helpers - - -class Spec(object): - def __init__(self, declaration, container_type): - self._container_type = weakref.ref(container_type) - self._contract = contracts.Contract( - declaration['Contract'], container_type) - self._has_default = 'Default' in declaration - self._default = declaration.get('Default') - - def _get_this_context(self, this): - executor = helpers.get_executor() - if isinstance(this, dsl_types.MuranoType): - return executor.create_object_context(this) - return executor.create_object_context( - this.cast(self._container_type())) - - def transform(self, value, this, owner, context, default=None, - finalize=True): - if default is None: - default = self.default - if isinstance(this, dsl_types.MuranoTypeReference): - this = this.type - if isinstance(this, dsl_types.MuranoType): - return self._contract.transform( - value, self._get_this_context(this), - None, None, default, helpers.get_type(context), - finalize=finalize) - else: - return self._contract.transform( - value, self._get_this_context(this), - this, owner, default, helpers.get_type(context), - finalize=finalize) - - def validate(self, value, this, context, default=None): - if default is None: - default = self.default - return self._contract.validate( - value, self._get_this_context(this), default, - helpers.get_type(context)) - - def check_type(self, value, context, default=None): - if default is None: - default = self.default - return self._contract.check_type( - value, context, default, helpers.get_type(context)) - - def finalize(self, value, this, context): - return self._contract.finalize( - value, self._get_this_context(this), helpers.get_type(context)) - - @property - def default(self): - return self._default - - @property - def contract(self): - return self._contract - - @property - def has_default(self): - return self._has_default - - @property - def declaring_type(self): - return self._container_type() diff --git a/murano/dsl/virtual_exceptions.py b/murano/dsl/virtual_exceptions.py deleted file mode 100644 index 9f3e8c75b..000000000 --- a/murano/dsl/virtual_exceptions.py +++ /dev/null @@ -1,158 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import constants -from murano.dsl import dsl_exception -from murano.dsl import expressions -from murano.dsl import helpers -from murano.dsl import macros -from murano.dsl.principal_objects import stack_trace -from murano.dsl import yaql_integration - - -class ThrowMacro(expressions.DslExpression): - def __init__(self, Throw, Message=None, Extra=None, Cause=None): - if not Throw: - raise ValueError() - if not isinstance(Throw, list): - Throw = [Throw] - - self._names = Throw - self._message = Message - self._extra = Extra or {} - self._cause = Cause - - def _resolve_names(self, names, context): - murano_class = helpers.get_type(context) - for name in names: - yield murano_class.namespace_resolver.resolve_name(name) - - def execute(self, context): - stacktrace = stack_trace.create_stack_trace(context, False) - cause = None - if self._cause: - cause = helpers.evaluate(self._cause, context).get_property( - 'nativeException') - raise dsl_exception.MuranoPlException( - list(self._resolve_names(helpers.evaluate(self._names, context), - context)), - helpers.evaluate(self._message, context), - stacktrace, self._extra, cause) - - def __unicode__(self): - if self._message: - return u'Throw {0}: {1}'.format(self._names, self._message) - return u'Throw ' + str(self._names) - - -class CatchBlock(expressions.DslExpression): - def __init__(self, With=None, As=None, Do=None): - if With is not None and not isinstance(With, list): - With = [With] - self._with = With - self._as = As - self._code_block = None if Do is None else macros.CodeBlock(Do) - - def _resolve_names(self, names, context): - murano_class = helpers.get_type(context) - for name in names: - yield murano_class.namespace_resolver.resolve_name(name) - - def execute(self, context): - exception = helpers.get_current_exception(context) - names = ( - None if self._with is None - else list(self._resolve_names(self._with, context)) - ) - - for name in exception.names: - if self._with is None or name in names: - if self._code_block: - if self._as: - wrapped = self._wrap_internal_exception( - exception, context, name) - context[self._as] = wrapped - self._code_block.execute(context) - return True - return False - - def _wrap_internal_exception(self, exception, context, name): - obj = yaql_integration.call_func(context, 'new', 'io.murano.Exception') - obj.set_property('name', name) - obj.set_property('message', exception.message) - obj.set_property('stackTrace', exception.stacktrace) - obj.set_property('extra', exception.extra) - obj.set_property('nativeException', exception) - return obj - - -class TryBlockMacro(expressions.DslExpression): - def __init__(self, Try, Catch=None, Finally=None, Else=None): - self._try_block = macros.CodeBlock(Try) - self._catch_block = None - if Catch is not None: - if not isinstance(Catch, list): - Catch = [Catch] - self._catch_block = [CatchBlock(**c) for c in Catch] - self._finally_block = ( - None if Finally is None - else macros.CodeBlock(Finally)) - self._else_block = ( - None if Else is None - else macros.CodeBlock(Else)) - - def execute(self, context): - try: - self._try_block.execute(context) - except dsl_exception.MuranoPlException as e: - caught = False - if self._catch_block: - try: - context[constants.CTX_CURRENT_EXCEPTION] = e - for cb in self._catch_block: - if cb.execute(context): - caught = True - break - if not caught: - raise - finally: - context[constants.CTX_CURRENT_EXCEPTION] = None - else: - raise - else: - if self._else_block: - self._else_block.execute(context) - finally: - if self._finally_block: - self._finally_block.execute(context) - - -class RethrowMacro(expressions.DslExpression): - def __init__(self, Rethrow): - pass - - def execute(self, context): - exception = context['$?currentException'] - if not exception: - raise TypeError('Rethrow must be inside Catch') - raise exception - - def __str__(self): - return 'Rethrow' - - -def register(): - expressions.register_macro(ThrowMacro) - expressions.register_macro(TryBlockMacro) - expressions.register_macro(RethrowMacro) diff --git a/murano/dsl/yaql_expression.py b/murano/dsl/yaql_expression.py deleted file mode 100644 index 086ec0fdc..000000000 --- a/murano/dsl/yaql_expression.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 re - -from yaql.language import exceptions as yaql_exceptions -from yaql.language import expressions - -from murano.dsl import constants -from murano.dsl import dsl_types -from murano.dsl import yaql_integration - - -EXPRESSION_REGEX = re.compile('^[\s\w\d.]*$') - - -class YaqlExpression(dsl_types.YaqlExpression): - def __init__(self, expression, version): - self._version = version - if isinstance(expression, str): - self._expression = str(expression) - self._parsed_expression = yaql_integration.parse( - self._expression, version) - self._file_position = None - elif isinstance(expression, YaqlExpression): - self._expression = expression._expression - self._parsed_expression = expression._parsed_expression - self._file_position = expression._file_position - elif isinstance(expression, expressions.Statement): - self._expression = str(expression) - self._parsed_expression = expression - self._file_position = None - else: - raise TypeError('expression is not of supported types') - - @property - def expression(self): - return self._expression - - @property - def version(self): - return self._version - - @property - def source_file_position(self): - return self._file_position - - @source_file_position.setter - def source_file_position(self, value): - self._file_position = value - - def __repr__(self): - return 'YAQL(%s)' % self._expression - - def __str__(self): - return self._expression - - @staticmethod - def is_expression(expression, version): - if not isinstance(expression, str): - return False - if EXPRESSION_REGEX.match(expression): - return False - try: - yaql_integration.parse(expression, version) - return True - except yaql_exceptions.YaqlParsingException: - return False - - def __call__(self, context): - if context: - context[constants.CTX_CURRENT_INSTRUCTION] = self - return self._parsed_expression.evaluate(context=context) diff --git a/murano/dsl/yaql_functions.py b/murano/dsl/yaql_functions.py deleted file mode 100644 index b4818ce51..000000000 --- a/murano/dsl/yaql_functions.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 eventlet -from yaql.language import expressions -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import reflection -from murano.dsl import serializer - - -@specs.parameter('object_', dsl.MuranoObjectParameter()) -def id_(object_): - return object_.id - - -@specs.parameter('object_', dsl.MuranoObjectParameter()) -@specs.parameter('type__', dsl.MuranoTypeParameter()) -@specs.parameter('version_spec', yaqltypes.String(True)) -def cast(context, object_, type__, version_spec=None): - return helpers.cast( - object_, type__.type.name, - version_spec or helpers.get_type(context)) - - -@specs.parameter('__type_name', dsl.MuranoTypeParameter()) -@specs.parameter('__extra', utils.MappingType) -@specs.parameter('__owner', dsl.MuranoObjectParameter( - nullable=True, decorate=False)) -@specs.parameter('__object_name', yaqltypes.String(True)) -def new(__context, __type_name, __owner=None, __object_name=None, __extra=None, - **parameters): - - data = { - __type_name: parameters, - 'name': __object_name - } - for key, value in (__extra or {}).items(): - if key.startswith('_'): - data[key] = value - - object_store = helpers.get_object_store() - return object_store.load(data, __owner, context=__context, - scope_type=helpers.get_names_scope(__context)) - - -@specs.parameter('type_name', dsl.MuranoTypeParameter()) -@specs.parameter('parameters', utils.MappingType) -@specs.parameter('extra', utils.MappingType) -@specs.parameter('owner', dsl.MuranoObjectParameter( - nullable=True, decorate=False)) -@specs.parameter('object_name', yaqltypes.String(True)) -@specs.name('new') -def new_from_dict(type_name, context, parameters, - owner=None, object_name=None, extra=None): - return new(context, type_name, owner, object_name, extra, - **utils.filter_parameters_dict(parameters)) - - -@specs.name('new') -@specs.parameter('owner', dsl.MuranoObjectParameter( - nullable=True, decorate=False)) -@specs.parameter('model', utils.MappingType) -def new_from_model(context, model, owner=None): - object_store = helpers.get_object_store() - return object_store.load(model, owner, context=context, - scope_type=helpers.get_names_scope(context)) - - -@specs.parameter('object_', dsl.MuranoObjectParameter(decorate=False)) -@specs.parameter('func', yaqltypes.Lambda()) -def super_(context, object_, func=None): - cast_type = helpers.get_type(context) - if func is None: - return [object_.cast(type) for type in cast_type.parents] - return map(func, super_(context, object_)) - - -@specs.parameter('object_', dsl.MuranoObjectParameter(decorate=False)) -@specs.parameter('func', yaqltypes.Lambda()) -def psuper(context, object_, func=None): - if func is None: - return super_(context, object_) - return helpers.parallel_select(super_(context, object_), func) - - -@specs.extension_method -def require(value): - if value is None: - raise ValueError('Required value is missing') - return value - - -@specs.parameter('obj', dsl.MuranoObjectParameter()) -@specs.parameter('murano_class_ref', dsl.MuranoTypeParameter()) -@specs.extension_method -def find(obj, murano_class_ref): - return obj.find_owner(murano_class_ref, optional=True) - - -@specs.parameter('seconds', yaqltypes.Number()) -def sleep_(seconds): - eventlet.sleep(seconds) - - -@specs.parameter('object_', dsl.MuranoObjectParameter(nullable=True)) -def type_(object_): - return None if object_ is None else object_.type.get_reference() - - -@specs.name('type') -@specs.parameter('object_', dsl.MuranoObjectParameter(nullable=True)) -def type_legacy(object_): - return None if object_ is None else object_.type.name - - -@specs.name('type') -@specs.parameter('cls', dsl.MuranoTypeParameter()) -def type_from_name(cls): - return cls - - -@specs.parameter('object_', dsl.MuranoObjectParameter(nullable=True)) -def typeinfo(object_): - return None if object_ is None else object_.type - - -@specs.parameter('cls', dsl.MuranoTypeParameter()) -@specs.name('typeinfo') -def typeinfo_for_class(cls): - return cls.type - - -@specs.parameter('object_', dsl.MuranoObjectParameter(nullable=True)) -def name(object_): - return None if object_ is None else object_.name - - -@specs.parameter('object_', dsl.MuranoObjectParameter()) -def metadata(object_): - return object_.object.metadata - - -@specs.parameter('obj', dsl.MuranoObjectParameter(decorate=False)) -@specs.parameter('property_name', yaqltypes.Keyword()) -@specs.name('#operator_.') -def obj_attribution(context, obj, property_name): - return obj.get_property(property_name, context) - - -@specs.parameter('cls', dsl_types.MuranoTypeReference) -@specs.parameter('property_name', yaqltypes.Keyword()) -@specs.name('#operator_.') -def obj_attribution_static(context, cls, property_name): - return helpers.get_executor().get_static_property( - cls.type, property_name, context) - - -@specs.parameter('receiver', dsl.MuranoObjectParameter(decorate=False)) -@specs.parameter('expr', yaqltypes.Lambda(method=True)) -@specs.inject('operator', yaqltypes.Super(with_context=True)) -@specs.name('#operator_.') -def op_dot(context, receiver, expr, operator): - executor = helpers.get_executor() - type_context = executor.context_manager.create_type_context(receiver.type) - obj_context = executor.context_manager.create_object_context(receiver) - ctx2 = helpers.link_contexts( - helpers.link_contexts(context, type_context), - obj_context) - return operator(ctx2, receiver, expr) - - -@specs.parameter('receiver', dsl_types.MuranoTypeReference) -@specs.parameter('expr', yaqltypes.Lambda(method=True)) -@specs.inject('operator', yaqltypes.Super(with_context=True)) -@specs.name('#operator_.') -def op_dot_static(context, receiver, expr, operator): - executor = helpers.get_executor() - type_context = executor.context_manager.create_type_context( - receiver.type) - ctx2 = helpers.link_contexts(context, type_context) - return operator(ctx2, receiver, expr) - - -@specs.parameter('prefix', yaqltypes.Keyword()) -@specs.parameter('name', yaqltypes.Keyword()) -@specs.name('#operator_:') -def ns_resolve(context, prefix, name): - return helpers.get_class(prefix + ':' + name, context).get_reference() - - -@specs.parameter('name', yaqltypes.Keyword()) -@specs.name('#unary_operator_:') -def ns_resolve_unary(context, name): - return ns_resolve(context, '', name) - - -@specs.parameter('obj', dsl.MuranoObjectParameter(nullable=True)) -@specs.parameter('type_', dsl.MuranoTypeParameter()) -@specs.name('#operator_is') -def is_instance_of(obj, type_): - if obj is None: - return False - return type_.type.is_compatible(obj) - - -def is_object(value): - return isinstance(value, (dsl_types.MuranoObject, - dsl_types.MuranoTypeReference)) - - -@specs.name('call') -@specs.parameter('name', yaqltypes.String()) -@specs.parameter('args', yaqltypes.Sequence()) -@specs.parameter('kwargs', utils.MappingType) -@specs.inject('op_dot', yaqltypes.Delegate('#operator_.', with_context=True)) -@specs.inject('base', yaqltypes.Super(with_context=True)) -def call_func(context, op_dot, base, name, args, kwargs, - receiver=utils.NO_VALUE): - if isinstance(receiver, (dsl_types.MuranoObject, - dsl_types.MuranoTypeReference)): - kwargs = utils.filter_parameters_dict(kwargs) - args += tuple( - expressions.MappingRuleExpression(expressions.KeywordConstant(key), - value) - for key, value in kwargs.items()) - function = expressions.Function(name, *args) - return op_dot(context, receiver, function) - else: - return base(context, name, args, kwargs, receiver) - - -@specs.parameter('obj', dsl.MuranoObjectParameter(decorate=False)) -@specs.parameter('serialization_type', yaqltypes.String()) -@specs.parameter('ignore_upcasts', bool) -def dump(obj, serialization_type=dsl_types.DumpTypes.Serializable, - ignore_upcasts=True): - if serialization_type not in dsl_types.DumpTypes.All: - raise ValueError('Invalid Serialization Type') - executor = helpers.get_executor() - if ignore_upcasts: - obj = obj.real_this - return serializer.serialize(obj, executor, serialization_type) - - -def register(context, runtime_version): - context.register_function(cast) - context.register_function(new) - context.register_function(new_from_dict) - context.register_function(new_from_model) - context.register_function(id_) - context.register_function(super_) - context.register_function(psuper) - context.register_function(require) - context.register_function(find) - context.register_function(sleep_) - context.register_function(typeinfo) - context.register_function(typeinfo_for_class) - context.register_function(name) - context.register_function(metadata) - context.register_function(obj_attribution) - context.register_function(obj_attribution_static) - context.register_function(op_dot) - context.register_function(op_dot_static) - context.register_function(ns_resolve) - context.register_function(ns_resolve_unary) - reflection.register(context) - context.register_function(is_instance_of) - if runtime_version <= constants.RUNTIME_VERSION_1_3: - context.register_function(type_legacy) - else: - context.register_function(type_) - context.register_function(call_func) - - if runtime_version <= constants.RUNTIME_VERSION_1_1: - context = context.create_child_context() - for t in ('id', 'cast', 'super', 'psuper', 'type'): - for spec in utils.to_extension_method(t, context): - context.register_function(spec) - - context.register_function(type_from_name) - context.register_function(is_object) - context.register_function(dump) - return context diff --git a/murano/dsl/yaql_integration.py b/murano/dsl/yaql_integration.py deleted file mode 100644 index ebfc1bde2..000000000 --- a/murano/dsl/yaql_integration.py +++ /dev/null @@ -1,415 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 weakref - -from oslo_config import cfg -import yaql -from yaql.language import contexts -from yaql.language import conventions -from yaql.language import factory -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes -from yaql import legacy - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import yaql_functions - - -CONF = cfg.CONF -ENGINE_10_OPTIONS = { - 'yaql.limitIterators': CONF.murano.dsl_iterators_limit, - 'yaql.memoryQuota': constants.EXPRESSION_MEMORY_QUOTA, - 'yaql.convertSetsToLists': True, - 'yaql.convertTuplesToLists': True, - 'yaql.iterableDicts': True -} - -ENGINE_12_OPTIONS = { - 'yaql.limitIterators': CONF.murano.dsl_iterators_limit, - 'yaql.memoryQuota': constants.EXPRESSION_MEMORY_QUOTA, - 'yaql.convertSetsToLists': True, - 'yaql.convertTuplesToLists': True -} - - -def _add_operators(engine_factory): - engine_factory.insert_operator( - '>', True, 'is', factory.OperatorType.BINARY_LEFT_ASSOCIATIVE, False) - engine_factory.insert_operator( - None, True, ':', factory.OperatorType.BINARY_LEFT_ASSOCIATIVE, True) - engine_factory.insert_operator( - ':', True, ':', factory.OperatorType.PREFIX_UNARY, False) - engine_factory.operators.insert(0, ()) - - -def _create_engine(runtime_version): - engine_factory = factory.YaqlFactory() - _add_operators(engine_factory=engine_factory) - options = (ENGINE_10_OPTIONS - if runtime_version <= constants.RUNTIME_VERSION_1_1 - else ENGINE_12_OPTIONS) - return engine_factory.create(options=options) - - -@specs.name('#finalize') -def _finalize(obj, context): - return helpers.evaluate(obj, context) - - -CONVENTION = conventions.CamelCaseConvention() -ENGINE_10 = _create_engine(constants.RUNTIME_VERSION_1_0) -ENGINE_12 = _create_engine(constants.RUNTIME_VERSION_1_2) -ROOT_CONTEXT_10 = legacy.create_context( - convention=CONVENTION, finalizer=_finalize) -ROOT_CONTEXT_12 = yaql.create_context( - convention=CONVENTION, finalizer=_finalize) - - -class ContractedValue(yaqltypes.GenericType): - def __init__(self, value_spec, with_check=False): - def converter(value, receiver, context, *args, **kwargs): - if isinstance(receiver, dsl_types.MuranoObject): - this = receiver.real_this - else: - this = receiver - return value_spec.transform( - value, this, context[constants.CTX_ARGUMENT_OWNER], - context) - - def checker(value, context, *args, **kwargs): - return value_spec.check_type(value, context) - - super(ContractedValue, self).__init__( - True, checker if with_check else None, converter) - - def convert(self, value, *args, **kwargs): - if value is None: - return self.converter(value, *args, **kwargs) - return super(ContractedValue, self).convert(value, *args, **kwargs) - - -def create_empty_context(): - context = contexts.Context(convention=CONVENTION) - context.register_function(_finalize) - return context - - -@helpers.memoize -def create_context(runtime_version): - if runtime_version <= constants.RUNTIME_VERSION_1_1: - context = ROOT_CONTEXT_10.create_child_context() - else: - context = ROOT_CONTEXT_12.create_child_context() - context[constants.CTX_YAQL_ENGINE] = choose_yaql_engine(runtime_version) - return yaql_functions.register(context, runtime_version) - - -def choose_yaql_engine(runtime_version): - return (ENGINE_10 if runtime_version <= constants.RUNTIME_VERSION_1_1 - else ENGINE_12) - - -def parse(expression, runtime_version): - return choose_yaql_engine(runtime_version)(expression) - - -def call_func(__context, __name, *args, **kwargs): - engine = __context[constants.CTX_YAQL_ENGINE] - return __context(__name, engine)( - *args, - **{CONVENTION.convert_parameter_name(key): value - for key, value in kwargs.items()}) - - -def _infer_parameter_type(name, class_name): - if name == 'context': - return yaqltypes.Context() - if name == 'this': - return dsl.ThisParameter() - if name == 'interfaces': - return dsl.InterfacesParameter() - if name == 'yaql_engine': - return yaqltypes.Engine() - - if name.startswith('__'): - return _infer_parameter_type(name[2:], class_name) - if class_name and name.startswith('_{0}__'.format(class_name)): - return _infer_parameter_type(name[3 + len(class_name):], class_name) - - -def get_function_definition(func, murano_method, original_name): - cls = murano_method.declaring_type.extension_class - - def param_type_func(name): - return None if not cls else _infer_parameter_type(name, cls.__name__) - - body = func - if (cls is None or helpers.inspect_is_method(cls, original_name) or - helpers.inspect_is_classmethod(cls, original_name)): - body = helpers.function(func) - fd = specs.get_function_definition( - body, convention=CONVENTION, - parameter_type_func=param_type_func) - fd.is_method = True - fd.is_function = False - if not cls or helpers.inspect_is_method(cls, original_name): - fd.set_parameter( - 0, - dsl.MuranoObjectParameter(murano_method.declaring_type), - overwrite=True) - if cls and helpers.inspect_is_classmethod(cls, original_name): - _remove_first_parameter(fd) - body = func - name = getattr(func, '__murano_name', None) - if name: - fd.name = name - fd.insert_parameter(specs.ParameterDefinition( - '?1', yaqltypes.Context(), 0)) - is_static = cls and ( - helpers.inspect_is_static(cls, original_name) or - helpers.inspect_is_classmethod(cls, original_name)) - if is_static: - fd.insert_parameter(specs.ParameterDefinition( - '?2', yaqltypes.PythonType(object), 1)) - - def payload(__context, __self, *args, **kwargs): - with helpers.contextual(__context): - __context[constants.CTX_NAMES_SCOPE] = \ - murano_method.declaring_type - return body(__self.extension, *args, **kwargs) - - def static_payload(__context, __receiver, *args, **kwargs): - with helpers.contextual(__context): - __context[constants.CTX_NAMES_SCOPE] = \ - murano_method.declaring_type - return body(*args, **kwargs) - - if is_static: - fd.payload = static_payload - else: - fd.payload = payload - fd.meta[constants.META_MURANO_METHOD] = murano_method - return fd - - -def _remove_first_parameter(fd): - first_param = None - first_param_name = None - for p_name, p in fd.parameters.items(): - if isinstance(p.value_type, yaqltypes.HiddenParameterType): - continue - if p.position is None: - continue - if first_param is None or p.position < first_param.position: - first_param = p - first_param_name = p_name - if first_param: - del fd.parameters[first_param_name] - for p_name, p in fd.parameters.items(): - if p.position is not None and p.position > first_param.position: - p.position -= 1 - - -def build_stub_function_definitions(murano_method): - if isinstance(murano_method.body, specs.FunctionDefinition): - return _build_native_stub_function_definitions(murano_method) - else: - return _build_mpl_stub_function_definitions(murano_method) - - -def _build_native_stub_function_definitions(murano_method): - runtime_version = murano_method.declaring_type.package.runtime_version - engine = choose_yaql_engine(runtime_version) - - @specs.method - @specs.name(murano_method.name) - @specs.meta(constants.META_MURANO_METHOD, murano_method) - @specs.parameter('__receiver', yaqltypes.NotOfType( - dsl_types.MuranoTypeReference)) - def payload(__context, __receiver, *args, **kwargs): - args = tuple(dsl.to_mutable(arg, engine) for arg in args) - kwargs = dsl.to_mutable(kwargs, engine) - return helpers.evaluate(murano_method.invoke( - __receiver, args, kwargs, __context, True), __context) - - @specs.method - @specs.name(murano_method.name) - @specs.meta(constants.META_MURANO_METHOD, murano_method) - @specs.parameter('__receiver', yaqltypes.NotOfType( - dsl_types.MuranoTypeReference)) - def extension_payload(__context, __receiver, *args, **kwargs): - args = tuple(dsl.to_mutable(arg, engine) for arg in args) - kwargs = dsl.to_mutable(kwargs, engine) - return helpers.evaluate(murano_method.invoke( - murano_method.declaring_type, (__receiver,) + args, kwargs, - __context, True), __context) - - @specs.method - @specs.name(murano_method.name) - @specs.meta(constants.META_MURANO_METHOD, murano_method) - @specs.parameter('__receiver', dsl_types.MuranoTypeReference) - def static_payload(__context, __receiver, *args, **kwargs): - args = tuple(dsl.to_mutable(arg, engine) for arg in args) - kwargs = dsl.to_mutable(kwargs, engine) - return helpers.evaluate(murano_method.invoke( - __receiver, args, kwargs, __context, True), __context) - - if murano_method.usage in dsl_types.MethodUsages.InstanceMethods: - return specs.get_function_definition(payload), None - elif murano_method.usage == dsl_types.MethodUsages.Static: - return (specs.get_function_definition(payload), - specs.get_function_definition(static_payload)) - elif murano_method.usage == dsl_types.MethodUsages.Extension: - return (specs.get_function_definition(extension_payload), - specs.get_function_definition(static_payload)) - else: - raise ValueError('Unknown method usage ' + murano_method.usage) - - -def _build_mpl_stub_function_definitions(murano_method): - if murano_method.usage in dsl_types.MethodUsages.InstanceMethods: - return _create_instance_mpl_stub(murano_method), None - elif murano_method.usage == dsl_types.MethodUsages.Static: - return (_create_instance_mpl_stub(murano_method), - _create_static_mpl_stub(murano_method)) - elif murano_method.usage == dsl_types.MethodUsages.Extension: - return (_create_extension_mpl_stub(murano_method), - _create_static_mpl_stub(murano_method)) - else: - raise ValueError('Unknown method usage ' + murano_method.usage) - - -def _create_instance_mpl_stub(murano_method): - def payload(__context, __receiver, *args, **kwargs): - return murano_method.invoke(__receiver, args, kwargs, __context, True) - fd = _create_basic_mpl_stub(murano_method, 1, payload, False) - - receiver_type = dsl.MuranoObjectParameter( - weakref.proxy(murano_method.declaring_type), decorate=False) - fd.set_parameter(specs.ParameterDefinition('__receiver', receiver_type, 1)) - return fd - - -def _create_static_mpl_stub(murano_method): - def payload(__context, __receiver, *args, **kwargs): - return murano_method.invoke(__receiver, args, kwargs, __context, True) - fd = _create_basic_mpl_stub(murano_method, 1, payload, False) - - receiver_type = dsl.MuranoTypeParameter( - weakref.proxy(murano_method.declaring_type), resolve_strings=False) - fd.set_parameter(specs.ParameterDefinition('__receiver', receiver_type, 1)) - return fd - - -def _create_extension_mpl_stub(murano_method): - def payload(__context, __receiver, *args, **kwargs): - return murano_method.invoke( - murano_method.declaring_type, (__receiver,) + args, kwargs, - __context, True) - return _create_basic_mpl_stub(murano_method, 0, payload, True) - - -def _create_basic_mpl_stub(murano_method, reserve_params, payload, - check_first_arg): - fd = specs.FunctionDefinition( - murano_method.name, payload, is_function=False, is_method=True) - - i = reserve_params + 1 - varargs = False - kwargs = False - for name, arg_spec in murano_method.arguments_scheme.items(): - position = i - if arg_spec.usage == dsl_types.MethodArgumentUsages.VarArgs: - name = '*' - varargs = True - elif arg_spec.usage == dsl_types.MethodArgumentUsages.KwArgs: - name = '**' - position = None - kwargs = True - p = specs.ParameterDefinition( - name, ContractedValue(arg_spec, with_check=check_first_arg), - position=position, default=dsl.NO_VALUE) - check_first_arg = False - fd.parameters[name] = p - i += 1 - - if not varargs: - fd.parameters['*'] = specs.ParameterDefinition( - '*', - value_type=yaqltypes.PythonType(object, nullable=True), - position=i) - if not kwargs: - fd.parameters['**'] = specs.ParameterDefinition( - '**', value_type=yaqltypes.PythonType(object, nullable=True)) - - fd.set_parameter(specs.ParameterDefinition( - '__context', yaqltypes.Context(), 0)) - - fd.meta[constants.META_MURANO_METHOD] = murano_method - return fd - - -def get_class_factory_definition(cls, murano_class): - runtime_version = murano_class.package.runtime_version - engine = choose_yaql_engine(runtime_version) - - def payload(__context, __receiver, *args, **kwargs): - args = tuple(dsl.to_mutable(arg, engine) for arg in args) - kwargs = dsl.to_mutable(kwargs, engine) - with helpers.contextual(__context): - __context[constants.CTX_NAMES_SCOPE] = murano_class - result = helpers.evaluate(cls(*args, **kwargs), __context) - __receiver.object.extension = result - - try: - fd = specs.get_function_definition( - helpers.function(cls.__init__), - parameter_type_func=lambda name: _infer_parameter_type( - name, cls.__name__), - convention=CONVENTION) - except AttributeError: - # __init__ is a slot wrapper inherited from object or other C type - fd = specs.get_function_definition(lambda self: None) - fd.meta[constants.META_NO_TRACE] = True - fd.insert_parameter(specs.ParameterDefinition( - '?1', yaqltypes.Context(), position=0)) - fd.is_method = True - fd.is_function = False - fd.name = '__init__' - fd.payload = payload - return fd - - -def filter_parameters(__fd, *args, **kwargs): - if '*' not in __fd.parameters: - position_args = 0 - for p in __fd.parameters.values(): - if p.position is not None: - position_args += 1 - args = args[:position_args] - kwargs = kwargs.copy() - for name in list(kwargs.keys()): - if not utils.is_keyword(name): - del kwargs[name] - if '**' not in __fd.parameters: - names = {p.alias or p.name for p in __fd.parameters.values()} - for name in list(kwargs.keys()): - if name not in names: - del kwargs[name] - return args, kwargs diff --git a/murano/engine/__init__.py b/murano/engine/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/engine/execution_session.py b/murano/engine/execution_session.py deleted file mode 100644 index 83a32661b..000000000 --- a/murano/engine/execution_session.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 oslo_log import log as logging - - -LOG = logging.getLogger(__name__) - - -class ExecutionSession(object): - def __init__(self): - self.token = None - self.project_id = None - self.user_id = None - self.environment_owner_project_id = None - self.environment_owner_user_id = None - self.trust_id = None - self.system_attributes = {} - self._set_up_list = [] - self._tear_down_list = [] - - def on_session_start(self, delegate): - self._set_up_list.append(delegate) - - def on_session_finish(self, delegate): - self._tear_down_list.append(delegate) - - def start(self): - for delegate in self._set_up_list: - try: - delegate() - except Exception: - LOG.exception('Unhandled exception on invocation of ' - 'pre-execution hook') - self._set_up_list = [] - - def finish(self): - for delegate in self._tear_down_list: - try: - delegate() - except Exception: - LOG.exception('Unhandled exception on invocation of ' - 'post-execution hook') - self._tear_down_list = [] diff --git a/murano/engine/mock_context_manager.py b/murano/engine/mock_context_manager.py deleted file mode 100644 index b8f90e6df..000000000 --- a/murano/engine/mock_context_manager.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# -# Licensed 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 yaql.language import specs -from yaql.language import yaqltypes - -from murano.common import engine -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import yaql_integration - - -class MockContextManager(engine.ContextManager): - - def __init__(self): - # { object_id: [mock_function_definitions] } - self.obj_mock_ctx = {} - - # { class_name: [mock_function_definitions] } - self.class_mock_ctx = {} - - def _create_new_ctx(self, mock_funcs): - mock_context = yaql_integration.create_empty_context() - for mock_func in mock_funcs: - mock_context.register_function(mock_func) - return mock_context - - def _create_new_ctx_for_class(self, name): - new_context = None - if name in self.class_mock_ctx: - new_context = self._create_new_ctx(self.class_mock_ctx[name]) - return new_context - - def _create_new_ctx_for_obj(self, obj_id): - new_context = None - if obj_id in self.obj_mock_ctx: - new_context = self._create_new_ctx(self.obj_mock_ctx[obj_id]) - return new_context - - def create_type_context(self, murano_type): - original_context = super( - MockContextManager, self).create_type_context(murano_type) - - mock_context = self._create_new_ctx_for_class(murano_type.name) - if mock_context: - result_context = helpers.link_contexts( - original_context, mock_context).create_child_context() - else: - result_context = original_context - return result_context - - def create_object_context(self, murano_obj): - original_context = super( - MockContextManager, self).create_object_context(murano_obj) - - mock_context = self._create_new_ctx_for_obj(murano_obj.type.name) - if mock_context: - result_context = helpers.link_contexts( - original_context, mock_context).create_child_context() - else: - result_context = original_context - return result_context - - def create_root_context(self, runtime_version): - root_context = super(MockContextManager, self).create_root_context( - runtime_version) - constext = root_context.create_child_context() - constext.register_function(inject_method_with_str, name='inject') - constext.register_function(inject_method_with_yaql_expr, - name='inject') - constext.register_function(with_original) - return constext - - -@specs.parameter('kwargs', yaqltypes.Lambda(with_context=True)) -def with_original(context, **kwargs): - new_context = context.create_child_context() - - original_context = context[constants.CTX_ORIGINAL_CONTEXT] - for k, v in kwargs.items(): - new_context['$' + k] = v(original_context) - return new_context - - -@specs.parameter( - 'target', - yaqltypes.AnyOf(dsl.MuranoTypeParameter(), dsl.MuranoObjectParameter())) -@specs.parameter('target_method', yaqltypes.String()) -@specs.parameter('mock_object', dsl.MuranoObjectParameter()) -@specs.parameter('mock_name', yaqltypes.String()) -def inject_method_with_str(context, target, target_method, - mock_object, mock_name): - ctx_manager = helpers.get_executor().context_manager - - current_class = helpers.get_type(context) - mock_func = current_class.find_single_method(mock_name) - original_class = target.type - - original_function = original_class.find_single_method(target_method) - result_fd = original_function.instance_stub.clone() - - def payload_adapter(__context, __sender, *args, **kwargs): - return mock_func.invoke( - mock_object, args, kwargs, __context, True) - - result_fd.payload = payload_adapter - existing_mocks = ctx_manager.class_mock_ctx.setdefault( - original_class.name, []) - existing_mocks.append(result_fd) - - -@specs.parameter( - 'target', - yaqltypes.AnyOf(dsl.MuranoTypeParameter(), dsl.MuranoObjectParameter())) -@specs.parameter('target_method', yaqltypes.String()) -@specs.parameter('expr', yaqltypes.Lambda(with_context=True)) -def inject_method_with_yaql_expr(context, target, target_method, expr): - ctx_manager = helpers.get_executor().context_manager - original_class = target.type - - original_function = original_class.find_single_method(target_method) - result_fd = original_function.instance_stub.clone() - - def payload_adapter(__super, __context, __sender, *args, **kwargs): - new_context = context.create_child_context() - new_context[constants.CTX_ORIGINAL_CONTEXT] = __context - mock_obj = context[constants.CTX_THIS] - new_context.register_function(lambda: __super(*args, **kwargs), - name='originalMethod') - return expr(new_context, mock_obj, *args, **kwargs) - - result_fd.payload = payload_adapter - result_fd.insert_parameter('__super', yaqltypes.Super()) - existing_mocks = ctx_manager.class_mock_ctx.setdefault( - original_class.name, []) - existing_mocks.append(result_fd) diff --git a/murano/engine/murano_package.py b/murano/engine/murano_package.py deleted file mode 100644 index cb1cb8a84..000000000 --- a/murano/engine/murano_package.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 json -import os - -from oslo_config import cfg -import yaml - -from murano.dsl import murano_package - -CONF = cfg.CONF - - -class MuranoPackage(murano_package.MuranoPackage): - def __init__(self, package_loader, application_package): - self.application_package = application_package - super(MuranoPackage, self).__init__( - package_loader, - application_package.full_name, - application_package.version, - application_package.runtime_version, - application_package.requirements, - application_package.meta - ) - - def get_class_config(self, name): - version_parts = ( - str(self.version.major), - str(self.version.minor), - str(self.version.patch) - ) - num_parts = len(version_parts) - - for i in range(num_parts + 1): - for ext in ('json', 'yaml'): - if i == num_parts: - if version_parts[0] == '0': - version_suffix = '' - else: - continue - else: - version_suffix = '-' + '.'.join( - version_parts[:num_parts - i]) - file_name = '{name}{version}.{extension}'.format( - name=name, version=version_suffix, extension=ext) - path = os.path.join(CONF.engine.class_configs, file_name) - if os.path.exists(path): - if ext == 'json': - with open(path) as f: - return json.load(f) - else: - with open(path) as f: - return yaml.safe_load(f) - return {} - - def get_resource(self, name): - return self.application_package.get_resource(name) diff --git a/murano/engine/package_loader.py b/murano/engine/package_loader.py deleted file mode 100644 index 6dd197ab9..000000000 --- a/murano/engine/package_loader.py +++ /dev/null @@ -1,637 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 collections -import itertools -import os -import os.path -import shutil -import sys -import tempfile -import uuid - -import eventlet -from muranoclient.common import exceptions as muranoclient_exc -from muranoclient.glance import client as glare_client -import muranoclient.v1.client as muranoclient -from oslo_config import cfg -from oslo_log import log as logging -from oslo_log import versionutils -from oslo_utils import fileutils - -from murano.common import auth_utils -from murano.common import utils -from murano.dsl import constants -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.dsl import package_loader -from murano.engine import murano_package -from murano.engine.system import system_objects -from murano.engine import yaql_yaml_loader -from murano.packages import exceptions as pkg_exc -from murano.packages import load_utils -from murano import utils as m_utils - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - -download_mem_locks = collections.defaultdict(m_utils.ReaderWriterLock) -usage_mem_locks = collections.defaultdict(m_utils.ReaderWriterLock) - - -class ApiPackageLoader(package_loader.MuranoPackageLoader): - def __init__(self, execution_session, root_loader=None): - self._cache_directory = self._get_cache_directory() - self._class_cache = {} - self._package_cache = {} - self._root_loader = root_loader or self - self._execution_session = execution_session - self._last_glare_token = None - self._glare_client = None - self._murano_client = None - self._murano_client_session = None - - self._mem_locks = [] - self._ipc_locks = [] - self._downloaded = [] - self._fixations = collections.defaultdict(set) - self._new_fixations = collections.defaultdict(set) - - def _get_glare_client(self): - glare_settings = CONF.glare - session = auth_utils.get_client_session(self._execution_session) - token = session.auth.get_token(session) - if self._last_glare_token != token: - self._last_glare_token = token - self._glare_client = None - - if self._glare_client is None: - url = glare_settings.url - if not url: - url = session.get_endpoint( - service_type='artifact', - interface=glare_settings.endpoint_type, - region_name=CONF.home_region) - # TODO(gyurco): use auth_utils.get_session_client_parameters - self._glare_client = glare_client.Client( - endpoint=url, token=token, - insecure=glare_settings.insecure, - key_file=glare_settings.keyfile or None, - ca_file=glare_settings.cafile or None, - cert_file=glare_settings.certfile or None, - type_name='murano', - type_version=1) - return self._glare_client - - @property - def client(self): - last_glare_client = self._glare_client - if CONF.engine.packages_service in ['glance', 'glare']: - if CONF.engine.packages_service == 'glance': - versionutils.report_deprecated_feature( - LOG, "'glance' packages_service option has been renamed " - "to 'glare', please update your configuration") - artifacts_client = self._get_glare_client() - else: - artifacts_client = None - if artifacts_client != last_glare_client: - self._murano_client = None - if not self._murano_client: - parameters = auth_utils.get_session_client_parameters( - service_type='application-catalog', - execution_session=self._execution_session, - conf='murano' - ) - self._murano_client = muranoclient.Client( - artifacts_client=artifacts_client, **parameters) - return self._murano_client - - def load_class_package(self, class_name, version_spec): - packages = self._class_cache.get(class_name) - if packages: - version = version_spec.select(packages.keys()) - if version: - return packages[version] - - filter_opts = {'class_name': class_name, - 'version': helpers.breakdown_spec_to_query( - version_spec)} - try: - package_definition = self._get_definition(filter_opts) - self._lock_usage(package_definition) - except LookupError: - exc_info = sys.exc_info() - utils.reraise(exceptions.NoPackageForClassFound, - exceptions.NoPackageForClassFound(class_name), - exc_info[2]) - return self._to_dsl_package( - self._get_package_by_definition(package_definition)) - - def load_package(self, package_name, version_spec): - fixed_versions = self._fixations[package_name] - version = version_spec.select(fixed_versions) - if version: - version_spec = helpers.parse_version_spec(version) - - packages = self._package_cache.get(package_name) - if packages: - version = version_spec.select(packages.keys()) - if version: - return packages[version] - - filter_opts = {'fqn': package_name, - 'version': helpers.breakdown_spec_to_query( - version_spec)} - try: - package_definition = self._get_definition(filter_opts) - self._lock_usage(package_definition) - except LookupError: - exc_info = sys.exc_info() - utils.reraise(exceptions.NoPackageFound, - exceptions.NoPackageFound(package_name), - exc_info[2]) - else: - package = self._get_package_by_definition(package_definition) - self._fixations[package_name].add(package.version) - self._new_fixations[package_name].add(package.version) - return self._to_dsl_package(package) - - def register_package(self, package): - for name in package.classes: - self._class_cache.setdefault(name, {})[package.version] = package - self._package_cache.setdefault(package.name, {})[ - package.version] = package - - @staticmethod - def _get_cache_directory(): - base_directory = ( - CONF.engine.packages_cache or - os.path.join(tempfile.gettempdir(), 'murano-packages-cache') - ) - - if CONF.engine.enable_packages_cache: - directory = os.path.abspath(base_directory) - else: - directory = os.path.abspath(os.path.join(base_directory, - uuid.uuid4().hex)) - - if not os.path.isdir(directory): - fileutils.ensure_tree(directory) - - LOG.debug('Cache for package loader is located at: {dir}'.format( - dir=directory)) - return directory - - def _get_definition(self, filter_opts): - filter_opts['catalog'] = True - try: - packages = list(self.client.packages.filter( - **filter_opts)) - if len(packages) > 1: - LOG.debug('Ambiguous package resolution: more than 1 package ' - 'found for query "{opts}", will resolve based on the' - ' ownership'.format(opts=filter_opts)) - return self._get_best_package_match(packages) - elif len(packages) == 1: - return packages[0] - else: - LOG.debug('There are no packages matching filter ' - '{opts}'.format(opts=filter_opts)) - raise LookupError() - except muranoclient_exc.HTTPException: - LOG.debug('Failed to get package definition from repository') - raise LookupError() - - def _to_dsl_package(self, app_package): - dsl_package = murano_package.MuranoPackage( - self._root_loader, app_package) - for name in app_package.classes: - dsl_package.register_class( - (lambda cls: lambda: get_class(app_package, cls))(name), - name) - if app_package.full_name == constants.CORE_LIBRARY: - system_objects.register(dsl_package) - self.register_package(dsl_package) - return dsl_package - - def _get_package_by_definition(self, package_def): - package_id = package_def.id - package_directory = os.path.join( - self._cache_directory, - package_def.fully_qualified_name, - getattr(package_def, 'version', '0.0.0'), - package_id) - - if os.path.isdir(package_directory): - try: - return load_utils.load_from_dir(package_directory) - except pkg_exc.PackageLoadError: - LOG.exception('Unable to load package from cache. Clean-up.') - shutil.rmtree(package_directory, ignore_errors=True) - - # the package is not yet in cache, let's try to download it. - download_lock_path = os.path.join( - self._cache_directory, '{}_download.lock'.format(package_id)) - download_ipc_lock = m_utils.ExclusiveInterProcessLock( - path=download_lock_path, sleep_func=eventlet.sleep) - - with download_mem_locks[package_id].write_lock(), download_ipc_lock: - - # NOTE(kzaitsev): - # in case there were 2 concurrent threads/processes one might have - # already downloaded this package. Check before trying to download - if os.path.isdir(package_directory): - try: - return load_utils.load_from_dir(package_directory) - except pkg_exc.PackageLoadError: - LOG.error('Unable to load package from cache. Clean-up.') - shutil.rmtree(package_directory, ignore_errors=True) - - # attempt the download itself - try: - LOG.debug("Attempting to download package {} {}".format( - package_def.fully_qualified_name, package_id)) - package_data = self.client.packages.download(package_id) - except muranoclient_exc.HTTPException as e: - msg = 'Error loading package id {0}: {1}'.format( - package_id, str(e) - ) - exc_info = sys.exc_info() - utils.reraise(pkg_exc.PackageLoadError, - pkg_exc.PackageLoadError(msg), - exc_info[2]) - package_file = None - try: - with tempfile.NamedTemporaryFile(delete=False) as package_file: - package_file.write(package_data) - - with load_utils.load_from_file( - package_file.name, - target_dir=package_directory, - drop_dir=False) as app_package: - LOG.info( - "Successfully downloaded and unpacked " - "package {} {}".format( - package_def.fully_qualified_name, package_id)) - self._downloaded.append(app_package) - - self.try_cleanup_cache( - os.path.split(package_directory)[0], - current_id=package_id) - return app_package - except IOError: - msg = 'Unable to extract package data for %s' % package_id - exc_info = sys.exc_info() - utils.reraise(pkg_exc.PackageLoadError, - pkg_exc.PackageLoadError(msg), - exc_info[2]) - finally: - try: - if package_file: - os.remove(package_file.name) - except OSError: - pass - - def try_cleanup_cache(self, package_directory=None, current_id=None): - """Attempts to cleanup cache in a given directory. - - :param package_directory: directory containing cached packages - :param current_id: optional id of the package to exclude from the list - of deleted packages - """ - if not package_directory: - return - - try: - pkg_ids_listed = set(os.listdir(package_directory)) - except OSError: - # No directory for this package, probably someone - # already deleted everything. Anyway nothing to delete - return - - # if current_id was given: ensure it's not checked for removal - pkg_ids_listed -= {current_id} - - for pkg_id in pkg_ids_listed: - stale_directory = os.path.join( - package_directory, - pkg_id) - - if not os.path.isdir(package_directory): - continue - - usage_lock_path = os.path.join( - self._cache_directory, - '{}_usage.lock'.format(pkg_id)) - ipc_lock = m_utils.ExclusiveInterProcessLock( - path=usage_lock_path, sleep_func=eventlet.sleep) - - try: - with usage_mem_locks[pkg_id].write_lock(False) as acquired: - if not acquired: - # the package is in use by other deployment in this - # process will do nothing, someone else would delete it - continue - acquired_ipc_lock = ipc_lock.acquire(blocking=False) - if not acquired_ipc_lock: - # the package is in use by other deployment in another - # process, will do nothing, someone else would delete - continue - - shutil.rmtree(stale_directory, - ignore_errors=True) - ipc_lock.release() - - for lock_type in ('usage', 'download'): - lock_path = os.path.join( - self._cache_directory, - '{}_{}.lock'.format(pkg_id, lock_type)) - try: - os.remove(lock_path) - except OSError: - LOG.warning("Couldn't delete lock file: " - "{}".format(lock_path)) - except RuntimeError: - # couldn't upgrade read lock to write-lock. go on. - continue - - def _get_best_package_match(self, packages): - public = None - other = [] - for package in packages: - if package.owner_id == self._execution_session.project_id: - return package - elif package.is_public: - public = package - else: - other.append(package) - if public is not None: - return public - elif other: - return other[0] - - def _lock_usage(self, package_definition): - """Locks package for usage""" - - # do not lock anything if we do not persist packages on disk - if not CONF.engine.enable_packages_cache: - return - - package_id = package_definition.id - - # A work around the fact that read_lock only supports `with` syntax. - mem_lock = _with_to_generator( - usage_mem_locks[package_id].read_lock()) - - usage_lock_path = os.path.join(self._cache_directory, - '{}_usage.lock'.format(package_id)) - ipc_lock = m_utils.SharedInterProcessLock( - path=usage_lock_path, - sleep_func=eventlet.sleep - ) - ipc_lock = _with_to_generator(ipc_lock) - - next(mem_lock) - next(ipc_lock) - self._mem_locks.append(mem_lock) - self._ipc_locks.append(ipc_lock) - - def import_fixation_table(self, fixations): - self._fixations = deserialize_package_fixations(fixations) - - def export_fixation_table(self): - return serialize_package_fixations(self._fixations) - - def compact_fixation_table(self): - self._fixations = self._new_fixations.copy() - - def cleanup(self): - """Cleans up any lock we had acquired and removes any stale packages""" - - if not CONF.engine.enable_packages_cache: - shutil.rmtree(self._cache_directory, ignore_errors=True) - return - - for lock in itertools.chain(self._mem_locks, self._ipc_locks): - try: - next(lock) - except StopIteration: - continue - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.cleanup() - return False - - -class DirectoryPackageLoader(package_loader.MuranoPackageLoader): - def __init__(self, base_path, root_loader=None): - self._base_path = base_path - self._packages_by_class = {} - self._packages_by_name = {} - self._loaded_packages = set() - self._root_loader = root_loader or self - self._fixations = collections.defaultdict(set) - self._new_fixations = collections.defaultdict(set) - self._build_index() - - def _build_index(self): - for folder in self.search_package_folders(self._base_path): - try: - package = load_utils.load_from_dir(folder) - dsl_package = murano_package.MuranoPackage( - self._root_loader, package) - for class_name in package.classes: - dsl_package.register_class( - (lambda pkg, cls: - lambda: get_class(pkg, cls))(package, class_name), - class_name - ) - if dsl_package.name == constants.CORE_LIBRARY: - system_objects.register(dsl_package) - self.register_package(dsl_package) - except pkg_exc.PackageLoadError: - LOG.info('Unable to load package from path: {0}'.format( - folder)) - continue - LOG.info('Loaded package from path {0}'.format(folder)) - - def import_fixation_table(self, fixations): - self._fixations = deserialize_package_fixations(fixations) - - def export_fixation_table(self): - return serialize_package_fixations(self._fixations) - - def compact_fixation_table(self): - self._fixations = self._new_fixations.copy() - - def load_class_package(self, class_name, version_spec): - packages = self._packages_by_class.get(class_name) - if not packages: - raise exceptions.NoPackageForClassFound(class_name) - version = version_spec.select(packages.keys()) - if not version: - raise exceptions.NoPackageForClassFound(class_name) - return packages[version] - - def load_package(self, package_name, version_spec): - fixed_versions = self._fixations[package_name] - version = version_spec.select(fixed_versions) - if version: - version_spec = helpers.parse_version_spec(version) - packages = self._packages_by_name.get(package_name) - if not packages: - raise exceptions.NoPackageFound(package_name) - version = version_spec.select(packages.keys()) - if not version: - raise exceptions.NoPackageFound(package_name) - self._fixations[package_name].add(version) - self._new_fixations[package_name].add(version) - return packages[version] - - def register_package(self, package): - for c in package.classes: - self._packages_by_class.setdefault(c, {})[ - package.version] = package - self._packages_by_name.setdefault(package.name, {})[ - package.version] = package - - @property - def packages(self): - for package_versions in self._packages_by_name.values(): - for package in package_versions.values(): - yield package - - @staticmethod - def split_path(path): - tail = True - while tail: - path, tail = os.path.split(path) - if tail: - yield path - - @classmethod - def search_package_folders(cls, path): - packages = set() - for folder, _, files in os.walk(path): - if 'manifest.yaml' in files: - found = False - for part in cls.split_path(folder): - if part in packages: - found = True - break - if not found: - packages.add(folder) - yield folder - - def cleanup(self): - """A stub for possible cleanup""" - pass - - -class CombinedPackageLoader(package_loader.MuranoPackageLoader): - def __init__(self, execution_session, root_loader=None): - root_loader = root_loader or self - self.api_loader = ApiPackageLoader(execution_session, root_loader) - self.directory_loaders = [] - - for folder in CONF.engine.load_packages_from: - if os.path.exists(folder): - self.directory_loaders.append(DirectoryPackageLoader( - folder, root_loader)) - - def load_package(self, package_name, version_spec): - for loader in self.directory_loaders: - try: - return loader.load_package(package_name, version_spec) - except exceptions.NoPackageFound: - continue - return self.api_loader.load_package( - package_name, version_spec) - - def load_class_package(self, class_name, version_spec): - for loader in self.directory_loaders: - try: - return loader.load_class_package(class_name, version_spec) - except exceptions.NoPackageForClassFound: - continue - return self.api_loader.load_class_package( - class_name, version_spec) - - def register_package(self, package): - self.api_loader.register_package(package) - - def export_fixation_table(self): - result = deserialize_package_fixations( - self.api_loader.export_fixation_table()) - for loader in self.directory_loaders: - fixations = deserialize_package_fixations( - loader.export_fixation_table()) - for key, value in fixations.items(): - result[key].update(value) - return serialize_package_fixations(result) - - def import_fixation_table(self, fixations): - self.api_loader.import_fixation_table(fixations) - for loader in self.directory_loaders: - loader.import_fixation_table(fixations) - - def compact_fixation_table(self): - self.api_loader.compact_fixation_table() - for loader in self.directory_loaders: - loader.compact_fixation_table() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.cleanup() - return False - - def cleanup(self): - """Calls cleanup method of all loaders we combine""" - self.api_loader.cleanup() - for d_loader in self.directory_loaders: - d_loader.cleanup() - - -def get_class(package, name): - version = package.runtime_version - loader = yaql_yaml_loader.get_loader(version) - contents, file_id = package.get_class(name) - return loader(contents, file_id) - - -def _with_to_generator(context_obj): - with context_obj as obj: - yield obj - yield - - -def deserialize_package_fixations(fixations): - result = collections.defaultdict(set) - for name, versions in fixations.items(): - for version in versions: - result[name].add(helpers.parse_version(version)) - return result - - -def serialize_package_fixations(fixations): - return { - name: list(str(v) for v in versions) - for name, versions in fixations.items() - } diff --git a/murano/engine/system/__init__.py b/murano/engine/system/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/engine/system/agent.py b/murano/engine/system/agent.py deleted file mode 100644 index 2658a4edc..000000000 --- a/murano/engine/system/agent.py +++ /dev/null @@ -1,389 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 copy -import datetime -import os -import os.path -import time -import urllib -import uuid - -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import padding -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization -import eventlet.event -from oslo_config import cfg -from oslo_log import log as logging -from oslo_serialization import base64 -from yaql import specs - -import murano.common.exceptions as exceptions -from murano.common.messaging import message -from murano.dsl import dsl -import murano.engine.system.common as common - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class AgentException(Exception): - pass - - -@dsl.name('io.murano.system.Agent') -class Agent(object): - def __init__(self, host): - self._enabled = False - if CONF.engine.disable_murano_agent: - LOG.debug('Use of murano-agent is disallowed ' - 'by the server configuration') - self._host = host - self._enabled = not CONF.engine.disable_murano_agent - env = host.find_owner('io.murano.Environment') - self._queue = str('e%s-h%s' % (env.id, host.id)).lower() - self._signing_key = None - if CONF.engine.signing_key: - key_path = os.path.expanduser(CONF.engine.signing_key) - if not os.path.exists(key_path): - LOG.warn("Key file %s does not exist. " - "Message signing is disabled") - else: - with open(key_path, "rb") as key_file: - key_data = key_file.read() - self._signing_key = serialization.load_pem_private_key( - key_data, password=None, backend=default_backend()) - self._last_stamp = 0 - self._initialized = False - - @property - def enabled(self): - return self._enabled - - @specs.parameter('line_prefix', specs.yaqltypes.String()) - def signing_key(self, line_prefix=''): - if not self._signing_key: - return "" - key = self._signing_key.public_key().public_bytes( - serialization.Encoding.PEM, - serialization.PublicFormat.PKCS1) - return line_prefix + line_prefix.join(key.splitlines(True)) - - def _initialize(self): - if self._initialized: - return - - # (sjmc7) - turn this into a no-op if agents are disabled - if CONF.engine.disable_murano_agent: - LOG.debug('Use of murano-agent is disallowed ' - 'by the server configuration') - else: - region = dsl.MuranoObjectInterface.create(self._host().getRegion()) - with common.create_rmq_client(region) as client: - client.declare(self._queue, enable_ha=True, ttl=86400000) - self._initialized = True - - def queue_name(self): - return self._queue - - def _check_enabled(self): - if CONF.engine.disable_murano_agent: - raise exceptions.PolicyViolationException( - 'Use of murano-agent is disallowed ' - 'by the server configuration') - - def _prepare_message(self, template, msg_id): - msg = message.Message() - msg.body = template - msg.id = msg_id - return msg - - def _send(self, template, wait_results, timeout): - """Send a message over the MQ interface.""" - self._initialize() - region = self._host().getRegion() - msg_id = template.get('ID', uuid.uuid4().hex) - if wait_results: - event = eventlet.event.Event() - listener = region['agentListener'] - listener().subscribe(msg_id, event) - - msg = self._prepare_message(template, msg_id) - with common.create_rmq_client(region) as client: - client.send(message=msg, key=self._queue, signing_func=self._sign) - if wait_results: - try: - with eventlet.Timeout(timeout): - result = event.wait() - - except eventlet.Timeout: - listener().unsubscribe(msg_id) - raise exceptions.TimeoutException( - 'The murano-agent did not respond ' - 'within {0} seconds'.format(timeout)) - - if not result: - return None - - if result.get('FormatVersion', '1.0.0').startswith('1.'): - return self._process_v1_result(result) - - else: - return self._process_v2_result(result) - - else: - return None - - @specs.parameter( - 'resources', dsl.MuranoObjectParameter('io.murano.system.Resources')) - def call(self, template, resources, timeout=None): - if timeout is None: - timeout = CONF.engine.agent_timeout - self._check_enabled() - plan = self.build_execution_plan(template, resources()) - return self._send(plan, True, timeout) - - @specs.parameter( - 'resources', dsl.MuranoObjectParameter('io.murano.system.Resources')) - def send(self, template, resources): - self._check_enabled() - plan = self.build_execution_plan(template, resources()) - return self._send(plan, False, 0) - - def call_raw(self, plan, timeout=None): - if timeout is None: - timeout = CONF.engine.agent_timeout - self._check_enabled() - return self._send(plan, True, timeout) - - def send_raw(self, plan): - self._check_enabled() - return self._send(plan, False, 0) - - def is_ready(self, timeout=100): - try: - self.wait_ready(timeout) - except exceptions.TimeoutException: - return False - else: - return True - - def wait_ready(self, timeout=100): - self._check_enabled() - template = {'Body': 'return', 'FormatVersion': '2.0.0', 'Scripts': {}} - self.call_raw(template, timeout) - - def _sign(self, msg): - if not self._signing_key: - return None - return self._signing_key.sign( - (self._queue + msg).encode('utf-8'), - padding.PKCS1v15(), hashes.SHA256()) - - def _process_v1_result(self, result): - if result['IsException']: - raise AgentException(dict(self._get_exception_info( - result.get('Result', [])), source='execution_plan')) - else: - results = result.get('Result', []) - if not result: - return None - value = results[-1] - if value['IsException']: - raise AgentException(dict(self._get_exception_info( - value.get('Result', [])), source='command')) - else: - return value.get('Result') - - def _process_v2_result(self, result): - error_code = result.get('ErrorCode', 0) - if not error_code: - return result.get('Body') - else: - body = result.get('Body') or {} - err = { - 'message': body.get('Message'), - 'details': body.get('AdditionalInfo'), - 'errorCode': error_code, - 'time': result.get('Time') - } - for attr in ('Message', 'AdditionalInfo'): - if attr in body: - del body[attr] - err['extra'] = body if body else None - raise AgentException(err) - - def _get_array_item(self, array, index): - return array[index] if len(array) > index else None - - def _get_exception_info(self, data): - data = data or [] - return { - 'type': self._get_array_item(data, 0), - 'message': self._get_array_item(data, 1), - 'command': self._get_array_item(data, 2), - 'details': self._get_array_item(data, 3), - 'timestamp': datetime.datetime.now().isoformat() - } - - def build_execution_plan(self, template, resources): - template = copy.deepcopy(template) - if not isinstance(template, dict): - raise ValueError('Incorrect execution plan ') - format_version = template.get('FormatVersion') - if not format_version or format_version.startswith('1.'): - return self._build_v1_execution_plan(template, resources) - else: - return self._build_v2_execution_plan(template, resources) - - def _generate_stamp(self): - stamp = int(time.time() * 10000) - if stamp <= self._last_stamp: - stamp = self._last_stamp + 1 - self._last_stamp = stamp - return stamp - - def _build_v1_execution_plan(self, template, resources): - scripts_folder = 'scripts' - script_files = template.get('Scripts', []) - scripts = [] - for script in script_files: - script_path = os.path.join(scripts_folder, script) - scripts.append(base64.encode_as_text( - resources.string(script_path, binary=True), - encoding='latin1')) - template['Scripts'] = scripts - template['Stamp'] = self._generate_stamp() - return template - - def _build_v2_execution_plan(self, template, resources): - scripts_folder = 'scripts' - plan_id = uuid.uuid4().hex - template['ID'] = plan_id - template['Stamp'] = self._generate_stamp() - - if 'Action' not in template: - template['Action'] = 'Execute' - if 'Files' not in template: - template['Files'] = {} - - files = {} - for file_id, file_descr in template['Files'].items(): - files[file_descr['Name']] = file_id - - for name, script in template.get('Scripts', {}).items(): - if 'EntryPoint' not in script: - raise ValueError('No entry point in script ' + name) - - if 'Application' == script['Type']: - if script['EntryPoint'] not in files: - script['EntryPoint'] = self._place_file( - scripts_folder, script['EntryPoint'], - template, resources, files) - else: - script['EntryPoint'] = files[script['EntryPoint']] - if 'Files' in script: - for i, file in enumerate(script['Files']): - if self._get_name(file) not in files: - script['Files'][i] = self._place_file( - scripts_folder, file, template, resources, files) - else: - script['Files'][i] = files[file] - return template - - def _is_url(self, file): - file = self._get_url(file) - parts = urllib.parse.urlsplit(file) - if not parts.scheme or not parts.netloc: - return False - else: - return True - - def _get_url(self, file): - if isinstance(file, dict): - return list(file.values())[0] - else: - return file - - def _get_name(self, file): - if isinstance(file, dict): - name = list(file.keys())[0] - else: - name = file - - if self._is_url(name): - name = name[name.rindex('/') + 1:len(name)] - elif name.startswith('<') and name.endswith('>'): - name = name[1: -1] - return name - - def _get_file_value(self, file): - if isinstance(file, dict): - file = list(file.values())[0] - return file - - def _get_body(self, file, resources, folder): - use_base64 = self._is_base64(file) - if use_base64: - path = os.path.join(folder, file[1: -1]) - body = resources.string(path, binary=True) - body = base64.encode_as_text(body) + "\n" - else: - path = os.path.join(folder, file) - body = resources.string(path) - return body - - def _is_base64(self, file): - return file.startswith('<') and file.endswith('>') - - def _get_body_type(self, file): - return 'Base64' if self._is_base64(file) else 'Text' - - def _place_file(self, folder, file, template, resources, files): - file_value = self._get_file_value(file) - name = self._get_name(file) - file_id = uuid.uuid4().hex - - if self._is_url(file_value): - template['Files'][file_id] = self._get_file_des_downloadable(file) - files[name] = file_id - - else: - template['Files'][file_id] = self._get_file_description( - file, resources, folder) - files[name] = file_id - return file_id - - def _get_file_des_downloadable(self, file): - name = self._get_name(file) - file = self._get_file_value(file) - return { - 'Name': str(name), - 'URL': file, - 'Type': 'Downloadable' - } - - def _get_file_description(self, file, resources, folder): - name = self._get_name(file) - file_value = self._get_file_value(file) - - body_type = self._get_body_type(file_value) - body = self._get_body(file_value, resources, folder) - return { - 'Name': name, - 'BodyType': body_type, - 'Body': body - } diff --git a/murano/engine/system/agent_listener.py b/murano/engine/system/agent_listener.py deleted file mode 100644 index 66d83de95..000000000 --- a/murano/engine/system/agent_listener.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 greenlet -from oslo_config import cfg -from oslo_log import log as logging - -from murano.common import exceptions -from murano.dsl import dsl -from murano.engine.system import common - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - - -class AgentListenerException(Exception): - pass - - -@dsl.name('io.murano.system.AgentListener') -class AgentListener(object): - def __init__(self, name): - self._enabled = not CONF.engine.disable_murano_agent - self._results_queue = str('-execution-results-%s' % name.lower()) - self._subscriptions = {} - self._receive_thread = None - - def _check_enabled(self): - if CONF.engine.disable_murano_agent: - LOG.error('Use of murano-agent is disallowed ' - 'by the server configuration') - - raise exceptions.PolicyViolationException( - 'Use of murano-agent is disallowed ' - 'by the server configuration') - - @property - def enabled(self): - return self._enabled - - def queue_name(self): - return self._results_queue - - def start(self): - if CONF.engine.disable_murano_agent: - # Noop - LOG.debug("murano-agent is disabled by the server") - return - - if self._receive_thread is None: - dsl.get_execution_session().on_session_finish( - lambda: self.stop()) - self._receive_thread = dsl.spawn( - self._receive, - dsl.get_this().find_owner('io.murano.CloudRegion')) - - def stop(self): - if CONF.engine.disable_murano_agent: - # Noop - LOG.debug("murano-agent is disabled by the server") - return - - if self._receive_thread is not None: - self._receive_thread.kill() - try: - self._receive_thread.wait() - except greenlet.GreenletExit: - pass - finally: - self._receive_thread = None - - def subscribe(self, message_id, event): - self._check_enabled() - self._subscriptions[message_id] = event - self.start() - - def unsubscribe(self, message_id): - self._check_enabled() - self._subscriptions.pop(message_id) - - def _receive(self, region): - with common.create_rmq_client(region) as client: - client.declare(self._results_queue, enable_ha=True, ttl=86400000) - with client.open(self._results_queue) as subscription: - while True: - msg = subscription.get_message() - if not msg: - continue - msg.ack() - msg_id = msg.body.get('SourceID', msg.id) - LOG.debug("Got execution result: id '{msg_id}'" - " body '{body}'".format(msg_id=msg_id, - body=msg.body)) - if msg_id in self._subscriptions: - event = self._subscriptions.pop(msg_id) - event.send(msg.body) diff --git a/murano/engine/system/common.py b/murano/engine/system/common.py deleted file mode 100644 index 7f1422a12..000000000 --- a/murano/engine/system/common.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 oslo_config import cfg - -from murano.common.messaging import mqclient - -CONF = cfg.CONF - - -def create_rmq_client(region): - region_config = region().getConfig() - rmq_settings = dict(region_config['agentRabbitMq']) - rmq_settings['ca_certs'] = CONF.rabbitmq.ca_certs.strip() or None - return mqclient.MqClient(**rmq_settings) diff --git a/murano/engine/system/heat_stack.py b/murano/engine/system/heat_stack.py deleted file mode 100644 index 1fbf933ca..000000000 --- a/murano/engine/system/heat_stack.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 copy -import json - -import eventlet -import heatclient.client as hclient -import heatclient.exc as heat_exc -from oslo_config import cfg -from oslo_log import log as logging - -from murano.common import auth_utils -from murano.common.helpers import token_sanitizer -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import session_local_storage - -LOG = logging.getLogger(__name__) -CONF = cfg.CONF - -HEAT_TEMPLATE_VERSION = '2013-05-23' - - -class HeatStackError(Exception): - pass - - -@dsl.name('io.murano.system.HeatStack') -class HeatStack(object): - def __init__(self, name, description=None, region_name=None): - self._name = name - self._template = None - self._parameters = {} - self._files = {} - self._hot_environment = {} - self._applied = True - self._description = description - self._last_stack_timestamps = (None, None) - self._tags = '' - self._region_name = region_name - self._push_thread = None - - def _is_push_thread_alive(self): - return self._push_thread is not None and not self._push_thread.dead - - def _kill_push_thread(self): - if self._is_push_thread_alive(): - self._push_thread.cancel() - self._wait_push_thread() - - def _wait_push_thread(self): - if not self._is_push_thread_alive(): - return - self._push_thread.wait() - - @staticmethod - def _create_client(session, region_name): - parameters = auth_utils.get_session_client_parameters( - service_type='orchestration', region=region_name, - conf='heat', session=session) - return hclient.Client('1', **parameters) - - @property - def _client(self): - return self._get_client(self._region_name) - - @staticmethod - @session_local_storage.execution_session_memoize - def _get_client(region_name): - session = auth_utils.get_client_session(conf='heat') - return HeatStack._create_client(session, region_name) - - def _get_token_client(self): - ks_session = auth_utils.get_token_client_session(conf='heat') - return self._create_client(ks_session, self._region_name) - - def current(self): - if self._template is not None: - return self._template - try: - stack_info = self._client.stacks.get(stack_id=self._name) - template = self._client.stacks.template( - stack_id='{0}/{1}'.format( - stack_info.stack_name, - stack_info.id)) - self._template = template - self._parameters.update( - HeatStack._remove_system_params(stack_info.parameters)) - self._applied = True - return self._template.copy() - except heat_exc.HTTPNotFound: - self._applied = True - self._template = {} - self._parameters.clear() - return {} - - def parameters(self): - self.current() - return self._parameters.copy() - - def reload(self): - self._template = None - self._parameters.clear() - return self.current() - - def set_template(self, template): - self._template = template - self._parameters.clear() - self._applied = False - - def set_parameters(self, parameters): - self._parameters = parameters - self._applied = False - - def set_files(self, files): - self._files = files - self._applied = False - - def set_hot_environment(self, hot_environment): - self._hot_environment = hot_environment - self._applied = False - - def update_template(self, template): - template_version = template.get('heat_template_version', - HEAT_TEMPLATE_VERSION) - if template_version != HEAT_TEMPLATE_VERSION: - err_msg = ("Currently only heat_template_version %s " - "is supported." % HEAT_TEMPLATE_VERSION) - raise HeatStackError(err_msg) - current = self.current() - self._template = helpers.merge_dicts(self._template, template) - self._applied = self._template == current and self._applied - - @staticmethod - def _remove_system_params(parameters): - return dict((k, v) for k, v in parameters.items() if - not k.startswith('OS::')) - - def _get_status(self): - status = [None] - - def status_func(state_value): - status[0] = state_value - return True - self._wait_state(status_func) - return status[0] - - def _wait_state(self, status_func, wait_progress=False): - tries = 4 - delay = 1 - while tries > 0: - while True: - try: - stack_info = self._client.stacks.get( - stack_id=self._name) - status = stack_info.stack_status - tries = 4 - delay = 1 - except heat_exc.HTTPNotFound: - stack_info = None - status = 'NOT_FOUND' - except Exception: - tries -= 1 - delay *= 2 - if not tries: - raise - eventlet.sleep(delay) - break - - if 'IN_PROGRESS' in status: - eventlet.sleep(2) - continue - - last_stack_timestamps = self._last_stack_timestamps - self._last_stack_timestamps = (None, None) if not stack_info \ - else(stack_info.creation_time, stack_info.updated_time) - - if (wait_progress and last_stack_timestamps == - self._last_stack_timestamps and - last_stack_timestamps != (None, None)): - eventlet.sleep(2) - continue - - if not status_func(status): - reason = ': {0}'.format( - stack_info.stack_status_reason) if stack_info else '' - raise EnvironmentError( - "Unexpected stack state {0}{1}".format(status, reason)) - - try: - return dict([(t['output_key'], t['output_value']) - for t in stack_info.outputs]) - except Exception: - return {} - return {} - - def output(self): - self._wait_push_thread() - return self._wait_state(lambda status: True) - - def _push(self, object_store=None): - template = copy.deepcopy(self._template) - s_template = token_sanitizer.TokenSanitizer().sanitize(template) - LOG.debug('Pushing: {template}'.format( - template=json.dumps(s_template))) - object_store = object_store or helpers.get_object_store() - while True: - try: - with helpers.with_object_store(object_store): - current_status = self._get_status() - resources = template.get('Resources') or template.get( - 'resources') - if current_status == 'NOT_FOUND': - if resources is not None: - token_client = self._get_token_client() - token_client.stacks.create( - stack_name=self._name, - parameters=self._parameters, - template=template, - files=self._files, - environment=self._hot_environment, - disable_rollback=True, - tags=self._tags) - - self._wait_state( - lambda status: status == 'CREATE_COMPLETE') - else: - if resources is not None: - self._client.stacks.update( - stack_id=self._name, - parameters=self._parameters, - files=self._files, - environment=self._hot_environment, - template=template, - disable_rollback=True, - tags=self._tags) - self._wait_state( - lambda status: status == 'UPDATE_COMPLETE', - True) - else: - self.delete() - except heat_exc.HTTPConflict as e: - LOG.warning('Conflicting operation: {msg}'.format(msg=e)) - eventlet.sleep(3) - else: - break - - self._applied = self._template == template - - def push(self, is_async=False): - if self._applied or self._template is None: - return - - self._tags = ','.join(CONF.heat.stack_tags) - if 'heat_template_version' not in self._template: - self._template['heat_template_version'] = HEAT_TEMPLATE_VERSION - - if 'description' not in self._template and self._description: - self._template['description'] = self._description - - self._kill_push_thread() - if is_async: - if self._push_thread is None: - - def cleanup(): - try: - self._wait_push_thread() - finally: - self._push_thread = None - - dsl.get_execution_session().on_session_finish(cleanup) - - self._push_thread =\ - eventlet.greenthread.spawn_after( - 1, self._push, helpers.get_object_store()) - else: - self._push() - - def delete(self): - self._kill_push_thread() - while True: - try: - if not self.current(): - return - self._wait_state(lambda s: True) - self._client.stacks.delete(stack_id=self._name) - self._wait_state( - lambda status: status in ('DELETE_COMPLETE', 'NOT_FOUND'), - wait_progress=True) - except heat_exc.NotFound: - LOG.warning('Stack {stack_name} already deleted?' - .format(stack_name=self._name)) - break - except heat_exc.HTTPConflict as e: - LOG.warning('Conflicting operation: {msg}'.format(msg=e)) - eventlet.sleep(3) - else: - break - - self._template = {} - self._applied = True diff --git a/murano/engine/system/instance_reporter.py b/murano/engine/system/instance_reporter.py deleted file mode 100644 index c028a105d..000000000 --- a/murano/engine/system/instance_reporter.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 oslo_config import cfg -import oslo_messaging as messaging - -from murano.common import rpc -from murano.common import uuidutils -from murano.dsl import dsl - -CONF = cfg.CONF - -UNCLASSIFIED = 0 -APPLICATION = 100 -OS_INSTANCE = 200 - - -@dsl.name('io.murano.system.InstanceNotifier') -class InstanceReportNotifier(object): - def __init__(self, environment): - if not rpc.initialized(): - rpc.init() - self._notifier = messaging.Notifier( - rpc.NOTIFICATION_TRANSPORT, - publisher_id=uuidutils.generate_uuid(), - topics=['murano']) - self._environment_id = environment.id - - def _track_instance(self, instance, instance_type, - type_title, unit_count): - payload = { - 'instance': instance.id, - 'environment': self._environment_id, - 'instance_type': instance_type, - 'type_name': instance.type.name, - 'type_title': type_title, - 'unit_count': unit_count - } - - self._notifier.info({}, 'murano.track_instance', payload) - - def _untrack_instance(self, instance, instance_type): - payload = { - 'instance': instance.id, - 'environment': self._environment_id, - 'instance_type': instance_type, - } - - self._notifier.info({}, 'murano.untrack_instance', payload) - - def track_application(self, instance, title=None, unit_count=None): - self._track_instance(instance, APPLICATION, title, unit_count) - - def untrack_application(self, instance): - self._untrack_instance(instance, APPLICATION) - - def track_cloud_instance(self, instance): - self._track_instance(instance, OS_INSTANCE, None, 1) - - def untrack_cloud_instance(self, instance): - self._untrack_instance(instance, OS_INSTANCE) diff --git a/murano/engine/system/logger.py b/murano/engine/system/logger.py deleted file mode 100644 index c2e09931e..000000000 --- a/murano/engine/system/logger.py +++ /dev/null @@ -1,112 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# -# Licensed 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 oslo_log import log as logging -from yaql.language import specs -from yaql.language import yaqltypes - -from murano.dsl import dsl - -NAME_TEMPLATE = u'applications.{0}' - -inject_format = specs.inject( - '_Logger__yaql_format_function', - yaqltypes.Delegate('format')) - - -@dsl.name('io.murano.system.Logger') -class Logger(object): - """Logger object for MuranoPL. - - Instance of this object returned by 'logger' YAQL function - and should not be instantiated directly - """ - - def __init__(self, logger_name): - self._underlying_logger = logging.getLogger( - NAME_TEMPLATE.format(logger_name)) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def trace(__self, __yaql_format_function, __message, *args, **kwargs): - __self._log(__self._underlying_logger.trace, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def debug(__self, __yaql_format_function, __message, *args, **kwargs): - __self._log(__self._underlying_logger.debug, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def info(__self, __yaql_format_function, __message, *args, **kwargs): - __self._log(__self._underlying_logger.info, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def warning(__self, __yaql_format_function, __message, *args, **kwargs): - __self._log(__self._underlying_logger.warning, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def error(__self, __yaql_format_function, __message, *args, **kwargs): - __self._log(__self._underlying_logger.error, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def critical(__self, __yaql_format_function, - __message, *args, **kwargs): - __self._log(__self._underlying_logger.critical, - __yaql_format_function, __message, args, kwargs) - - @specs.parameter('_Logger__message', yaqltypes.String()) - @inject_format - def exception(__self, __yaql_format_function, - __exc, __message, *args, **kwargs): - """Print error message and stacktrace""" - stack_trace_message = u'\n'.join([ - __self._format_without_exceptions( - __yaql_format_function, __message, args, kwargs), - __exc['stackTrace']().toString() - ]) - __self._underlying_logger.error(stack_trace_message) - - def _format_without_exceptions(self, format_function, - message, args, kwargs): - """Wrap YAQL function "format" to suppress exceptions - - Wrap YAQL function "format" to suppress exceptions - that may be raised when message cannot be formatted - due to invalid parameters provided - We do not want to break program workflow - even when formatting parameters are incorrect - """ - try: - message = format_function(message, *args, **kwargs) - except (IndexError, KeyError): - # NOTE(akhivin): we do not want break program workflow - # even formatting parameters are incorrect - self._underlying_logger.warning( - u'Can not format string: {0}'.format(message)) - return message - - def _log(self, log_function, yaql_format_function, message, args, kwargs): - log_function( - self._format_without_exceptions( - yaql_format_function, message, args, kwargs)) diff --git a/murano/engine/system/metadef_browser.py b/murano/engine/system/metadef_browser.py deleted file mode 100644 index 1234ef261..000000000 --- a/murano/engine/system/metadef_browser.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2016 Mirantis Inc. -# -# Licensed 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 glanceclient.v2.client as gclient -from oslo_config import cfg - -from murano.common import auth_utils -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import session_local_storage - - -CONF = cfg.CONF - - -@dsl.name('io.murano.system.MetadefBrowser') -class MetadefBrowser(object): - def __init__(self, this, region_name=None, cache=True): - session = helpers.get_execution_session() - self._project_id = session.project_id - self._region = this.find_owner('io.murano.CloudRegion') - self._region_name = region_name - self._cache = cache - self._namespaces = {} - self._objects = {} - - @staticmethod - @session_local_storage.execution_session_memoize - def _get_client(region_name): - return gclient.Client(**auth_utils.get_session_client_parameters( - service_type='image', region=region_name, conf='glance' - )) - - @property - def _client(self): - region = self._region_name or ( - None if self._region is None else self._region['name']) - return self._get_client(region) - - def get_namespaces(self, resource_type): - if not self._cache or resource_type not in self._namespaces: - nss = list(self._client.metadefs_namespace.list( - resource_type=resource_type)) - self._namespaces[resource_type] = nss - return nss - else: - return self._namespaces[resource_type] - - def get_objects(self, namespace): - if not self._cache or namespace not in self._objects: - objects = list(self._client.metadefs_object.list( - namespace=namespace)) - self._objects[namespace] = objects - return objects - else: - return self._objects[namespace] diff --git a/murano/engine/system/net_explorer.py b/murano/engine/system/net_explorer.py deleted file mode 100644 index 88ce6d9d0..000000000 --- a/murano/engine/system/net_explorer.py +++ /dev/null @@ -1,213 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 math - -import netaddr -from netaddr.strategy import ipv4 -import neutronclient.v2_0.client as nclient -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import netutils -from oslo_utils import uuidutils -import tenacity - -from murano.common import auth_utils -from murano.common import exceptions as exc -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import session_local_storage - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -@dsl.name('io.murano.system.NetworkExplorer') -class NetworkExplorer(object): - def __init__(self, this, region_name=None): - session = helpers.get_execution_session() - self._project_id = session.project_id - self._settings = CONF.networking - self._available_cidrs = self._generate_possible_cidrs() - self._region = this.find_owner('io.murano.CloudRegion') - self._region_name = region_name - - @staticmethod - @session_local_storage.execution_session_memoize - def _get_client(region_name): - return nclient.Client(**auth_utils.get_session_client_parameters( - service_type='network', region=region_name, conf='neutron' - )) - - @property - def _client(self): - region = self._region_name or ( - None if self._region is None else self._region['name']) - return self._get_client(region) - - # NOTE(starodubcevna): to avoid simultaneous router requests we use retry - # decorator with random delay 1-10 seconds between attempts and maximum - # delay time 30 seconds. - @tenacity.retry( - retry=tenacity.retry_if_exception_type(exc.RouterInfoException), - stop=tenacity.stop_after_delay(30), - wait=tenacity.wait_random(min=1, max=10), - reraise=True) - def get_default_router(self): - router_name = self._settings.router_name - - routers = self._client.list_routers( - tenant_id=self._project_id, name=router_name).get('routers') - if len(routers) == 0: - LOG.debug('Router {name} not found'.format(name=router_name)) - if self._settings.create_router: - LOG.debug('Attempting to create Router {router}'. - format(router=router_name)) - external_network = self._settings.external_network - kwargs = {'id': external_network} \ - if uuidutils.is_uuid_like(external_network) \ - else {'name': external_network} - networks = self._client.list_networks(**kwargs).get('networks') - ext_nets = list(filter(lambda n: n['router:external'], - networks)) - if len(ext_nets) == 0: - raise KeyError('Router %s could not be created, ' - 'no external network found' % router_name) - nid = ext_nets[0]['id'] - - body_data = { - 'router': { - 'name': router_name, - 'external_gateway_info': { - 'network_id': nid - }, - 'admin_state_up': True, - } - } - router = self._client.create_router( - body=body_data).get('router') - LOG.info('Created router: {id}'.format(id=router['id'])) - return router['id'] - else: - raise KeyError('Router %s was not found' % router_name) - else: - if routers[0]['external_gateway_info'] is None: - raise exc.RouterInfoException('Please set external gateway for' - ' the router %s ' % router_name) - router_id = routers[0]['id'] - return router_id - - def get_available_cidr(self, router_id, net_id, ip_version=4): - """Uses hash of network IDs to minimize the collisions - - Different nets will attempt to pick different cidrs out of available - range. - If the cidr is taken will pick another one. - """ - taken_cidrs = self._get_cidrs_taken_by_router(router_id) - id_hash = hash(net_id) - num_fails = 0 - available_ipv6_cidrs = [] - if ip_version == 6: - for cidr in self._available_cidrs: - available_ipv6_cidrs.append(cidr.ipv6()) - self._available_cidrs = available_ipv6_cidrs - while num_fails < len(self._available_cidrs): - cidr = self._available_cidrs[ - (id_hash + num_fails) % len(self._available_cidrs)] - if any(self._cidrs_overlap(cidr, taken_cidr) for taken_cidr in - taken_cidrs): - num_fails += 1 - else: - return str(cidr) - return None - - def get_default_dns(self, ip_version=4): - dns_list = self._settings.default_dns - valid_dns = [] - for ip in dns_list: - if ip_version == 6 and netutils.is_valid_ipv6(ip): - valid_dns.append(ip) - elif ip_version == 4 and netutils.is_valid_ipv4(ip): - valid_dns.append(ip) - else: - LOG.warning('{0} is not a vaild IPV{1} address, ' - 'ingore...'.format(ip, ip_version)) - return valid_dns - - def get_external_network_id_for_router(self, router_id): - router = self._client.show_router(router_id).get('router') - if not router or 'external_gateway_info' not in router: - return None - return router['external_gateway_info'].get('network_id') - - def get_external_network_id_for_network(self, network_id): - network = self._client.show_network(network_id).get('network') - if network.get('router:external', False): - return network_id - - # Get router interfaces of the network - router_ports = self._client.list_ports( - **{'device_owner': 'network:router_interface', - 'network_id': network_id}).get('ports') - - # For each router this network is connected to - # check if the router has external_gateway set - for router_port in router_ports: - ext_net_id = self.get_external_network_id_for_router( - router_port.get('device_id')) - if ext_net_id: - return ext_net_id - return None - - def _get_cidrs_taken_by_router(self, router_id): - if not router_id: - return [] - ports = self._client.list_ports(device_id=router_id)['ports'] - subnet_ids = [] - for port in ports: - for fixed_ip in port['fixed_ips']: - subnet_ids.append(fixed_ip['subnet_id']) - - all_subnets = self._client.list_subnets()['subnets'] - filtered_cidrs = [netaddr.IPNetwork(subnet['cidr']) for subnet in - all_subnets if subnet['id'] in subnet_ids] - - return filtered_cidrs - - @staticmethod - def _cidrs_overlap(cidr1, cidr2): - return (cidr1 in cidr2) or (cidr2 in cidr1) - - def _generate_possible_cidrs(self): - bits_for_envs = int( - math.ceil(math.log(self._settings.max_environments, 2))) - bits_for_hosts = int(math.ceil(math.log(self._settings.max_hosts, 2))) - width = ipv4.width - mask_width = width - bits_for_hosts - bits_for_envs - net = netaddr.IPNetwork( - '{0}/{1}'.format(self._settings.env_ip_template, mask_width)) - return list(net.subnet(width - bits_for_hosts)) - - def list_networks(self): - return self._client.list_networks()['networks'] - - def list_subnetworks(self): - return self._client.list_subnets()['subnets'] - - def list_ports(self): - return self._client.list_ports()['ports'] - - def list_neutron_extensions(self): - return self._client.list_extensions()['extensions'] diff --git a/murano/engine/system/project.py b/murano/engine/system/project.py deleted file mode 100644 index 46cd0bef9..000000000 --- a/murano/engine/system/project.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2016 Mirantis Inc. -# -# Licensed 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 murano.common import auth_utils -from murano.dsl import dsl -from murano.dsl import helpers - - -@dsl.name('io.murano.Project') -class Project(object): - @classmethod - def get_current(cls): - fields = auth_utils.get_project( - helpers.get_execution_session().project_id) - return cls._to_object(fields) - - @classmethod - def get_environment_owner(cls): - fields = auth_utils.get_project( - helpers.get_execution_session().environment_owner_project_id) - return cls._to_object(fields) - - @staticmethod - def _to_object(fields): - for field in ('links', 'parent_id', 'enabled'): - fields.pop(field, None) - obj_def = { - 'id': fields.pop('id'), - 'name': fields.pop('name'), - 'domain': fields.pop('domain_id', 'Default'), - 'description': fields.pop('description', None), - 'extra': fields - } - return dsl.new(obj_def) diff --git a/murano/engine/system/resource_manager.py b/murano/engine/system/resource_manager.py deleted file mode 100644 index e705dbc03..000000000 --- a/murano/engine/system/resource_manager.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 json as jsonlib - -import yaml as yamllib -from yaql.language import specs -from yaql.language import yaqltypes - -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import helpers - -if hasattr(yamllib, 'CSafeLoader'): - yaml_loader = yamllib.CSafeLoader -else: - yaml_loader = yamllib.SafeLoader - - -def _construct_yaml_str(self, node): - # Override the default string handling function - # to always return unicode objects - return self.construct_scalar(node) - - -yaml_loader.add_constructor(u'tag:yaml.org,2002:str', _construct_yaml_str) -# Unquoted dates like 2013-05-23 in yaml files get loaded as objects of type -# datetime.data which causes problems in API layer when being processed by -# oslo.serialization.jsonutils. Therefore, make unicode string out of -# timestamps until jsonutils can handle dates. -yaml_loader.add_constructor(u'tag:yaml.org,2002:timestamp', - _construct_yaml_str) - - -@dsl.name('io.murano.system.Resources') -class ResourceManager(object): - def __init__(self, context): - murano_class = helpers.get_type(helpers.get_caller_context(context)) - self._package = murano_class.package - - @staticmethod - @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) - @specs.inject('receiver', yaqltypes.Receiver()) - @specs.meta(constants.META_NO_TRACE, True) - def string(receiver, name, owner=None, binary=False): - path = ResourceManager._get_package(owner, receiver).get_resource(name) - mode = 'rb' if binary else 'rU' - with open(path, mode) as file: - return file.read() - - @classmethod - @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) - @specs.inject('receiver', yaqltypes.Receiver()) - @specs.meta(constants.META_NO_TRACE, True) - def json(cls, receiver, name, owner=None): - return jsonlib.loads(cls.string(receiver, name, owner)) - - @classmethod - @specs.parameter('owner', dsl.MuranoTypeParameter(nullable=True)) - @specs.inject('receiver', yaqltypes.Receiver()) - @specs.meta(constants.META_NO_TRACE, True) - def yaml(cls, receiver, name, owner=None): - # NOTE(kzaitsev, Sam Pilla) Bandit will raise an issue here, - # because it thinks that we're using an unsafe yaml.load. - # However we're passing a SafeLoader here - # (see definition of `yaml_loader` in this file; L27-30) - # so a `nosec` was added to ignore the false positive report. - return yamllib.load( # nosec - cls.string(receiver, name, owner), Loader=yaml_loader) - - @staticmethod - def _get_package(owner, receiver): - if owner is None: - if isinstance(receiver, dsl_types.MuranoObjectInterface): - return receiver.extension._package - murano_class = helpers.get_type(helpers.get_caller_context()) - else: - murano_class = owner.type - return murano_class.package diff --git a/murano/engine/system/status_reporter.py b/murano/engine/system/status_reporter.py deleted file mode 100644 index 92aa87fa3..000000000 --- a/murano/engine/system/status_reporter.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 datetime import datetime -import socket - -from oslo_config import cfg -from oslo_log import log as logging -import oslo_messaging as messaging - -from murano.common import rpc -from murano.common import uuidutils -from murano.dsl import dsl - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -@dsl.name('io.murano.system.StatusReporter') -class StatusReporter(object): - def __init__(self, environment): - if not rpc.initialized(): - rpc.init() - self._notifier = messaging.Notifier( - rpc.NOTIFICATION_TRANSPORT, - publisher_id=uuidutils.generate_uuid(), - topics=['murano']) - if isinstance(environment, str): - self._environment_id = environment - else: - self._environment_id = environment.id - - def _report(self, instance, msg, details=None, level='info'): - body = { - 'id': (self._environment_id if instance is None - else instance.id), - 'text': msg, - 'details': details, - 'level': level, - 'environment_id': self._environment_id, - 'timestamp': datetime.utcnow().isoformat() - } - self._notifier.info({}, 'murano.report_notification', body) - - def report(self, instance, msg): - self._report(instance, msg) - - def report_error_(self, instance, msg): - self._report(instance, msg, None, 'error') - - @dsl.name('report_error') - def report_error(self, instance, msg): - self._report(instance, msg, None, 'error') - - -class Notification(object): - def __init__(self): - if not CONF.stats.env_audit_enabled: - return - if not rpc.initialized(): - rpc.init() - self._notifier = messaging.Notifier( - rpc.NOTIFICATION_TRANSPORT, - publisher_id=('murano.%s' % socket.gethostname()), - driver='messaging') - - def _report(self, event_type, environment, level='info'): - if not CONF.stats.env_audit_enabled: - return - - if 'deleted' in environment: - deleted_at = environment['deleted'].isoformat() - else: - deleted_at = None - - body = { - 'id': environment['id'], - 'level': level, - 'environment_id': environment['id'], - 'tenant_id': environment['tenant_id'], - 'created_at': environment.get('created').isoformat(), - 'deleted_at': deleted_at, - 'launched_at': None, - 'timestamp': datetime.utcnow().isoformat() - } - - optional_fields = ("deployment_started", "deployment_finished") - for f in optional_fields: - body[f] = environment.get(f, None) - - LOG.debug("Sending out notification, type=%s, body=%s, level=%s", - event_type, body, level) - - self._notifier.info({}, 'murano.%s' % event_type, - body) - - def report(self, event_type, environment): - self._report(event_type, environment) - - def report_error(self, event_type, environment): - self._report(event_type, environment, 'error') - - -NOTIFIER = None - - -def get_notifier(): - global NOTIFIER - if not NOTIFIER: - NOTIFIER = Notification() - - return NOTIFIER diff --git a/murano/engine/system/system_objects.py b/murano/engine/system/system_objects.py deleted file mode 100644 index 0370c9e60..000000000 --- a/murano/engine/system/system_objects.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 murano.engine.system import agent -from murano.engine.system import agent_listener -from murano.engine.system import heat_stack -from murano.engine.system import instance_reporter -from murano.engine.system import logger -from murano.engine.system import metadef_browser -from murano.engine.system import net_explorer -from murano.engine.system import project -from murano.engine.system import resource_manager -from murano.engine.system import status_reporter -from murano.engine.system import test_fixture -from murano.engine.system import user -from murano.engine.system import workflowclient - - -def register(package): - package.register_class(agent.Agent) - package.register_class(agent_listener.AgentListener) - package.register_class(heat_stack.HeatStack) - package.register_class(resource_manager.ResourceManager) - package.register_class(instance_reporter.InstanceReportNotifier) - package.register_class(status_reporter.StatusReporter) - package.register_class(net_explorer.NetworkExplorer) - package.register_class(logger.Logger) - package.register_class(test_fixture.TestFixture) - package.register_class(workflowclient.MistralClient) - package.register_class(metadef_browser.MetadefBrowser) - package.register_class(user.User) - package.register_class(project.Project) diff --git a/murano/engine/system/test_fixture.py b/murano/engine/system/test_fixture.py deleted file mode 100644 index 2e7d7c40f..000000000 --- a/murano/engine/system/test_fixture.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# -# Licensed 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 testtools - -from murano.dsl import dsl -from murano.dsl import helpers - - -@dsl.name('io.murano.test.TestFixture') -class TestFixture(object): - def __init__(self): - self._test_case = testtools.TestCase('__init__') - - def load(self, model): - exc = helpers.get_executor() - return exc.load(model) - - def finish_env(self): - session = helpers.get_execution_session() - session.finish() - - def start_env(self): - session = helpers.get_execution_session() - session.start() - - def assert_equal(self, expected, observed, message=None): - self._test_case.assertEqual(expected, observed, message) - - def assert_true(self, expr, message=None): - self._test_case.assertTrue(expr, message) - - def assert_false(self, expr, message=None): - self._test_case.assertFalse(expr, message) - - def assert_in(self, needle, haystack, message=None): - self._test_case.assertIn(needle, haystack, message) - - def assert_not_in(self, needle, haystack, message=None): - self._test_case.assertNotIn(needle, haystack, message) diff --git a/murano/engine/system/user.py b/murano/engine/system/user.py deleted file mode 100644 index 823bb2db0..000000000 --- a/murano/engine/system/user.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2016 Mirantis Inc. -# -# Licensed 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 murano.common import auth_utils -from murano.dsl import dsl -from murano.dsl import helpers - - -@dsl.name('io.murano.User') -class User(object): - @classmethod - def get_current(cls): - fields = auth_utils.get_user(helpers.get_execution_session().user_id) - return cls._to_object(fields) - - @classmethod - def get_environment_owner(cls): - fields = auth_utils.get_user( - helpers.get_execution_session().environment_owner_user_id) - return cls._to_object(fields) - - @staticmethod - def _to_object(fields): - fields = dict(fields) - for field in ('links', 'enabled', 'default_project_id'): - fields.pop(field, None) - obj_def = { - 'id': fields.pop('id'), - 'name': fields.pop('name'), - 'domain': fields.pop('domain_id', 'Default'), - 'email': fields.pop('email', None), - 'extra': fields - } - return dsl.new(obj_def) diff --git a/murano/engine/system/workflowclient.py b/murano/engine/system/workflowclient.py deleted file mode 100644 index bc9f4fbc5..000000000 --- a/murano/engine/system/workflowclient.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# -# Licensed 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 json - -import eventlet -try: - from mistralclient.api import client as mistralcli -except ImportError: - mistralcli = None -from oslo_config import cfg -from oslo_log import log as logging - -from murano.common import auth_utils -from murano.dsl import dsl -from murano.dsl import session_local_storage - -CONF = cfg.CONF -LOG = logging.getLogger(__name__) - - -class MistralError(Exception): - pass - - -@dsl.name('io.murano.system.MistralClient') -class MistralClient(object): - def __init__(self, this, region_name=None): - self._owner = this.find_owner('io.murano.Environment') - self._region_name = region_name - - @property - def _client(self): - region = self._region_name or ( - None if self._owner is None else self._owner['region']) - return self._create_client(region) - - @staticmethod - @session_local_storage.execution_session_memoize - def _create_client(region): - if not mistralcli: - LOG.warning("Mistral client is not available") - raise ImportError("Import mistralcliet error") - - mistral_settings = CONF.mistral - - endpoint_type = mistral_settings.endpoint_type - service_type = mistral_settings.service_type - session = auth_utils.get_client_session() - - mistral_url = mistral_settings.url or session.get_endpoint( - service_type=service_type, - endpoint_type=endpoint_type, - region_name=region) - auth_ref = session.auth.get_access(session) - - # TODO(gyurco): use auth_utils.get_session_client_parameters - return mistralcli.client( - mistral_url=mistral_url, - project_id=auth_ref.project_id, - endpoint_type=endpoint_type, - service_type=service_type, - auth_token=auth_ref.auth_token, - user_id=auth_ref.user_id, - insecure=mistral_settings.insecure, - cacert=mistral_settings.cafile - ) - - def upload(self, definition): - self._client.workflows.create(definition) - - def run(self, name, timeout=600, inputs=None, params=None): - execution = self._client.executions.create( - workflow_identifier=name, workflow_input=inputs, params=params) - # For the fire and forget functionality - when we do not want to wait - # for the result of the run. - if timeout == 0: - return execution.id - - state = execution.state - try: - # While the workflow is running we continue to wait until timeout. - with eventlet.timeout.Timeout(timeout): - while state not in ('ERROR', 'SUCCESS'): - eventlet.sleep(2) - execution = self._client.executions.get(execution.id) - state = execution.state - except eventlet.timeout.Timeout: - error_message = ( - 'Mistral run timed out. Execution id: {0}.').format( - execution.id) - raise MistralError(error_message) - - if state == 'ERROR': - error_message = ('Mistral execution completed with ERROR.' - ' Execution id: {0}. Output: {1}').format( - execution.id, execution.output) - raise MistralError(error_message) - - # Load the JSON we got from Mistral client to dictionary. - output = json.loads(execution.output) - - # Clean the returned dictionary from unnecessary data. - # We want to keep only flow level outputs. - output.pop('openstack', None) - output.pop('__execution', None) - output.pop('task', None) - - return output diff --git a/murano/engine/system/yaql_functions.py b/murano/engine/system/yaql_functions.py deleted file mode 100644 index 173a36b0f..000000000 --- a/murano/engine/system/yaql_functions.py +++ /dev/null @@ -1,264 +0,0 @@ -# Copyright (c) 2013 Mirantis Inc. -# -# Licensed 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 collections import abc -import random -import re -import string -import time - -import jsonpatch -import jsonpointer -from oslo_config import cfg as oslo_cfg -from oslo_log import log as logging -from oslo_serialization import base64 -from yaql.language import specs -from yaql.language import utils -from yaql.language import yaqltypes - -from murano.common import config as cfg -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import yaql_integration - -from castellan.common import exception as castellan_exception -from castellan.common import utils as castellan_utils -from castellan import key_manager -from castellan import options - -LOG = logging.getLogger(__name__) - -_random_string_counter = None - - -@specs.parameter('value', yaqltypes.String()) -@specs.extension_method -def base64encode(value): - return base64.encode_as_text(value) - - -@specs.parameter('value', yaqltypes.String()) -@specs.extension_method -def base64decode(value): - return base64.decode_as_text(value) - - -@specs.parameter('collection', yaqltypes.Iterable()) -@specs.parameter('composer', yaqltypes.Lambda()) -@specs.extension_method -def pselect(collection, composer): - return helpers.parallel_select(collection, composer) - - -@specs.parameter('mappings', abc.Mapping) -@specs.extension_method -def bind(obj, mappings): - if isinstance(obj, str) and obj.startswith('$'): - value = _convert_macro_parameter(obj[1:], mappings) - if value is not None: - return value - elif utils.is_sequence(obj): - return [bind(t, mappings) for t in obj] - elif isinstance(obj, abc.Mapping): - result = {} - for key, value in obj.items(): - result[bind(key, mappings)] = bind(value, mappings) - return result - elif isinstance(obj, str) and obj.startswith('$'): - value = _convert_macro_parameter(obj[1:], mappings) - if value is not None: - return value - return obj - - -def _convert_macro_parameter(macro, mappings): - replaced = [False] - - def replace(match): - replaced[0] = True - return str(mappings.get(match.group(1))) - - result = re.sub('{(\\w+?)}', replace, macro) - if replaced[0]: - return result - else: - return mappings[macro] - - -@specs.parameter('group', yaqltypes.String()) -@specs.parameter('setting', yaqltypes.String()) -@specs.parameter('read_as_file', bool) -def config(group, setting, read_as_file=False): - config_value = cfg.CONF[group][setting] - if read_as_file: - with open(config_value) as target_file: - return target_file.read() - else: - return config_value - - -@specs.parameter('setting', yaqltypes.String()) -@specs.name('config') -def config_default(setting): - return cfg.CONF[setting] - - -@specs.parameter('string', yaqltypes.String()) -@specs.parameter('start', int) -@specs.parameter('length', int) -@specs.inject('delegate', yaqltypes.Delegate('substring', method=True)) -@specs.extension_method -def substr(delegate, string, start, length=-1): - return delegate(string, start, length) - - -@specs.extension_method -def patch_(engine, obj, patch): - if not isinstance(patch, tuple): - patch = (patch,) - patch = dsl.to_mutable(patch, engine) - patch = jsonpatch.JsonPatch(patch) - try: - obj = dsl.to_mutable(obj, engine) - return patch.apply(obj, in_place=True) - except jsonpointer.JsonPointerException: - return obj - - -def _int2base(x, base): - """Converts decimal integers into another number base - - Converts decimal integers into another number base - from base-2 to base-36. - - :param x: decimal integer - :param base: number base, max value is 36 - :return: integer converted to the specified base - """ - digs = string.digits + string.ascii_lowercase - if x < 0: - sign = -1 - elif x == 0: - return '0' - else: - sign = 1 - x *= sign - digits = [] - while x: - digits.append(digs[x % base]) - x //= base - if sign < 0: - digits.append('-') - digits.reverse() - return ''.join(digits) - - -def random_name(): - """Replace '#' char in pattern with supplied number - - Replace '#' char in pattern with supplied number. If no pattern is - supplied, generate a short and unique name for the host. - - :param pattern: hostname pattern - :param number: number to replace with in pattern - :return: hostname - """ - global _random_string_counter - - counter = _random_string_counter or 1 - # generate first 5 random chars - prefix = ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) - # convert timestamp to higher base to shorten hostname string - # (up to 8 chars) - timestamp = _int2base(int(time.time() * 1000), 36)[:8] - # third part of random name up to 2 chars - # (1295 is last 2-digit number in base-36, 1296 is first 3-digit number) - suffix = _int2base(counter, 36) - _random_string_counter = (counter + 1) % 1296 - return prefix + timestamp + suffix - - -@specs.parameter('collection', yaqltypes.Iterable()) -@specs.parameter('default', nullable=True) -@specs.extension_method -def first_or_default(collection, default=None): - try: - return next(iter(collection)) - except StopIteration: - return default - - -@specs.parameter('logger_name', yaqltypes.String(True)) -def logger(context, logger_name): - """Instantiate Logger""" - log = yaql_integration.call_func( - context, 'new', 'io.murano.system.Logger', - logger_name=logger_name) - return log - - -@specs.parameter('value', yaqltypes.String()) -@specs.extension_method -def decrypt_data(value): - options.set_defaults(oslo_cfg.CONF, - barbican_endpoint_type='internal') - manager = key_manager.API() - try: - context = castellan_utils.credential_factory(conf=cfg.CONF) - except castellan_exception.AuthTypeInvalidError as e: - LOG.exception(e) - LOG.error("Castellan must be correctly configured in order to use " - "decryptData()") - raise - try: - data = manager.get(context, value).get_encoded() - except castellan_exception.KeyManagerError as e: - LOG.exception(e) - raise - return data - - -@helpers.memoize -def get_context(runtime_version): - context = yaql_integration.create_empty_context() - context.register_function(base64decode) - context.register_function(base64encode) - context.register_function(pselect) - context.register_function(bind) - context.register_function(random_name) - context.register_function(patch_) - context.register_function(logger) - context.register_function(decrypt_data, 'decryptData') - - if runtime_version <= constants.RUNTIME_VERSION_1_1: - context.register_function(substr) - context.register_function(first_or_default) - - root_context = yaql_integration.create_context(runtime_version) - for t in ('to_lower', 'to_upper', 'trim', 'join', 'split', - 'starts_with', 'ends_with', 'matches', 'replace', - 'flatten'): - for spec in utils.to_extension_method(t, root_context): - context.register_function(spec) - return context - - -@helpers.memoize -def get_restricted_context(): - context = yaql_integration.create_empty_context() - context.register_function(config) - context.register_function(config_default) - return context diff --git a/murano/engine/yaql_yaml_loader.py b/murano/engine/yaql_yaml_loader.py deleted file mode 100644 index 7678fe224..000000000 --- a/murano/engine/yaql_yaml_loader.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 yaml -import yaml.composer -import yaml.constructor - -from murano.dsl import dsl_types -from murano.dsl import helpers -from murano.dsl import yaql_expression - - -@helpers.memoize -def get_loader(version): - version = helpers.parse_version(version) - - class MuranoPlDict(dict): - pass - - class YaqlExpression(yaql_expression.YaqlExpression): - @staticmethod - def match(expr): - return yaql_expression.YaqlExpression.is_expression(expr, version) - - def load(contents, file_id): - def build_position(node): - return dsl_types.ExpressionFilePosition( - file_id, - node.start_mark.line + 1, - node.start_mark.column + 1, - node.end_mark.line + 1, - node.end_mark.column + 1) - - class MuranoPlYamlConstructor(yaml.constructor.SafeConstructor): - def construct_yaml_map(self, node): - data = MuranoPlDict() - data.source_file_position = build_position(node) - yield data - value = self.construct_mapping(node) - data.update(value) - - class YaqlYamlLoader(yaml.SafeLoader, MuranoPlYamlConstructor): - pass - - YaqlYamlLoader.add_constructor( - u'tag:yaml.org,2002:map', - MuranoPlYamlConstructor.construct_yaml_map) - - # workaround for PyYAML bug: http://pyyaml.org/ticket/221 - resolvers = {} - for k, v in yaml.SafeLoader.yaml_implicit_resolvers.items(): - resolvers[k] = v[:] - YaqlYamlLoader.yaml_implicit_resolvers = resolvers - - def yaql_constructor(loader, node): - value = loader.construct_scalar(node) - result = yaql_expression.YaqlExpression(value, version) - result.source_file_position = build_position(node) - return result - - YaqlYamlLoader.add_constructor(u'!yaql', yaql_constructor) - YaqlYamlLoader.add_implicit_resolver(u'!yaql', YaqlExpression, None) - return list(filter( - lambda t: t, - yaml.load_all(contents, Loader=YaqlYamlLoader)) - ) - - return load diff --git a/murano/hacking/__init__.py b/murano/hacking/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/hacking/checks.py b/murano/hacking/checks.py deleted file mode 100644 index dea11d718..000000000 --- a/murano/hacking/checks.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (c) 2015 Intel, Inc. -# All Rights Reserved. -# -# Licensed 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. - - -""" -Guidelines for writing new hacking checks - - Use only for Murano specific tests. OpenStack general tests - should be submitted to the common 'hacking' module. - - Pick numbers in the range M3xx. Find the current test with - the highest allocated number and then pick the next value. - If nova has an N3xx code for that test, use the same number. - - Keep the test method code in the source file ordered based - on the M3xx value. - - List the new rule in the top level HACKING.rst file - - Add test cases for each new rule to /tests/unit/test_hacking.py -""" - -import re - -from hacking import core - -mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])") -assert_equal_end_with_none_re = re.compile( - r"(.)*assertEqual\((\w|\.|\'|\"|\[|\])+, None\)") -assert_equal_start_with_none_re = re.compile( - r"(.)*assertEqual\(None, (\w|\.|\'|\"|\[|\])+\)") - - -@core.flake8ext -def assert_equal_none(logical_line): - """Check for assertEqual(A, None) or assertEqual(None, A) sentences - - M318 - """ - msg = ("M318: assertEqual(A, None) or assertEqual(None, A) " - "sentences not allowed") - res = (assert_equal_start_with_none_re.match(logical_line) or - assert_equal_end_with_none_re.match(logical_line)) - if res: - yield (0, msg) - - -@core.flake8ext -def no_mutable_default_args(logical_line): - msg = "M322: Method's default argument shouldn't be mutable!" - if mutable_default_args.match(logical_line): - yield (0, msg) - - -@core.flake8ext -def check_no_basestring(logical_line): - if re.search(r"\bbasestring\b", logical_line): - msg = ("M326: basestring is not Python3 usage, use " - "str instead.") - yield(0, msg) diff --git a/murano/httpd/__init__.py b/murano/httpd/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/httpd/murano_api.py b/murano/httpd/murano_api.py deleted file mode 100644 index e4aae17af..000000000 --- a/murano/httpd/murano_api.py +++ /dev/null @@ -1,52 +0,0 @@ -# Licensed 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. - -"""WSGI script for murano-api. - -Script for running murano-api under Apache2. -""" - - -from oslo_config import cfg -import oslo_i18n as i18n -from oslo_log import log as logging - -from murano.api.v1 import request_statistics -from murano.common import app_loader -from murano.common import config -from murano.common.i18n import _ -from murano.common import policy -from murano.common import server - - -def init_application(): - i18n.enable_lazy() - - LOG = logging.getLogger('murano.api') - - logging.register_options(cfg.CONF) - # NOTE(hberaud): Call reset to ensure the ConfigOpts object doesn't - # already contain registered options if the app is reloaded. - cfg.CONF.reset() - cfg.CONF(project='murano') - logging.setup(cfg.CONF, 'murano') - config.set_middleware_defaults() - request_statistics.init_stats() - policy.init() - server.get_notification_listener().start() - server.get_rpc_server().start() - - port = cfg.CONF.bind_port - host = cfg.CONF.bind_host - LOG.info(_('Starting Murano REST API on %(host)s:%(port)s'), - {'host': host, 'port': port}) - return app_loader.load_paste_app('murano') diff --git a/murano/locale/en_GB/LC_MESSAGES/murano.po b/murano/locale/en_GB/LC_MESSAGES/murano.po deleted file mode 100644 index e4168b44a..000000000 --- a/murano/locale/en_GB/LC_MESSAGES/murano.po +++ /dev/null @@ -1,467 +0,0 @@ -# OpenStack Infra , 2015. #zanata -# Andi Chandler , 2017. #zanata -# Andi Chandler , 2018. #zanata -# Andi Chandler , 2019. #zanata -# Andi Chandler , 2022. #zanata -msgid "" -msgstr "" -"Project-Id-Version: murano VERSION\n" -"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2022-04-20 01:19+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2022-06-10 09:22+0000\n" -"Last-Translator: Andi Chandler \n" -"Language-Team: English (United Kingdom)\n" -"Language: en_GB\n" -"X-Generator: Zanata 4.3.3\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" - -msgid "" -"'multipart/form-data' request body should contain 1 or 2 parts: json string " -"and zip archive. Current body consists of {amount} part(s)" -msgstr "" -"'multipart/form-data' request body should contain 1 or 2 parts: json string " -"and zip archive. Current body consists of {amount} part(s)" - -msgid "Acceptable response can not be provided" -msgstr "Acceptable response cannot be provided" - -msgid "Attribute '{0}' is invalid" -msgstr "Attribute '{0}' is invalid" - -msgid "Authentication URL" -msgstr "Authentication URL" - -msgid "Authorization required" -msgstr "Authorisation required" - -msgid "Bad value passed to filter. Got {key}, expected:{valid}" -msgstr "Bad value passed to filter. Got {key}, expected:{valid}" - -msgid "Category '{name}' doesn't exist" -msgstr "Category '{name}' doesn't exist" - -msgid "Category id '{id}' not found" -msgstr "Category id '{id}' not found" - -msgid "Category name should be 80 characters maximum" -msgstr "Category name should be 80 characters maximum" - -msgid "Category with specified name is already exist" -msgstr "Category with specified name is already exist" - -msgid "Class name and method name must be specified for static action" -msgstr "Class name and method name must be specified for static action" - -msgid "" -"Class with the same full name is already registered in the visibility scope" -msgstr "" -"Class with the same full name is already registered in the visibility scope" - -msgid "Content-Type must be '{type}'" -msgstr "Content-Type must be '{type}'" - -#, python-format -msgid "" -"Could not bind to %(host)s:%(port)s after trying for 30 seconds: Address " -"already in use." -msgstr "" -"Could not bind to %(host)s:%(port)s after trying for 30 seconds: Address " -"already in use." - -msgid "" -"Could not open session for environment , environment has " -"deploying or deleting status." -msgstr "" -"Could not open session for environment , environment has " -"deploying or deleting status." - -msgid "Couldn't load package from file: {reason}" -msgstr "Couldn't load package from file: {reason}" - -msgid "Create resources using trust token rather than user's token" -msgstr "Create resources using trust token rather than user's token" - -msgid "Disallow the use of murano-agent" -msgstr "Disallow the use of murano-agent" - -msgid "Domain name of the project" -msgstr "Domain name of the project" - -msgid "Domain name of the user" -msgstr "Domain name of the user" - -msgid "Enable model policy enforcer using Congress" -msgstr "Enable model policy enforcer using Congress" - -msgid "" -"Enables murano-engine to persist on disk packages downloaded during " -"deployments. The packages would be re-used for consequent deployments." -msgstr "" -"Enables murano-engine to persist on disk packages downloaded during " -"deployments. The packages would be re-used for consequent deployments." - -msgid "Env Template with specified name already exists" -msgstr "Env Template with specified name already exists" - -msgid "Env template with specified name already exists" -msgstr "Env template with specified name already exists" - -msgid "EnvTemplate is not found" -msgstr "EnvTemplate is not found" - -msgid "EnvTemplate body is incorrect" -msgstr "EnvTemplate body is incorrect" - -msgid "Environment is not found" -msgstr "Environment is not found" - -msgid "Environment Template is not found" -msgstr "Environment Template is not found" - -msgid "Environment Template is not found" -msgstr "Environment Template is not found" - -msgid "Environment Template must contain at least one non-white space symbol" -msgstr "Environment Template must contain at least one non-white space symbol" - -msgid "Environment Template with id {id} not found" -msgstr "Environment Template with id {id} not found" - -msgid "Environment audit interval in minutes. Default value is 60 minutes." -msgstr "Environment audit interval in minutes. Default value is 60 minutes." - -msgid "Environment name must contain at least one non-white space symbol" -msgstr "Environment name must contain at least one non-white space symbol" - -msgid "Environment name should be 255 characters maximum" -msgstr "Environment name should be 255 characters maximum" - -msgid "Environment template name should be 255 characters maximum" -msgstr "Environment template name should be 255 characters maximum" - -msgid "Environment template specified name already exists" -msgstr "Environment template specified name already exists" - -msgid "Environment with id {env_id} not found" -msgstr "Environment with id {env_id} not found" - -msgid "Environment with specified name already exists" -msgstr "Environment with specified name already exists" - -msgid "Host for service broker" -msgstr "Host for service broker" - -#, python-format -msgid "Invalid SSL version: %s" -msgstr "Invalid SSL version: %s" - -#, python-format -msgid "Invalid filter value %s. The quote is not closed." -msgstr "Invalid filter value %s. The quote is not closed." - -#, python-format -msgid "" -"Invalid filter value %s. There is no comma after opening quotation mark." -msgstr "" -"Invalid filter value %s. There is no comma after opening quotation mark." - -#, python-format -msgid "" -"Invalid filter value %s. There is no comma before opening quotation mark." -msgstr "" -"Invalid filter value %s. There is no comma before opening quotation mark." - -msgid "Invalid sort direction: {0}" -msgstr "Invalid sort direction: {0}" - -msgid "Invalid sort key: {sort_key}. Must be one of the following: {available}" -msgstr "" -"Invalid sort key: {sort_key}. Must be one of the following: {available}" - -msgid "" -"It's impossible to delete categories assigned to the package, uploaded to " -"the catalog" -msgstr "" -"It's impossible to delete categories assigned to the package, uploaded to " -"the catalogue" - -msgid "JSON-patch must be a list." -msgstr "JSON-patch must be a list." - -msgid "Limit param must be an integer" -msgstr "Limit param must be an integer" - -msgid "Limit param must be positive" -msgstr "Limit param must be positive" - -msgid "" -"List of directories to load local packages from. If not provided, packages " -"will be loaded only API" -msgstr "" -"List of directories to load local packages from. If not provided, packages " -"will be loaded only API" - -msgid "" -"Local package is not found since \"load-packages-from\" engine parameter is " -"not provided and specified packages is not loaded to murano-api" -msgstr "" -"Local package is not found since \"load-packages-from\" engine parameter is " -"not provided and specified packages is not loaded to murano-api" - -msgid "Malformed request body" -msgstr "Malformed request body" - -msgid "Maximum number of elements that can be iterated per object type." -msgstr "Maximum number of elements that can be iterated per object type." - -msgid "" -"Method '{method}' is not allowed for a path with name '{name}'. Allowed " -"operations are: {ops}" -msgstr "" -"Method '{method}' is not allowed for a path with name '{name}'. Allowed " -"operations are: {ops}" - -msgid "Murano object model validation failed: {0}" -msgstr "Murano object model validation failed: {0}" - -msgid "Nested paths are not allowed" -msgstr "Nested paths are not allowed" - -msgid "No tests found for execution." -msgstr "No tests found for execution." - -msgid "Number of API workers" -msgstr "Number of API workers" - -msgid "Number of engine workers" -msgstr "Number of engine workers" - -#, python-format -msgid "Operation \"%s\" requires a member named \"value\"." -msgstr "Operation \"%s\" requires a member named \"value\"." - -msgid "Operations must be JSON objects." -msgstr "Operations must be JSON objects." - -msgid "Package '{pkg_id}' is not owned by tenant '{tenant}'" -msgstr "Package '{pkg_id}' is not owned by tenant '{tenant}'" - -msgid "Package '{pkg_id}' is not public and not owned by tenant '{tenant}' " -msgstr "Package '{pkg_id}' is not public and not owned by tenant '{tenant}' " - -msgid "Package id '{pkg_id}' not found" -msgstr "Package id '{pkg_id}' not found" - -msgid "Package name should be 80 characters maximum" -msgstr "Package name should be 80 characters maximum" - -msgid "Package schema is not valid: {reason}" -msgstr "Package schema is not valid: {reason}" - -msgid "Package service which should be used by service broker" -msgstr "Package service which should be used by service broker" - -msgid "Package with specified full name is already registered" -msgstr "Package with specified full name is already registered" - -msgid "Package with the same Name is already made public" -msgstr "Package with the same Name is already made public" - -msgid "Path to RSA key for agent message signing" -msgstr "Path to RSA key for agent message signing" - -msgid "Path to class configuration files" -msgstr "Path to class configuration files" - -msgid "Please, specify a name of the environment template." -msgstr "Please, specify a name of the environment template." - -msgid "Please, specify a name of the environment to create" -msgstr "Please, specify a name of the environment to create" - -#, python-format -msgid "" -"Pointer `%s` contains \"~\", which is not part of a recognized escape " -"sequence." -msgstr "" -"Pointer `%s` contains \"~\", which is not part of a recognised escape " -"sequence." - -#, python-format -msgid "Pointer `%s` contains adjacent \"/\"." -msgstr "Pointer `%s` contains adjacent \"/\"." - -#, python-format -msgid "Pointer `%s` does not contain a valid token." -msgstr "Pointer `%s` does not contain a valid token." - -#, python-format -msgid "Pointer `%s` does not start with \"/\"." -msgstr "Pointer `%s` does not start with \"/\"." - -#, python-format -msgid "Pointer `%s` ends with \"/\"." -msgstr "Pointer `%s` ends with \"/\"." - -msgid "Policy File JSON to YAML Migration" -msgstr "Policy File JSON to YAML Migration" - -msgid "Port for service broker" -msgstr "Port for service broker" - -msgid "Project for service broker" -msgstr "Project for service broker" - -msgid "Request body is empty: please, provide application object model" -msgstr "Request body is empty: please, provide application object model" - -msgid "Request body is empty: please, provide environment object model patch" -msgstr "Request body is empty: please, provide environment object model patch" - -msgid "Request body must be a JSON array of operation objects." -msgstr "Request body must be a JSON array of operation objects." - -msgid "Role used to identify an authenticated user as administrator." -msgstr "Role used to identify an authenticated user as administrator." - -msgid "Session is already in deployment state" -msgstr "Session is already in deployment state" - -msgid "" -"Session is invalid: environment has been updated or updating " -"right now with other session" -msgstr "" -"Session is invalid: environment has been updated or updating " -"right now with other session" - -msgid "Session is not found" -msgstr "Session is not found" - -msgid "Session is not found" -msgstr "Session is not found" - -msgid "" -"Session is already deployed or deployment is in progress" -msgstr "" -"Session is already deployed or deployment is in progress" - -msgid "" -"Session is not tied with Environment " -msgstr "" -"Session is not tied with Environment " - -msgid "" -"Session is in deploying state and could not be deleted" -msgstr "" -"Session is in deploying state and could not be deleted" - -msgid "Source object or path is malformed" -msgstr "Source object or path is malformed" - -msgid "" -"Specified package is not found: {0} were scanned together with murano " -"database" -msgstr "" -"Specified package is not found: {0} were scanned together with Murano " -"database" - -#, python-format -msgid "Starting Murano REST API on %(host)s:%(port)s" -msgstr "Starting Murano REST API on %(host)s:%(port)s" - -msgid "Statistics collection interval in minutes.Default value is 5 minutes." -msgstr "Statistics collection interval in minutes. Default value is 5 minutes." - -msgid "The environment template {templ_id} does not exist" -msgstr "The environment template {templ_id} does not exist" - -msgid "" -"The service to store murano packages: murano (stands for legacy behavior " -"using murano-api) or glance (stands for glance-glare artifact service)" -msgstr "" -"The service to store Murano packages: Murano (stands for legacy behaviour " -"using murano-api) or Glance (stands for glance-glare artefact service)" - -msgid "The template does not exist {templ_id}" -msgstr "The template does not exist {templ_id}" - -msgid "There is no file package with application description" -msgstr "There is no file package with application description" - -msgid "Time for waiting for a response from murano agent during the deployment" -msgstr "" -"Time for waiting for a response from murano agent during the deployment" - -#, python-format -msgid "Unable to find '%s' in JSON Schema change" -msgstr "Unable to find '%s' in JSON Schema change" - -#, python-format -msgid "" -"Unable to load %(app_name)s from configuration file %(conf_file)s. \n" -"Got: %(e)r" -msgstr "" -"Unable to load %(app_name)s from configuration file %(conf_file)s. \n" -"Got: %(e)r" - -#, python-format -msgid "Unable to locate paste config file for %s." -msgstr "Unable to locate paste config file for %s." - -msgid "Unsupported Content-Type" -msgstr "Unsupported Content-Type" - -msgid "Uploading file can't be empty" -msgstr "Uploading file can't be empty" - -msgid "Uploading file is too large. The limit is {0} Mb" -msgstr "Uploading file is too large. The limit is {0} Mb" - -msgid "" -"User is not authorized to access session ." -msgstr "" -"User is not authorised to access session ." - -msgid "User has no access to these resources." -msgstr "User has no access to these resources." - -msgid "User is not authorized to access these tenant resources" -msgstr "User is not authorised to access these tenant resources" - -msgid "User is not authorized to access this tenant resources" -msgstr "User is not authorised to access this tenant resources" - -msgid "Value '{value}' of property '{path}' does not exist." -msgstr "Value '{value}' of property '{path}' does not exist." - -msgid "Whether environment audit events enabled" -msgstr "Whether environment audit events enabled" - -msgid "X-Configuration-Session header which indicates to the session is missed" -msgstr "" -"X-Configuration-Session header which indicates to the session is missed" - -msgid "Your credentials are wrong. Please try again" -msgstr "Your credentials are wrong. Please try again" - -msgid "cannot understand JSON" -msgstr "cannot understand JSON" - -msgid "cannot understand XML" -msgstr "cannot understand XML" - -msgid "pip URL/package spec for murano-agent" -msgstr "pip URL/package spec for murano-agent" - -msgid "{0}: Unsupported Format. Only {1} allowed" -msgstr "{0}: Unsupported Format. Only {1} allowed" - -msgid "{0}: Uploaded image size {1} is too large. Max allowed size is {2}" -msgstr "{0}: Uploaded image size {1} is too large. Max allowed size is {2}" diff --git a/murano/locale/ru/LC_MESSAGES/murano.po b/murano/locale/ru/LC_MESSAGES/murano.po deleted file mode 100644 index 377437d5d..000000000 --- a/murano/locale/ru/LC_MESSAGES/murano.po +++ /dev/null @@ -1,321 +0,0 @@ -# OpenStack Infra , 2015. #zanata -# Andreas Jaeger , 2016. #zanata -msgid "" -msgstr "" -"Project-Id-Version: murano 3.2.1.dev28\n" -"Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" -"POT-Creation-Date: 2017-03-22 16:25+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2016-04-12 05:46+0000\n" -"Last-Translator: Copied by Zanata \n" -"Language-Team: Russian\n" -"Language: ru\n" -"X-Generator: Zanata 3.9.6\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" - -msgid "" -"'multipart/form-data' request body should contain 1 or 2 parts: json string " -"and zip archive. Current body consists of {amount} part(s)" -msgstr "" -"'multipart/form-data' тело запроса должно содержать 1 или 2 части: строка " -"json и zip архив. Настоящее тело запроса состоит из {amount} частей" - -msgid "Attribute '{0}' is invalid" -msgstr "Неверный атрибут '{0}'" - -msgid "Authentication URL" -msgstr "URL аутентификации" - -msgid "Authorization required" -msgstr "Требуется авторизация" - -msgid "Category '{name}' doesn't exist" -msgstr "Категория '{name}' не существует" - -msgid "Category id '{id}' not found" -msgstr "Категория с id '{id}' не найдена" - -msgid "Category name should be 80 characters maximum" -msgstr "Имя категории не должно превышать 80 символов" - -msgid "Category with specified name is already exist" -msgstr "Категория с указанным именем уже существует" - -msgid "" -"Class with the same full name is already registered in the visibility scope" -msgstr "" -"Класс с таким полным именем уже зарегистрирован в текущей области видимости" - -msgid "Content-Type must be '{type}'" -msgstr "Content-Type должен быть '{type}'" - -#, python-format -msgid "" -"Could not bind to %(host)s:%(port)s after trying for 30 seconds: Address " -"already in use." -msgstr "" -"Не удалось связать с %(host)s:%(port)s в течении 30 секунд: адрес уже " -"используется" - -msgid "Couldn't load package from file: {reason}" -msgstr "Не удалось загрузить пакет из файла: {reason}" - -msgid "Create resources using trust token rather than user's token" -msgstr "Создание ресурсов используя trust токен, а не токен пользователя" - -msgid "Disallow the use of murano-agent" -msgstr "Запретить использование мурано агента" - -msgid "Enable model policy enforcer using Congress" -msgstr "Включите enforcer политик, используя Congress" - -msgid "Env Template with specified name already exists" -msgstr "Шаблон окружения с указанным именем уже существует" - -msgid "EnvTemplate is not found" -msgstr "Шаблон окружения не найден" - -msgid "EnvTemplate body is incorrect" -msgstr "Недопустимое тело шаблона окружения" - -msgid "Environment is not found" -msgstr "Окружение не найдено" - -msgid "Environment Template is not found" -msgstr "Шаблон окружения не найден" - -msgid "Environment Template is not found" -msgstr "Шаблон окружения не найден" - -msgid "Environment Template must contain at least one non-white space symbol" -msgstr "Шаблон окружения должен содержать хотя бы один не-пробельный символ" - -msgid "Environment Template with id {id} not found" -msgstr "Шаблон окружения с id {id} не найден" - -msgid "Environment name must contain at least one non-white space symbol" -msgstr "Имя окружения должно содержать хотя бы один не-пробельный символ" - -msgid "Environment name should be 255 characters maximum" -msgstr "Имя окружения не должно превышать 255 символов" - -msgid "Environment template name should be 255 characters maximum" -msgstr "Имя шаблона окружения не должно превышать 255 символов" - -msgid "Environment template specified name already exists" -msgstr "Шаблон окружения с указанным именем уже существует" - -msgid "Environment with id {env_id} not found" -msgstr "Окружение с id {env_id} не найдено" - -msgid "Environment with specified name already exists" -msgstr "Окружение с указанным именем уже существует" - -msgid "Invalid sort direction: {0}" -msgstr "Неверное направление сортировки: {0}" - -msgid "Invalid sort key: {sort_key}. Must be one of the following: {available}" -msgstr "" -"Недопустимый ключ сортировки: {sort_key}. Допускается один из следующих " -"ключей: {available}" - -msgid "" -"It's impossible to delete categories assigned to the package, uploaded to " -"the catalog" -msgstr "" -"Невозможно удалить категории, присвоенные загруженному в каталог пакету" - -msgid "Limit param must be an integer" -msgstr "Параметр limit должен быть целым числом" - -msgid "Limit param must be positive" -msgstr "Параметр limit должен быть положительным числом" - -msgid "" -"Local package is not found since \"load-packages-from\" engine parameter is " -"not provided and specified packages is not loaded to murano-api" -msgstr "" -"Пакет не найдет локально, поскольку параметр \"load-packages-from\" не " -"установлен, а так же пакет не был загружен через API" - -msgid "Malformed request body" -msgstr "Неправильно сформированное тело запроса" - -msgid "Murano object model validation failed: {0}" -msgstr "Ошибка при валидации объкетной модели мурано: {0}" - -msgid "Nested paths are not allowed" -msgstr "Вложенные пути недопустимы" - -msgid "No tests found for execution." -msgstr "Не найдено тестов для выполнения." - -#, python-format -msgid "Operation \"%s\" requires a member named \"value\"." -msgstr "Операции \"%s\" требуется участник с именем \"value\"." - -msgid "Operations must be JSON objects." -msgstr "Операции должны быть объектами JSON." - -msgid "Package '{pkg_id}' is not owned by tenant '{tenant}'" -msgstr "Пакет '{pkg_id}' не принадлежит проекту '{tenant}'" - -msgid "Package '{pkg_id}' is not public and not owned by tenant '{tenant}' " -msgstr "" -"Пакет '{pkg_id}' не является публичным и не принадлежит проекту '{tenant}'" - -msgid "Package id '{pkg_id}' not found" -msgstr "Пакет с id '{pkg_id}' не найден" - -msgid "Package schema is not valid: {reason}" -msgstr "Неверная схема пакета: {reason}" - -msgid "Package with specified full name is already registered" -msgstr "Пакет с указанным полным именем уже зарегистрирован" - -msgid "Package with the same Name is already made public" -msgstr "Пакет с таким именем уже является публичным" - -msgid "Path to class configuration files" -msgstr "Путь к классу конфигурационных файлов" - -msgid "Please, specify a name of the environment template." -msgstr "Укажите имя шаблона окружения" - -msgid "Please, specify a name of the environment to create" -msgstr "Укажите имя создаваемого окружения" - -#, python-format -msgid "Pointer `%s` contains adjacent \"/\"." -msgstr "Указатель `%s` содержит смежный \"/\"." - -#, python-format -msgid "Pointer `%s` does not start with \"/\"." -msgstr "Указатель `%s` не начинается с \"/\"." - -msgid "Request body is empty: please, provide application object model" -msgstr "Пустое тело запроса: укажите объектную модель приложения" - -msgid "Request body must be a JSON array of operation objects." -msgstr "Тело запроса должно быть массивом JSON объектов операций." - -msgid "Role used to identify an authenticated user as administrator." -msgstr "" -"Роль, применяемая для определения идентифицированного пользователя в " -"качестве администратора." - -msgid "Session is already in deployment state" -msgstr "Сессия находится в состоянии развёртывания" - -msgid "" -"Session is invalid: environment has been updated or updating " -"right now with other session" -msgstr "" -"Сессия недействительна: окружение было обновлено или " -"обновляется в другой сессии" - -msgid "Session is not found" -msgstr "Сессия не найдена" - -msgid "Session is not found" -msgstr "Сессия не найдена" - -msgid "" -"Session is already deployed or deployment is in progress" -msgstr "" -"Сессия уже задеплоена или находится в состоянии " -"развёртывания" - -msgid "" -"Session is not tied with Environment " -msgstr "" -"Сессия не привязана к Окружению " - -msgid "" -"Session is in deploying state and could not be deleted" -msgstr "" -"Сессия находится в состоянии развёртывания и не может " -"быть удалена" - -msgid "Source object or path is malformed" -msgstr "Неверный исходный объект или путь" - -msgid "" -"Specified package is not found: {0} were scanned together with murano " -"database" -msgstr "" -"Пакет не был найден: {0} были просканированны вместе с базой данных murano" - -msgid "Statistics collection interval in minutes.Default value is 5 minutes." -msgstr "Интервал сбора статистики в минутах. Значение по умолчанию - 5 минут." - -msgid "The environment template {templ_id} does not exist" -msgstr "Шаблон окружения {templ_id} не существует" - -msgid "The template does not exist {templ_id}" -msgstr "Шаблон не существует {templ_id}" - -msgid "There is no file package with application description" -msgstr "Отсутствует файл пакета с описанием приложения" - -msgid "Time for waiting for a response from murano agent during the deployment" -msgstr "Время ожидания ответа от мурано агента при развёртывания" - -#, python-format -msgid "Unable to find '%s' in JSON Schema change" -msgstr "'%s' не найден в изменении схемы JSON" - -#, python-format -msgid "" -"Unable to load %(app_name)s from configuration file %(conf_file)s. \n" -"Got: %(e)r" -msgstr "" -"Невозможно загрузить %(app_name)s из конфигурационного файла %(conf_file)s.\n" -"Ошибка: %(e)r" - -#, python-format -msgid "Unable to locate paste config file for %s." -msgstr "Не удается найти файл конфигурации paste для %s." - -msgid "Unsupported Content-Type" -msgstr "Неподдерживаемый Content-Type" - -msgid "Uploading file can't be empty" -msgstr "Загружаемый файл не может быть пустым" - -msgid "Uploading file is too large. The limit is {0} Mb" -msgstr "Загружаемый файл слишком большой. Значение не должно превышать {0} Mb" - -msgid "" -"User is not authorized to access session ." -msgstr "" -"У пользователя нет прав на доступ к сессии ." - -msgid "User is not authorized to access these tenant resources" -msgstr "У пользователя нет прав на доступ к этим ресурсам этого проекта" - -msgid "User is not authorized to access this tenant resources" -msgstr "У пользователя нет прав на доступ к ресурсам этого проекта" - -msgid "Value '{value}' of property '{path}' does not exist." -msgstr "Значение '{value}' для свойства '{path}' не существует" - -msgid "X-Configuration-Session header which indicates to the session is missed" -msgstr "" -"Заголовок X-Configuration-Session, которым определяется сессия, отсутствует" - -msgid "Your credentials are wrong. Please try again" -msgstr "Ваша авторизация не верна. Попробуйте ещё раз" - -msgid "cannot understand JSON" -msgstr "не удается проанализировать JSON" - -msgid "cannot understand XML" -msgstr "не удается проанализировать XML" diff --git a/murano/monkey_patch.py b/murano/monkey_patch.py deleted file mode 100644 index 094aeee4a..000000000 --- a/murano/monkey_patch.py +++ /dev/null @@ -1,31 +0,0 @@ -# -# Licensed 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 os - -import eventlet - - -if os.name == 'nt': - # eventlet monkey patching causes subprocess.Popen to fail on Windows - # when using pipes due to missing non blocking I/O support - eventlet.monkey_patch(os=False) -else: - eventlet.monkey_patch() -# Monkey patch the original current_thread to use the up-to-date _active -# global variable. See https://bugs.launchpad.net/bugs/1863021 and -# https://github.com/eventlet/eventlet/issues/592 -import __original_module_threading as orig_threading -import threading # noqa -orig_threading.current_thread.__globals__['_active'] = threading._active diff --git a/murano/opts.py b/murano/opts.py deleted file mode 100644 index 815159338..000000000 --- a/murano/opts.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 copy -import itertools -from keystoneauth1 import loading as ks_loading - -import oslo_service.sslutils - -import murano.common.cf_config -import murano.common.config -import murano.common.wsgi - -__all__ = [ - 'list_opts', - 'list_cfapi_opts', -] - - -def build_list(opt_list): - return list(itertools.chain(*opt_list)) - - -# List of *all* options in [DEFAULT] namespace of murano. -# Any new option list or option needs to be registered here. -_opt_lists = [ - ('engine', murano.common.config.engine_opts), - ('rabbitmq', murano.common.config.rabbit_opts), - ('heat', - murano.common.config.heat_opts + - ks_loading.get_session_conf_options()), - ('neutron', - murano.common.config.neutron_opts + - ks_loading.get_session_conf_options()), - ('murano', murano.common.config.murano_opts + - ks_loading.get_session_conf_options()), - ('glare', - murano.common.config.glare_opts + - ks_loading.get_session_conf_options()), - ('mistral', - murano.common.config.mistral_opts + - ks_loading.get_session_conf_options()), - ('networking', murano.common.config.networking_opts), - ('stats', murano.common.config.stats_opts), - ('murano_auth', - murano.common.config.murano_auth_opts + - ks_loading.get_session_conf_options() + - ks_loading.get_auth_common_conf_options() + - ks_loading.get_auth_plugin_conf_options('password') + - ks_loading.get_auth_plugin_conf_options('v2password') + - ks_loading.get_auth_plugin_conf_options('v3password')), - (None, build_list([ - murano.common.config.metadata_dir, - murano.common.config.bind_opts, - murano.common.config.file_server, - murano.common.wsgi.wsgi_opts, - ])), -] - -_cfapi_opt_lists = [ - ('cfapi', murano.common.cf_config.cfapi_opts), - ('glare', - murano.common.config.glare_opts + - ks_loading.get_session_conf_options()) -] - -_opt_lists.extend(oslo_service.sslutils.list_opts()) - - -def list_opts(): - """Return a list of oslo.config options available in Murano. - - Each element of the list is a tuple. The first element is the name of the - group under which the list of elements in the second element will be - registered. A group name of None corresponds to the [DEFAULT] group in - config files. - - This function is also discoverable via the 'murano' entry point - under the 'oslo.config.opts' namespace. - - The purpose of this is to allow tools like the Oslo sample config file - generator to discover the options exposed to users by Murano. - - :returns: a list of (group_name, opts) tuples - """ - return [(g, copy.deepcopy(o)) for g, o in _opt_lists] - - -def list_cfapi_opts(): - """Return a list of oslo_config options available in service broker.""" - return [(g, copy.deepcopy(o)) for g, o in _cfapi_opt_lists] diff --git a/murano/packages/__init__.py b/murano/packages/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/packages/exceptions.py b/murano/packages/exceptions.py deleted file mode 100644 index 07e3767e0..000000000 --- a/murano/packages/exceptions.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 murano.common.exceptions as e - - -class PackageException(e.Error): - pass - - -class PackageClassLoadError(PackageException): - def __init__(self, class_name, message=None): - msg = 'Unable to load class "{0}" from package'.format(class_name) - if message: - msg += ": " + message - super(PackageClassLoadError, self).__init__(msg) - - -class PackageUILoadError(PackageException): - def __init__(self, message=None): - msg = 'Unable to load ui definition from package' - if message: - msg += ": " + message - super(PackageUILoadError, self).__init__(msg) - - -class PackageLoadError(PackageException): - pass - - -class PackageFormatError(PackageLoadError): - def __init__(self, message=None): - msg = 'Incorrect package format' - if message: - msg += ': ' + message - super(PackageFormatError, self).__init__(msg) diff --git a/murano/packages/hot_package.py b/murano/packages/hot_package.py deleted file mode 100644 index 323a0eae3..000000000 --- a/murano/packages/hot_package.py +++ /dev/null @@ -1,541 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 os -import shutil -import sys - -import yaml - -from murano.common.helpers import path -from murano.packages import exceptions -from murano.packages import package_base - -RESOURCES_DIR_NAME = 'Resources/' -HOT_FILES_DIR_NAME = 'HotFiles/' -HOT_ENV_DIR_NAME = 'HotEnvironments/' - - -class YAQL(object): - def __init__(self, expr): - self.expr = expr - - -class Dumper(yaml.SafeDumper): - pass - - -def yaql_representer(dumper, data): - return dumper.represent_scalar(u'!yaql', data.expr) - - -Dumper.add_representer(YAQL, yaql_representer) - - -class HotPackage(package_base.PackageBase): - def __init__(self, format_name, runtime_version, source_directory, - manifest): - super(HotPackage, self).__init__( - format_name, runtime_version, source_directory, manifest) - - self._translated_class = None - self._source_directory = source_directory - self._translated_ui = None - - @property - def classes(self): - return self.full_name, - - @property - def requirements(self): - return {} - - @property - def ui(self): - if not self._translated_ui: - self._translated_ui = self._translate_ui() - return self._translated_ui - - def get_class(self, name): - if name != self.full_name: - raise exceptions.PackageClassLoadError( - name, 'Class not defined in this package') - if not self._translated_class: - self._translate_class() - return self._translated_class, '' - - def _translate_class(self): - template_file = path.secure_join( - self._source_directory, 'template.yaml') - - if not os.path.isfile(template_file): - raise exceptions.PackageClassLoadError( - self.full_name, 'File with class definition not found') - - shutil.copy(template_file, self.get_resource(self.full_name)) - with open(template_file) as stream: - hot = yaml.safe_load(stream) - if 'resources' not in hot: - raise exceptions.PackageFormatError('Not a HOT template') - translated = { - 'Name': self.full_name, - 'Extends': 'io.murano.Application' - } - - hot_envs_path = path.secure_join( - self._source_directory, RESOURCES_DIR_NAME, HOT_ENV_DIR_NAME) - - # if using hot environments, doing parameter validation with contracts - # will overwrite the parameters in the hot environment. - # don't validate parameters if hot environments exist. - validate_hot_parameters = (not os.path.isdir(hot_envs_path) or - not os.listdir(hot_envs_path)) - - parameters = HotPackage._build_properties(hot, validate_hot_parameters) - parameters.update(HotPackage._translate_outputs(hot)) - translated['Properties'] = parameters - - files = HotPackage._translate_files(self._source_directory) - translated.update(HotPackage._generate_workflow(hot, files)) - - # use default_style with double quote mark because by default PyYAML - # doesn't put any quote marks ans as a result strings with e.g. dashes - # may be interpreted as YAQL expressions upon load - self._translated_class = yaml.dump( - translated, Dumper=Dumper, default_style='"') - - @staticmethod - def _build_properties(hot, validate_hot_parameters): - result = { - 'generatedHeatStackName': { - 'Contract': YAQL('$.string()'), - 'Usage': 'Out' - }, - 'hotEnvironment': { - 'Contract': YAQL('$.string()'), - 'Usage': 'In' - }, - 'name': { - 'Contract': YAQL('$.string().notNull()'), - 'Usage': 'In', - - } - } - - if validate_hot_parameters: - params_dict = {} - for key, value in (hot.get('parameters') or {}).items(): - param_contract = HotPackage._translate_param_to_contract(value) - params_dict[key] = param_contract - result['templateParameters'] = { - 'Contract': params_dict, - 'Default': {}, - 'Usage': 'In' - } - else: - result['templateParameters'] = { - 'Contract': {}, - 'Default': {}, - 'Usage': 'In' - } - - return result - - @staticmethod - def _translate_param_to_contract(value): - contract = '$' - - parameter_type = value['type'] - if parameter_type in ('string', 'comma_delimited_list', 'json'): - contract += '.string()' - elif parameter_type == 'number': - contract += '.int()' - elif parameter_type == 'boolean': - contract += '.bool()' - else: - raise ValueError('Unsupported parameter type ' + parameter_type) - - constraints = value.get('constraints') or [] - for constraint in constraints: - translated = HotPackage._translate_constraint(constraint) - if translated: - contract += translated - - result = YAQL(contract) - return result - - @staticmethod - def _translate_outputs(hot): - contract = {} - for key in (hot.get('outputs') or {}).keys(): - contract[key] = YAQL("$.string()") - return { - 'templateOutputs': { - 'Contract': contract, - 'Default': {}, - 'Usage': 'Out' - } - } - - @staticmethod - def _translate_files(source_directory): - hot_files_path = path.secure_join( - source_directory, RESOURCES_DIR_NAME, HOT_FILES_DIR_NAME) - - return HotPackage._build_hot_resources(hot_files_path) - - @staticmethod - def _build_hot_resources(basedir): - result = [] - if os.path.isdir(basedir): - for root, _, files in os.walk(os.path.abspath(basedir)): - for f in files: - full_path = path.secure_join(root, f) - relative_path = os.path.relpath(full_path, basedir) - result.append(relative_path) - return result - - @staticmethod - def _translate_constraint(constraint): - if 'allowed_values' in constraint: - return HotPackage._translate_allowed_values_constraint( - constraint['allowed_values']) - elif 'length' in constraint: - return HotPackage._translate_length_constraint( - constraint['length']) - elif 'range' in constraint: - return HotPackage._translate_range_constraint( - constraint['range']) - elif 'allowed_pattern' in constraint: - return HotPackage._translate_allowed_pattern_constraint( - constraint['allowed_pattern']) - - @staticmethod - def _translate_allowed_pattern_constraint(value): - return ".check(matches($, '{0}'))".format(value) - - @staticmethod - def _translate_allowed_values_constraint(values): - return '.check($ in list({0}))'.format( - ', '.join([HotPackage._format_value(v) for v in values])) - - @staticmethod - def _translate_length_constraint(value): - if 'min' in value and 'max' in value: - return '.check(len($) >= {0} and len($) <= {1})'.format( - int(value['min']), int(value['max'])) - elif 'min' in value: - return '.check(len($) >= {0})'.format(int(value['min'])) - elif 'max' in value: - return '.check(len($) <= {0})'.format(int(value['max'])) - - @staticmethod - def _translate_range_constraint(value): - if 'min' in value and 'max' in value: - return '.check($ >= {0} and $ <= {1})'.format( - int(value['min']), int(value['max'])) - elif 'min' in value: - return '.check($ >= {0})'.format(int(value['min'])) - elif 'max' in value: - return '.check($ <= {0})'.format(int(value['max'])) - - @staticmethod - def _format_value(value): - if isinstance(value, str): - return str("'" + value + "'") - return str(value) - - @staticmethod - def _generate_workflow(hot, files): - hot_files_map = {} - for f in files: - file_path = "$resources.string('{0}{1}')".format( - HOT_FILES_DIR_NAME, f) - hot_files_map[f] = YAQL(file_path) - - hot_env = YAQL("$.hotEnvironment") - - deploy = [ - {YAQL('$environment'): YAQL( - "$.find('io.murano.Environment').require()" - )}, - {YAQL('$reporter'): YAQL( - "new('io.murano.system.StatusReporter', " - "environment => $environment)")}, - { - 'If': YAQL('$.getAttr(generatedHeatStackName) = null'), - 'Then': [ - YAQL("$.setAttr(generatedHeatStackName, " - "'{0}_{1}'.format(randomName(), id($environment)))") - ] - }, - {YAQL('$stack'): YAQL( - "new('io.murano.system.HeatStack', $environment, " - "name => $.getAttr(generatedHeatStackName))")}, - - YAQL("$reporter.report($this, " - "'Application deployment has started')"), - - {YAQL('$resources'): YAQL("new('io.murano.system.Resources')")}, - - {YAQL('$template'): YAQL("$resources.yaml(type($this))")}, - YAQL('$stack.setTemplate($template)'), - {YAQL('$parameters'): YAQL("$.templateParameters")}, - YAQL('$stack.setParameters($parameters)'), - {YAQL('$files'): hot_files_map}, - YAQL('$stack.setFiles($files)'), - {YAQL('$hotEnv'): hot_env}, - { - 'If': YAQL("bool($hotEnv)"), - 'Then': [ - {YAQL('$envRelPath'): YAQL("'{0}' + $hotEnv".format( - HOT_ENV_DIR_NAME))}, - {YAQL('$hotEnvContent'): YAQL("$resources.string(" - "$envRelPath)")}, - YAQL('$stack.setHotEnvironment($hotEnvContent)') - ] - }, - - YAQL("$reporter.report($this, 'Stack creation has started')"), - { - 'Try': [YAQL('$stack.push()')], - 'Catch': [ - { - 'As': 'e', - 'Do': [ - YAQL("$reporter.report_error($this, $e.message)"), - {'Rethrow': None} - ] - } - ], - 'Else': [ - {YAQL('$.templateOutputs'): YAQL('$stack.output()')}, - YAQL("$reporter.report($this, " - "'Stack was successfully created')"), - - YAQL("$reporter.report($this, " - "'Application deployment has finished')"), - ] - } - ] - - destroy = [ - {YAQL('$environment'): YAQL( - "$.find('io.murano.Environment').require()" - )}, - {YAQL('$stack'): YAQL( - "new('io.murano.system.HeatStack', $environment, " - "name => $.getAttr(generatedHeatStackName))")}, - YAQL('$stack.delete()') - ] - - return { - 'Methods': { - 'deploy': { - 'Body': deploy - }, - 'destroy': { - 'Body': destroy - } - } - } - - @staticmethod - def _translate_ui_parameters(hot, title): - groups = hot.get('parameter_groups', []) - result_groups = [] - - predefined_fields = [ - { - 'name': 'title', - 'type': 'string', - 'required': False, - 'hidden': True, - 'description': title - }, - { - 'name': 'name', - 'type': 'string', - 'label': 'Application Name', - 'required': True, - 'description': - 'Enter a desired name for the application.' - ' Just A-Z, a-z, 0-9, and dash are allowed' - } - ] - used_parameters = set() - hot_parameters = hot.get('parameters') or {} - for group in groups: - fields = [] - properties = [] - for parameter in group.get('parameters', []): - parameter_value = hot_parameters.get(parameter) - if parameter_value: - fields.append(HotPackage._translate_ui_parameter( - parameter, parameter_value)) - used_parameters.add(parameter) - properties.append(parameter) - result_groups.append((fields, properties)) - - rest_group = [] - properties = [] - for key, value in hot_parameters.items(): - if key not in used_parameters: - rest_group.append(HotPackage._translate_ui_parameter( - key, value)) - properties.append(key) - if rest_group: - result_groups.append((rest_group, properties)) - - result_groups.insert(0, (predefined_fields, ['name'])) - return result_groups - - @staticmethod - def _translate_ui_parameter(name, parameter_spec): - translated = { - 'name': name, - 'label': name.title().replace('_', ' ') - } - parameter_type = parameter_spec['type'] - if parameter_type == 'number': - translated['type'] = 'integer' - elif parameter_type == 'boolean': - translated['type'] = 'boolean' - else: - # string, json, and comma_delimited_list parameters are all - # displayed as strings in UI. Any unsupported parameter would also - # be displayed as strings. - translated['type'] = 'string' - - label = parameter_spec.get('label') - if label: - translated['label'] = label - - if 'description' in parameter_spec: - translated['description'] = parameter_spec['description'] - - if 'default' in parameter_spec: - translated['initial'] = parameter_spec['default'] - translated['required'] = False - else: - translated['required'] = True - - constraints = parameter_spec.get('constraints') or [] - translated_constraints = [] - - for constraint in constraints: - if 'length' in constraint: - spec = constraint['length'] - if 'min' in spec: - translated['minLength'] = max( - translated.get('minLength', -sys.maxsize - 1), - int(spec['min'])) - if 'max' in spec: - translated['maxLength'] = min( - translated.get('maxLength', sys.maxsize), - int(spec['max'])) - - elif 'range' in constraint: - spec = constraint['range'] - if 'min' in spec and 'max' in spec: - ui_constraint = { - 'expr': YAQL('$ >= {0} and $ <= {1}'.format( - spec['min'], spec['max'])) - } - elif 'min' in spec: - ui_constraint = { - 'expr': YAQL('$ >= {0}'.format(spec['min'])) - } - else: - ui_constraint = { - 'expr': YAQL('$ <= {0}'.format(spec['max'])) - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - elif 'allowed_values' in constraint: - values = constraint['allowed_values'] - ui_constraint = { - 'expr': YAQL('$ in list({0})'.format(', '.join( - [HotPackage._format_value(v) for v in values]))) - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - elif 'allowed_pattern' in constraint: - pattern = constraint['allowed_pattern'] - ui_constraint = { - 'expr': { - 'regexpValidator': pattern - } - } - if 'description' in constraint: - ui_constraint['message'] = constraint['description'] - translated_constraints.append(ui_constraint) - - if translated_constraints: - translated['validators'] = translated_constraints - - return translated - - @staticmethod - def _generate_application_ui(groups, type_name, - package_name=None, package_version=None): - app = { - '?': { - 'type': type_name - } - } - if package_name: - app['?']['package'] = package_name - if package_version: - app['?']['classVersion'] = package_version - for i, record in enumerate(groups): - if i == 0: - section = app - else: - section = app.setdefault('templateParameters', {}) - for property_name in record[1]: - section[property_name] = YAQL( - '$.group{0}.{1}'.format(i, property_name)) - app['name'] = YAQL('$.group0.name') - - return app - - def _translate_ui(self): - template_file = path.secure_join( - self._source_directory, 'template.yaml') - - if not os.path.isfile(template_file): - raise exceptions.PackageClassLoadError( - self.full_name, 'File with class definition not found') - with open(template_file) as stream: - hot = yaml.safe_load(stream) - - groups = HotPackage._translate_ui_parameters(hot, self.description) - forms = [] - for i, record in enumerate(groups): - forms.append({'group{0}'.format(i): {'fields': record[0]}}) - - translated = { - 'Version': 2, - 'Application': HotPackage._generate_application_ui( - groups, self.full_name, self.full_name, str(self.version)), - 'Forms': forms - } - - # see comment above about default_style - return yaml.dump(translated, Dumper=Dumper, default_style='"') diff --git a/murano/packages/load_utils.py b/murano/packages/load_utils.py deleted file mode 100644 index 4d9d9f767..000000000 --- a/murano/packages/load_utils.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 contextlib -import os -import shutil -import sys -import tempfile -import zipfile - -import yaml - -from murano.common.helpers import path -from murano.common.plugins import package_types_loader -from murano.common import utils -import murano.packages.exceptions as e -import murano.packages.hot_package -import murano.packages.mpl_package - - -PLUGIN_LOADER = None - - -def get_plugin_loader(): - global PLUGIN_LOADER - - if PLUGIN_LOADER is None: - PLUGIN_LOADER = package_types_loader.PluginLoader() - for runtime_version in ('1.0', '1.1', '1.2', '1.3', '1.4'): - format_string = 'MuranoPL/' + runtime_version - PLUGIN_LOADER.register_format( - format_string, murano.packages.mpl_package.MuranoPlPackage) - PLUGIN_LOADER.register_format( - 'Heat.HOT/1.0', murano.packages.hot_package.HotPackage) - return PLUGIN_LOADER - - -@contextlib.contextmanager -def load_from_file(archive_path, target_dir=None, drop_dir=False): - if not os.path.isfile(archive_path): - raise e.PackageLoadError('Unable to find package file') - created = False - if not target_dir: - target_dir = tempfile.mkdtemp() - created = True - elif not os.path.exists(target_dir): - os.makedirs(target_dir) - created = True - else: - if os.listdir(target_dir): - raise e.PackageLoadError('Target directory is not empty') - - try: - if not zipfile.is_zipfile(archive_path): - raise e.PackageFormatError("Uploaded file {0} is not a " - "zip archive".format(archive_path)) - package = zipfile.ZipFile(archive_path) - package.extractall(path=target_dir) - yield load_from_dir(target_dir) - except ValueError as err: - raise e.PackageLoadError("Couldn't load package from file: " - "{0}".format(err)) - finally: - if drop_dir: - if created: - shutil.rmtree(target_dir) - else: - for f in os.listdir(target_dir): - os.unlink(path.secure_join(target_dir, f)) - - -def load_from_dir(source_directory, filename='manifest.yaml'): - if not os.path.isdir(source_directory) or not os.path.exists( - source_directory): - raise e.PackageLoadError('Invalid package directory') - full_path = path.secure_join(source_directory, filename) - if not os.path.isfile(full_path): - raise e.PackageLoadError('Unable to find package manifest') - - try: - with open(full_path) as stream: - content = yaml.safe_load(stream) - except Exception as ex: - trace = sys.exc_info()[2] - utils.reraise( - e.PackageLoadError, - e.PackageLoadError("Unable to load due to '{0}'".format(ex)), - trace) - else: - format_spec = str(content.get('Format') or 'MuranoPL/1.0') - if format_spec[0].isdigit(): - format_spec = 'MuranoPL/' + format_spec - plugin_loader = get_plugin_loader() - handler = plugin_loader.get_package_handler(format_spec) - if handler is None: - raise e.PackageFormatError( - 'Unsupported format {0}'.format(format_spec)) - return handler(source_directory, content) diff --git a/murano/packages/mpl_package.py b/murano/packages/mpl_package.py deleted file mode 100644 index 09f1d8228..000000000 --- a/murano/packages/mpl_package.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 os - -from murano.common.helpers import path -from murano.packages import exceptions -from murano.packages import package_base - - -class MuranoPlPackage(package_base.PackageBase): - def __init__(self, format_name, runtime_version, source_directory, - manifest): - super(MuranoPlPackage, self).__init__( - format_name, runtime_version, source_directory, manifest) - self._classes = manifest.get('Classes') - self._ui_file = manifest.get('UI', 'ui.yaml') - self._requirements = manifest.get('Require') or {} - self._meta = manifest.get('Meta') - - @property - def classes(self): - return self._classes.keys() - - @property - def ui(self): - full_path = path.secure_join( - self._source_directory, 'UI', self._ui_file) - if not os.path.isfile(full_path): - return None - with open(full_path, 'rb') as stream: - return stream.read() - - @property - def requirements(self): - return self._requirements - - def get_class(self, name): - if name not in self._classes: - raise exceptions.PackageClassLoadError( - name, 'Class not defined in package ' + self.full_name) - def_file = self._classes[name] - full_path = path.secure_join( - self._source_directory, 'Classes', def_file) - if not os.path.isfile(full_path): - raise exceptions.PackageClassLoadError( - name, 'File with class definition not found') - with open(full_path, 'rb') as stream: - return stream.read(), full_path - - @property - def meta(self): - return self._meta diff --git a/murano/packages/package.py b/murano/packages/package.py deleted file mode 100644 index d58a697ff..000000000 --- a/murano/packages/package.py +++ /dev/null @@ -1,144 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# -# Licensed 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 abc -import io -import os -import zipfile - -from murano.common.helpers import path - - -class PackageType(object): - Library = 'Library' - Application = 'Application' - ALL = [Library, Application] - - -class Package(object, metaclass=abc.ABCMeta): - def __init__(self, format_name, runtime_version, source_directory): - self._source_directory = source_directory - self._format_name = format_name - self._runtime_version = runtime_version - self._blob_cache = None - - @property - def format_name(self): - return self._format_name - - @property - @abc.abstractmethod - def full_name(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def version(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def classes(self): - raise NotImplementedError() - - @property - def runtime_version(self): - return self._runtime_version - - @property - @abc.abstractmethod - def requirements(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def package_type(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def display_name(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def description(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def author(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def supplier(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def tags(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def logo(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def supplier_logo(self): - raise NotImplementedError() - - @property - def blob(self): - if not self._blob_cache: - self._blob_cache = _pack_dir(self._source_directory) - return self._blob_cache - - @abc.abstractmethod - def get_class(self, name): - raise NotImplementedError() - - @abc.abstractmethod - def get_resource(self, name): - raise NotImplementedError() - - @property - @abc.abstractmethod - def ui(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def meta(self): - raise NotImplementedError() - - -def _zip_dir(base, zip_file): - for root, _, files in os.walk(base): - for f in files: - abs_path = path.secure_join(root, f) - relative_path = os.path.relpath(abs_path, base) - zip_file.write(abs_path, relative_path) - - -def _pack_dir(source_directory): - blob = io.BytesIO() - zip_file = zipfile.ZipFile(blob, mode='w') - _zip_dir(source_directory, zip_file) - zip_file.close() - - return blob.getvalue() diff --git a/murano/packages/package_base.py b/murano/packages/package_base.py deleted file mode 100644 index a988dd826..000000000 --- a/murano/packages/package_base.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# -# Licensed 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 abc -import imghdr -import os -import re -import sys - -import semantic_version - -from murano.common.helpers import path -from murano.common.i18n import _ -from murano.common import utils -from murano.packages import exceptions -from murano.packages import package - - -class PackageBase(package.Package): - def __init__(self, format_name, runtime_version, - source_directory, manifest): - super(PackageBase, self).__init__( - format_name, runtime_version, source_directory) - self._full_name = manifest.get('FullName') - if not self._full_name: - raise exceptions.PackageFormatError('FullName is not specified') - self._check_full_name(self._full_name) - self._version = semantic_version.Version.coerce(str(manifest.get( - 'Version', '0.0.0'))) - self._package_type = manifest.get('Type') - if self._package_type not in package.PackageType.ALL: - raise exceptions.PackageFormatError( - 'Invalid package Type {0}'.format(self._package_type)) - self._display_name = manifest.get('Name', self._full_name) - self._description = manifest.get('Description') - self._author = manifest.get('Author') - self._supplier = manifest.get('Supplier') or {} - self._logo = manifest.get('Logo') - self._tags = manifest.get('Tags', []) - - self._logo_cache = None - self._supplier_logo_cache = None - self._source_directory = source_directory - - @property - @abc.abstractmethod - def requirements(self): - raise NotImplementedError() - - @property - @abc.abstractmethod - def classes(self): - raise NotImplementedError() - - @abc.abstractmethod - def get_class(self, name): - raise NotImplementedError() - - @property - @abc.abstractmethod - def ui(self): - raise NotImplementedError() - - @property - def full_name(self): - return self._full_name - - @property - def source_directory(self): - return self._source_directory - - @property - def version(self): - return self._version - - @property - def package_type(self): - return self._package_type - - @property - def display_name(self): - return self._display_name - - @property - def description(self): - return self._description - - @property - def author(self): - return self._author - - @property - def supplier(self): - return self._supplier - - @property - def tags(self): - return list(self._tags) - - @property - def logo(self): - return self._load_image(self._logo, 'logo.png', 'logo') - - @property - def meta(self): - return None - - @property - def supplier_logo(self): - return self._load_image( - self._supplier.get('Logo'), 'supplier_logo.png', 'supplier logo') - - def get_resource(self, name): - resources_dir = path.secure_join(self._source_directory, 'Resources') - if not os.path.exists(resources_dir): - os.makedirs(resources_dir) - return path.secure_join(resources_dir, name) - - def _load_image(self, file_name, default_name, what_image): - full_path = path.secure_join( - self._source_directory, file_name or default_name) - if not os.path.isfile(full_path) and not file_name: - return - - allowed_ftype = ('png', 'jpeg', 'gif') - allowed_size = 500 * 1024 - try: - - if imghdr.what(full_path) not in allowed_ftype: - msg = _('{0}: Unsupported Format. Only {1} allowed').format( - what_image, ', '.join(allowed_ftype)) - - raise exceptions.PackageLoadError(msg) - - fsize = os.stat(full_path).st_size - if fsize > allowed_size: - msg = _('{0}: Uploaded image size {1} is too large. ' - 'Max allowed size is {2}').format( - what_image, fsize, allowed_size) - raise exceptions.PackageLoadError(msg) - - with open(full_path, 'rb') as stream: - return stream.read() - - except Exception as ex: - trace = sys.exc_info()[2] - utils.reraise( - exceptions.PackageLoadError, - exceptions.PackageLoadError( - 'Unable to load {0}: {1}'.format(what_image, ex)), - trace) - - @staticmethod - def _check_full_name(full_name): - error = exceptions.PackageFormatError('Invalid FullName ' + full_name) - if re.match(r'^[\w\.]+$', full_name): - if full_name.startswith('.') or full_name.endswith('.'): - raise error - if '..' in full_name: - raise error - else: - raise error diff --git a/murano/policy/__init__.py b/murano/policy/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/policy/congress_rules.py b/murano/policy/congress_rules.py deleted file mode 100644 index 5020cc693..000000000 --- a/murano/policy/congress_rules.py +++ /dev/null @@ -1,269 +0,0 @@ -# Copyright (c) 2014 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 murano.dsl import helpers - - -class CongressRulesManager(object): - """Converts murano model to list of congress rules - - The Congress rules are: - - murano:objects+(env_id, obj_id, type_name) - - murano:properties+(obj_id, prop_name, prop_value) - - murano:relationships+(source, target, name) - - murano:parent_types+(obj_id, parent_name) - - murano:states+(env_id, state) - """ - - _rules = [] - _env_id = '' - _package_loader = None - - def convert(self, model, package_loader=None, tenant_id=None): - self._rules = [] - self._package_loader = package_loader - - if model is None: - return self._rules - - self._env_id = model['?']['id'] - - state_rule = StateRule(self._env_id, 'pending') - self._rules.append(state_rule) - - self._walk(model, owner_id=tenant_id) - - # Convert MuranoProperty containing reference to another object - # to MuranoRelationship. - object_ids = [rule.obj_id for rule in self._rules - if isinstance(rule, ObjectRule)] - - self._rules = [self._create_relationship(rule, object_ids) - for rule in self._rules] - - relations = [(rel.source_id, rel.target_id) - for rel in self._rules - if isinstance(rel, RelationshipRule)] - closure = self.transitive_closure(relations) - - for rel in closure: - self._rules.append(ConnectedRule(rel[0], rel[1])) - - return self._rules - - @staticmethod - def transitive_closure(relations): - """Computes transitive closure on a directed graph. - - In other words computes reachability within the graph. - E.g. {(1, 2), (2, 3)} -> {(1, 2), (2, 3), (1, 3)} - (1, 3) was added because there is path from 1 to 3 in the graph. - - :param relations: list of relations/edges in form of tuples - :return: transitive closure including original relations - """ - closure = set(relations) - while True: - # Attempts to discover new transitive relations - # by joining 2 subsequent relations/edges within the graph. - new_relations = {(x, w) for x, y in closure - for q, w in closure if q == y} - # Creates union with already discovered relations. - closure_until_now = closure | new_relations - # If no new relations were discovered in last cycle - # the computation is finished. - if closure_until_now == closure: - return closure - closure = closure_until_now - - def _walk(self, obj, owner_id, path=()): - - if obj is None: - return - - obj = self._to_dict(obj) - new_owner = self._process_item(obj, owner_id, path) or owner_id - if isinstance(obj, list) or isinstance(obj, tuple): - for v in obj: - self._walk(v, new_owner, path) - elif isinstance(obj, dict): - for key, value in obj.items(): - self._walk(value, new_owner, path + (key, )) - - def _process_item(self, obj, owner_id, path): - if isinstance(obj, dict) and '?' in obj: - obj_rule = self._create_object_rule(obj, owner_id) - - self._rules.append(obj_rule) - # the environment has 'services' relationships - # to all its top-level applications - # traversal path is used to test whether - # we are at the right place within the tree - if path == ('applications',): - self._rules.append(RelationshipRule(self._env_id, - obj_rule.obj_id, - "services")) - self._rules.extend( - self._create_property_rules(obj_rule.obj_id, obj)) - - cls = obj['?']['type'] - if 'classVersion' in obj['?']: - version_spec = obj['?']['classVersion'] - else: - version_spec = '*' - types = self._get_parent_types( - cls, self._package_loader, version_spec) - self._rules.extend(self._create_parent_type_rules(obj['?']['id'], - types)) - # current object will be the owner for its subtree - return obj_rule.obj_id - - @staticmethod - def _to_dict(obj): - # If we have MuranoObject class we need to convert to dictionary. - if 'to_dictionary' in dir(obj): - return obj.to_dictionary() - else: - return obj - - def _create_object_rule(self, app, owner_id): - return ObjectRule(app['?']['id'], owner_id, app['?']['type']) - - def _create_property_rules(self, obj_id, obj, prefix=""): - rules = [] - - # Skip when inside properties of other object. - if '?' in obj and prefix != "": - rules.append(RelationshipRule(obj_id, obj['?']['id'], - prefix.split('.')[0])) - return rules - - for key, value in obj.items(): - if key == '?': - continue - - if value is not None: - value = self._to_dict(value) - if isinstance(value, dict): - rules.extend(self._create_property_rules( - obj_id, value, prefix + key + ".")) - elif isinstance(value, list) or isinstance(obj, tuple): - for v in value: - v = self._to_dict(v) - if not isinstance(v, dict): - rule = PropertyRule(obj_id, prefix + key, v) - rules.append(rule) - else: - rule = PropertyRule(obj_id, prefix + key, value) - rules.append(rule) - - return rules - - @staticmethod - def _is_relationship(rule, app_ids): - if not isinstance(rule, PropertyRule): - return False - - return rule.prop_value in app_ids - - def _create_relationship(self, rule, app_ids): - if self._is_relationship(rule, app_ids): - return RelationshipRule(rule.obj_id, rule.prop_value, - rule.prop_name) - else: - return rule - - @staticmethod - def _get_parent_types(type_name, package_loader, version_spec): - type_name, version_spec, _ = helpers.parse_type_string( - type_name, version_spec, None) - version_spec = helpers.parse_version_spec(version_spec) - result = {type_name} - if package_loader: - pkg = package_loader.load_class_package(type_name, version_spec) - cls = pkg.find_class(type_name, False) - if cls: - result.update(t.name for t in cls.ancestors()) - return result - - @staticmethod - def _create_parent_type_rules(app_id, types): - rules = [] - for type_name in types: - rules.append(ParentTypeRule(app_id, type_name)) - return rules - - -class ObjectRule(object): - def __init__(self, obj_id, owner_id, type_name): - self.obj_id = obj_id - self.owner_id = owner_id - self.type_name = helpers.parse_type_string(type_name, None, None)[0] - - def __str__(self): - return 'murano:objects+("{0}", "{1}", "{2}")'.format(self.obj_id, - self.owner_id, - self.type_name) - - -class PropertyRule(object): - def __init__(self, obj_id, prop_name, prop_value): - self.obj_id = obj_id - self.prop_name = prop_name - self.prop_value = prop_value - - def __str__(self): - return 'murano:properties+("{0}", "{1}", "{2}")'.format( - self.obj_id, self.prop_name, self.prop_value) - - -class RelationshipRule(object): - def __init__(self, source_id, target_id, rel_name): - self.source_id = source_id - self.target_id = target_id - self.rel_name = rel_name - - def __str__(self): - return 'murano:relationships+("{0}", "{1}", "{2}")'.format( - self.source_id, self.target_id, self.rel_name) - - -class ConnectedRule(object): - def __init__(self, source_id, target_id): - self.source_id = source_id - self.target_id = target_id - - def __str__(self): - return 'murano:connected+("{0}", "{1}")'.format( - self.source_id, self.target_id) - - -class ParentTypeRule(object): - def __init__(self, obj_id, type_name): - self.obj_id = obj_id - self.type_name = type_name - - def __str__(self): - return 'murano:parent_types+("{0}", "{1}")'.format(self.obj_id, - self.type_name) - - -class StateRule(object): - def __init__(self, obj_id, state): - self.obj_id = obj_id - self.state = state - - def __str__(self): - return 'murano:states+("{0}", "{1}")'.format(self.obj_id, self.state) diff --git a/murano/policy/model_policy_enforcer.py b/murano/policy/model_policy_enforcer.py deleted file mode 100644 index 7aff66844..000000000 --- a/murano/policy/model_policy_enforcer.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2014 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 re - -try: - # integration with congress is optional - import congressclient.v1.client as congress_client -except ImportError: - congress_client = None -from oslo_log import log as logging - -from murano.common import auth_utils -from murano.common.i18n import _ -from murano.policy import congress_rules -from murano.policy.modify.actions import action_manager as am - - -LOG = logging.getLogger(__name__) - - -class ValidationError(Exception): - """Raised for validation errors.""" - pass - - -class ModelPolicyEnforcer(object): - """Policy Enforcer Implementation using Congress client - - Converts murano model to list of congress data rules. - - We ask congress using simulation api of congress rest client - to resolve "murano_system:predeploy_errors(env_id, obj_id, msg)" - table along with congress data rules to return validation results. - """ - - def __init__(self, execution_session, action_manager=None): - self._execution_session = execution_session - self._action_manager = action_manager or am.ModifyActionManager() - self._client = None - - def _create_client(self): - if not congress_client: - # congress client was not imported - raise ImportError("Import congresscliet error") - return congress_client.Client( - **auth_utils.get_session_client_parameters( - service_type='policy', - execution_session=self._execution_session)) - - @property - def client(self): - if self._client is None: - self._client = self._create_client() - return self._client - - def modify(self, obj, package_loader=None): - """Modifies model using Congress rule engine. - - @type obj: object model - @param obj: Representation of model starting on - environment level (['Objects']) - @type class_loader: murano.dsl.class_loader.MuranoClassLoader - @param class_loader: Optional. Used for evaluating parent class types - @raises ValidationError in case validation was not successful - """ - - model = obj.to_dictionary() - - LOG.debug('Modifying model') - LOG.debug(model) - - env_id = model['?']['id'] - - result = self._execute_simulation(package_loader, env_id, model, - 'predeploy_modify(eid, oid, action)') - - raw_actions = result["result"] - if raw_actions: - actions = self._parse_simulation_result('predeploy_modify', - env_id, raw_actions) - for action in actions: - self._action_manager.apply_action(obj, action) - - def validate(self, model, package_loader=None): - """Validate model using Congress rule engine. - - @type model: dict - @param model: Dictionary representation of model starting on - environment level (['Objects']) - @type package_loader: murano.dsl.package_loader.MuranoPackageLoader - @param package_loader: Optional. Used for evaluating parent class types - @raises ValidationError in case validation was not successful - """ - - if model is None: - return - - env_id = model['?']['id'] - - validation_result = self._execute_simulation( - package_loader, env_id, model, - 'predeploy_errors(eid, oid, msg)') - - if validation_result["result"]: - - messages = self._parse_simulation_result( - 'predeploy_errors', env_id, - validation_result["result"]) - - if messages: - result_str = "\n ".join(map(str, messages)) - msg = _("Murano object model validation failed: {0}").format( - "\n " + result_str) - LOG.error(msg) - raise ValidationError(msg) - else: - LOG.info('Model valid') - - def _execute_simulation(self, package_loader, env_id, model, query): - rules = congress_rules.CongressRulesManager().convert( - model, package_loader, self._execution_session.project_id) - rules_str = list(map(str, rules)) - # cleanup of data populated by murano driver - rules_str.insert(0, 'deleteEnv("{0}")'.format(env_id)) - rules_line = " ".join(rules_str) - LOG.debug('Congress rules: \n {rules} ' - .format(rules='\n '.join(rules_str))) - - validation_result = self.client.execute_policy_action( - "murano_system", - "simulate", - False, - False, - {'query': query, - 'action_policy': 'murano_action', - 'sequence': rules_line}) - return validation_result - - @staticmethod - def _parse_simulation_result(query, env_id, results): - """Transforms the list of results - - Transforms a list of strings in format: - - ['predeploy_errors("env_id_1", "obj_id_1", "message1")', - 'predeploy_errors("env_id_2", "obj_id_2", "message2")'] - - to a list of strings with message only filtered to provided - env_id (e.g. 'env_id_1'): - - ['message2'] - """ - - messages = [] - regexp = query + '\("([^"]*)",\s*"([^"]*)",\s*"(.*)"\)' - for result in results: - match = re.search(regexp, result) - if match: - if env_id in match.group(1): - messages.append(match.group(3)) - - return messages diff --git a/murano/policy/modify/__init__.py b/murano/policy/modify/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/policy/modify/actions/__init__.py b/murano/policy/modify/actions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/policy/modify/actions/action_manager.py b/murano/policy/modify/actions/action_manager.py deleted file mode 100644 index b32c5cd5f..000000000 --- a/murano/policy/modify/actions/action_manager.py +++ /dev/null @@ -1,94 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 oslo_log import log as logging -from oslo_utils import importutils -from stevedore import extension -import yaml - -LOG = logging.getLogger(__name__) - - -class ModifyActionManager(object): - """Manages modify actions - - The manager encapsulates extensible plugin mechanism for - modify actions loading. Provides ability to apply action on - given object model based on action specification retrieved - from congress - """ - - def __init__(self): - self._cache = {} - - def load_action(self, name): - """Loads action by its name - - Loaded actions are cached. Plugin mechanism is based on - distutils entry points. Entry point namespace is - 'murano_policy_modify_actions' - - :param name: action name - :return: - """ - if name in self._cache: - return self._cache[name] - action = self._load_action(name) - self._cache[name] = action - return action - - @staticmethod - def _load_action(name): - mgr = extension.ExtensionManager( - namespace='murano_policy_modify_actions', - invoke_on_load=False - ) - for ext in mgr.extensions: - if name == ext.name: - target = ext.entry_point_target.replace(':', '.') - return importutils.import_class(target) - raise ValueError('No such action definition: {action_name}' - .format(action_name=name)) - - def apply_action(self, obj, action_spec): - """Apply action on given model - - Parse action and its parameters from action specification - retrieved from congress. Action specification is YAML format. - - E.g. remove-object: {object_id: abc123}") - - Action names are keys in top-level dictionary. Values are - dictionaries containing key/value parameters of the action - - :param obj: subject of modification - :param action_spec: YAML action spec - :raise ValueError: in case of malformed action spec - """ - actions = yaml.safe_load(action_spec) - if not isinstance(actions, dict): - raise ValueError('Expected action spec format is ' - '"action-name: {{p1: v1, ...}}" ' - 'but got "{action_spec}"' - .format(action_spec=action_spec)) - for name, kwargs in actions.items(): - LOG.debug('Executing action {name}, params {params}' - .format(name=name, params=kwargs)) - # loads action class - action_class = self.load_action(name) - # creates action instance - action_instance = action_class(**kwargs) - # apply action on object model - action_instance.modify(obj) diff --git a/murano/policy/modify/actions/base.py b/murano/policy/modify/actions/base.py deleted file mode 100644 index a40168a25..000000000 --- a/murano/policy/modify/actions/base.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 abc - - -class ModifyActionBase(object, metaclass=abc.ABCMeta): - """Base class for model modify actions. - - Class is instantiated base on list of actions returned from congress then - is performed on given object model. - - Base action class initializer doesn't have arguments. However, concrete - action classes may have any number of parameters defining action behavior. - These parameters must correspond to parameters returned from congress. - - Action must be registered/exposed to action manager via entry point - 'murano_policy_modify_actions'. Only actions registered via this - entry point are can be used. - """ - - @abc.abstractmethod - def modify(self, model): - - """Modifies given object model - - Action parameters are available as instance variables - passed to initializer. - - :param model: object model to be modified - """ - pass diff --git a/murano/policy/modify/actions/default_actions.py b/murano/policy/modify/actions/default_actions.py deleted file mode 100644 index c9da091c6..000000000 --- a/murano/policy/modify/actions/default_actions.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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. - -""" -Default actions reside in this module. -These action are available out of the box for Murano users. - -""" -import yaql.language.utils as utils - -import murano.dsl.exceptions as exceptions -import murano.dsl.murano_object as mo -import murano.policy.modify.actions.base as base - - -class ActionUtils(object): - def _get_objects_by_id(self, obj, objects=None): - if objects is None: - objects = {} - if isinstance(obj, mo.MuranoObject): - objects[obj.object_id] = obj - for prop_name in obj.type.properties: - try: - prop_val = obj.get_property(prop_name) - except exceptions.UninitializedPropertyAccessError: - continue - self._get_objects_by_id(prop_val, objects) - elif isinstance(obj, tuple): - for item in obj: - self._get_objects_by_id(item, objects) - elif isinstance(obj, utils.FrozenDict): - for k, v in obj.items(): - self._get_objects_by_id(k, objects) - self._get_objects_by_id(v, objects) - return objects - - @staticmethod - def _check_present(obj_id, objects): - if obj_id not in objects.keys(): - raise ValueError('No such object, obj_id: {0}' - .format(obj_id)) - - -class RemoveObjectAction(base.ModifyActionBase, ActionUtils): - """Remove object from given model""" - - def __init__(self, object_id): - """Initializes action parameters - - :param object_id: id of an object - """ - self._object_id = object_id - - @staticmethod - def _match_object_id(obj_id, obj): - return isinstance(obj, mo.MuranoObject) and obj_id == obj.object_id - - def modify(self, obj): - """Remove object from given model""" - objects = self._get_objects_by_id(obj) - self._check_present(self._object_id, objects) - - for _obj in objects.values(): - for prop_name in _obj.type.properties: - try: - val = _obj.get_property(prop_name) - except exceptions.UninitializedPropertyAccessError: - continue - if self._match_object_id(self._object_id, val): - _obj.set_property(prop_name, None) - # remove object from list - elif isinstance(val, tuple): - filtered_list = list(val) - for item in val: - if self._match_object_id(self._object_id, item): - filtered_list.remove(item) - if len(filtered_list) < len(val): - _obj.set_property(prop_name, tuple(filtered_list)) - # remove object from dict - elif isinstance(val, utils.FrozenDict): - filtered_dict = {k: v for k, v in val.items() if not - self._match_object_id(self._object_id, - k) and not - self._match_object_id(self._object_id, v)} - if len(filtered_dict) < len(val): - _obj.set_property(prop_name, - utils.FrozenDict(filtered_dict)) - - -class SetPropertyAction(base.ModifyActionBase, ActionUtils): - """Set property on given object""" - - def __init__(self, object_id, prop_name, value): - """Initializes action parameters - - :param object_id: id of an object - :param prop_name: property name - :param value: new value of the property - """ - self._object_id = object_id - self._prop_name = prop_name - self._value = value - - def modify(self, obj): - """Set property on given object in model""" - objects = self._get_objects_by_id(obj) - self._check_present(self._object_id, objects) - target_obj = objects[self._object_id] - target_obj.set_property(self._prop_name, self._value) - - -class RemoveRelationAction(SetPropertyAction): - """Remove relation from given model""" - - def __init__(self, object_id, prop_name): - super(RemoveRelationAction, self).__init__(object_id, prop_name, None) - - -class AddObjectAction(base.ModifyActionBase, ActionUtils): - """Add new object to object model""" - - def __init__(self, owner_id, owner_relation, type, init_args): - """Initializes action parameters - - :param owner_id: id of an owner - :param owner_relation: name of relation on owner - :param type: new object type - :param init_args: properties of the new object - """ - self._init_args = init_args - self._type = type - self._owner_relation = owner_relation - self._owner_id = owner_id - - def modify(self, model): - """Creates new object and adds it to the model""" - new_obj = {'type': type} - new_obj.update(self._init_args) - objects = self._get_objects_by_id(model) - self._check_present(self._owner_id, objects) - owner = objects[self._owner_id] - # adding objects to list or dict is not supported for now - owner.set_property(self._owner_relation, new_obj) - - -class AddRelationAction(base.ModifyActionBase, ActionUtils): - """Adds relation between two existing objects within model""" - - def __init__(self, source_id, relation, target_id): - """Initializes action parameters - - :param source_id: id of an relation source - :param relation: name of relation on source object - :param target_id: id of an relation target - """ - self._source_id = source_id - self._relation = relation - self._target_id = target_id - - def modify(self, model): - """Creates new object and adds it to the model""" - objects = self._get_objects_by_id(model) - self._check_present(self._source_id, objects) - self._check_present(self._target_id, objects) - source = objects[self._source_id] - target = objects[self._target_id] - # adding objects to list or dict is not supported for now - source.set_property(self._relation, target) diff --git a/murano/services/__init__.py b/murano/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/services/actions.py b/murano/services/actions.py deleted file mode 100644 index ae21b4463..000000000 --- a/murano/services/actions.py +++ /dev/null @@ -1,123 +0,0 @@ -# Licensed 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 murano.common import rpc -from murano.db import models -from murano.db.services import actions as actions_db -from murano.services import states - - -class ActionServices(object): - @staticmethod - def create_action_task(action_name, target_obj, - args, environment, session, context): - action = None - if action_name and target_obj: - action = { - 'object_id': target_obj, - 'method': action_name, - 'args': args or {} - } - task = { - 'action': action, - 'model': session.description, - 'token': context.auth_token, - 'project_id': context.project_id, - 'user_id': context.user, - 'id': environment.id - } - if session.description['Objects'] is not None: - task['model']['Objects']['?']['id'] = environment.id - task['model']['Objects']['applications'] = \ - task['model']['Objects'].pop('services', []) - - return task - - @staticmethod - def update_task(action, session, task, unit): - session.state = states.SessionState.DEPLOYING - task_info = models.Task() - task_info.environment_id = session.environment_id - task_info.description = dict(session.description.get('Objects')) - task_info.action = task['action'] - status = models.Status() - status.text = 'Action {0} is scheduled'.format(action[1]['name']) - status.level = 'info' - task_info.statuses.append(status) - with unit.begin(): - unit.add(session) - unit.add(task_info) - - @staticmethod - def submit_task(action_name, target_obj, - args, environment, session, context, unit): - task = ActionServices.create_action_task( - action_name, target_obj, args, - environment, session, context) - task_id = actions_db.update_task(action_name, session, task, unit) - rpc.engine().handle_task(task) - return task_id - - @staticmethod - def execute(action_id, session, unit, context, args=None): - if args is None: - args = {} - environment = actions_db.get_environment(session, unit) - action = ActionServices.find_action(session.description, action_id) - if action is None: - raise LookupError('Action is not found') - if not action[1].get('enabled', True): - raise ValueError('Cannot execute disabled action') - - return ActionServices.submit_task( - action[1]['name'], action[0], args, environment, - session, context, unit) - - @staticmethod - def find_action(model, action_id): - """Find_action for object def with specified action - - Traverses object model looking for an object definition - containing specified action - - :param model: object model - :param action_id: ID of an action - :return: tuple (object id, {"name": "action_name_in_MuranoPL", - "enabled": True }) - """ - if isinstance(model, list): - for item in model: - result = ActionServices.find_action(item, action_id) - if result is not None: - return result - elif isinstance(model, dict): - if '?' in model and 'id' in model['?'] and \ - '_actions' in model['?'] and \ - action_id in model['?']['_actions']: - return model['?']['id'], model['?']['_actions'][action_id] - - for obj in model.values(): - result = ActionServices.find_action(obj, action_id) - if result is not None: - return result - else: - return None - - @staticmethod - def get_result(environment_id, task_id, unit): - task = unit.query(models.Task).filter_by( - id=task_id, environment_id=environment_id).first() - - if task is not None: - return task.result - - return None diff --git a/murano/services/states.py b/murano/services/states.py deleted file mode 100644 index 96af385bb..000000000 --- a/murano/services/states.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 collections - -SessionState = collections.namedtuple('SessionState', [ - 'OPENED', 'DEPLOYING', 'DEPLOYED', 'DEPLOY_FAILURE', 'DELETING', - 'DELETE_FAILURE' -])( - OPENED='opened', - DEPLOYING='deploying', - DEPLOYED='deployed', - DEPLOY_FAILURE='deploy failure', - DELETING='deleting', - DELETE_FAILURE='delete failure' -) - -EnvironmentStatus = collections.namedtuple('EnvironmentStatus', [ - 'READY', 'PENDING', 'DEPLOYING', 'DEPLOY_FAILURE', 'DELETING', - 'DELETE_FAILURE' -])( - READY='ready', - PENDING='pending', - DEPLOYING='deploying', - DEPLOY_FAILURE='deploy failure', - DELETING='deleting', - DELETE_FAILURE='delete failure' - -) diff --git a/murano/services/static_actions.py b/murano/services/static_actions.py deleted file mode 100644 index 168e346e8..000000000 --- a/murano/services/static_actions.py +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed 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 uuid - -from murano.common import rpc - - -class StaticActionServices(object): - - @staticmethod - def execute(method_name, class_name, pkg_name, class_version, args, - credentials): - action = { - 'method': method_name, - 'args': args or {}, - 'class_name': class_name, - 'pkg_name': pkg_name, - 'class_version': class_version - } - task = { - 'action': action, - 'token': credentials['token'], - 'project_id': credentials['project_id'], - 'user_id': credentials['user_id'], - 'id': str(uuid.uuid4()) - } - return rpc.engine().call_static_action(task) diff --git a/murano/tests/__init__.py b/murano/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/functional/README.rst b/murano/tests/functional/README.rst deleted file mode 100644 index e9f9d933c..000000000 --- a/murano/tests/functional/README.rst +++ /dev/null @@ -1,6 +0,0 @@ -===== -MOVED -===== - -The Congress and Mistral functional integration tests has moved to -http://opendev.org/openstack/murano-tempest-plugin diff --git a/murano/tests/unit/__init__.py b/murano/tests/unit/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/__init__.py b/murano/tests/unit/api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/base.py b/murano/tests/unit/api/base.py deleted file mode 100644 index c6c9f32e5..000000000 --- a/murano/tests/unit/api/base.py +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 -from unittest import mock - -import fixtures -from oslo_config import cfg -from oslo_utils import timeutils -import routes -import urllib -import webob - -from murano.api.v1 import request_statistics -from murano.api.v1 import router -from murano.common import policy -from murano.common import rpc -from murano.common import wsgi -from murano.tests.unit import base -from murano.tests.unit import utils - -TEST_DEFAULT_LOGLEVELS = {'migrate': logging.WARN, 'sqlalchemy': logging.WARN} - - -def test_with_middleware(self, middleware, func, req, *args, **kwargs): - @webob.dec.wsgify - def _app(req): - return func(req, *args, **kwargs) - - resp = middleware(_app).process_request(req) - return resp - - -class FakeLogMixin(object): - """Allows logs to be tested - - Allow logs to be tested (rather than just disabling - logging. This is taken from heat - """ - def setup_logging(self): - # Assign default logs to self.LOG so we can still - # assert on heat logs. - self.LOG = self.useFixture( - fixtures.FakeLogger(level=logging.DEBUG)) - base_list = set([nlog.split('.')[0] - for nlog in logging.Logger.manager.loggerDict]) - for base_name in base_list: - if base_name in TEST_DEFAULT_LOGLEVELS: - self.useFixture(fixtures.FakeLogger( - level=TEST_DEFAULT_LOGLEVELS[base_name], - name=base_name)) - elif base_name != 'murano': - self.useFixture(fixtures.FakeLogger( - name=base_name)) - - -class MuranoApiTestCase(base.MuranoWithDBTestCase, FakeLogMixin): - # Set this if common.rpc is imported into other scopes so that - # it can be mocked properly - RPC_IMPORT = 'murano.common.rpc' - - def setUp(self): - super(MuranoApiTestCase, self).setUp() - - self.setup_logging() - - # Mock the RPC classes - self.mock_api_rpc = mock.Mock(rpc.ApiClient) - self.mock_engine_rpc = mock.Mock(rpc.EngineClient) - mock.patch(self.RPC_IMPORT + '.engine', - return_value=self.mock_engine_rpc).start() - mock.patch(self.RPC_IMPORT + '.api', - return_value=self.mock_api_rpc).start() - mock.patch('murano.db.services.environments.EnvironmentServices.' - 'get_network_driver', return_value='neutron').start() - - self.addCleanup(mock.patch.stopall) - - def tearDown(self): - super(MuranoApiTestCase, self).tearDown() - timeutils.utcnow.override_time = None - - def _stub_uuid(self, values=None): - class FakeUUID(object): - def __init__(self, v): - self.hex = v - - if values is None: - values = [] - mock_uuid4 = mock.patch('uuid.uuid4').start() - mock_uuid4.side_effect = [FakeUUID(v) for v in values] - return mock_uuid4 - - -class ControllerTest(object): - """Common utilities for testing API Controllers.""" - - DEFAULT_USER = 'test_user' - DEFAULT_TENANT = 'test_tenant' - - def __init__(self, *args, **kwargs): - super(ControllerTest, self).__init__(*args, **kwargs) - - # cfg.CONF.set_default('host', 'server.test') - self.api_version = '1.0' - self.tenant = 'test_tenant' - self.user = 'test_user' - self.mock_policy_check = None - self.mapper = routes.Mapper() - self.api = router.API(self.mapper) - - request_statistics.init_stats() - - def setUp(self): - super(ControllerTest, self).setUp() - - self.is_admin = False - cfg.CONF(args=[], project='murano') - policy.init(use_conf=False) - real_policy_check = policy.check - - self._policy_check_expectations = [] - self._actual_policy_checks = [] - - def wrap_policy_check(rule, ctxt, target=None, **kwargs): - if target is None: - target = {} - self._actual_policy_checks.append((rule, target)) - return real_policy_check(rule, ctxt, target=target, **kwargs) - - mock.patch('murano.common.policy.check', - side_effect=wrap_policy_check).start() - - # Deny everything - self._set_policy_rules({"default": "!"}) - - def _environ(self, path): - return { - 'SERVER_NAME': 'server.test', - 'SERVER_PORT': '8082', - 'SERVER_PROTOCOL': 'http', - 'SCRIPT_NAME': '/v1', - 'PATH_INFO': path, - 'wsgi.url_scheme': 'http', - 'QUERY_STRING': '', - 'CONTENT_TYPE': 'application/json', - } - - def _simple_request(self, path, params=None, method='GET', - user=DEFAULT_USER, tenant=DEFAULT_TENANT): - """Returns a request with a fake but valid-looking context - - Returns a request with a fake but valid-looking context - and sets the request environment variables. If `params` is given, - it should be a dictionary or sequence of tuples. - """ - environ = self._environ(path) - environ['REQUEST_METHOD'] = method - - if params: - qs = urllib.parse.urlencode(params) - environ['QUERY_STRING'] = qs - - req = wsgi.Request(environ) - req.context = utils.dummy_context(user, tenant, - is_admin=self.is_admin) - self.context = req.context - return req - - def _get(self, path, params=None, user=DEFAULT_USER, - tenant=DEFAULT_TENANT): - return self._simple_request(path, params=params, user=user, - tenant=tenant) - - def _get_with_accept(self, path, params=None, user=DEFAULT_USER, - tenant=DEFAULT_TENANT, - accept='application/octet-stream'): - req = self._simple_request(path, params=params, user=user, - tenant=tenant) - req.accept = accept - return req - - def _delete(self, path, params=None, user=DEFAULT_USER, - tenant=DEFAULT_TENANT): - if params is None: - params = {} - return self._simple_request(path, params=params, method='DELETE', - user=user, tenant=tenant) - - def _data_request(self, path, data, content_type='application/json', - method='POST', params=None, - user=DEFAULT_USER, tenant=DEFAULT_TENANT): - if params is None: - params = {} - environ = self._environ(path) - environ['REQUEST_METHOD'] = method - - req = wsgi.Request(environ) - req.context = utils.dummy_context(user, tenant, - is_admin=self.is_admin) - self.context = req.context - req.content_type = content_type - req.body = data - - if params: - qs = urllib.parse.urlencode(params) - environ['QUERY_STRING'] = qs - - return req - - def _post(self, path, data, content_type='application/json', params=None, - user=DEFAULT_USER, tenant=DEFAULT_TENANT): - if params is None: - params = {} - return self._data_request(path, data, content_type, params=params, - user=user, tenant=tenant) - - def _put(self, path, data, content_type='application/json', params=None, - user=DEFAULT_USER, tenant=DEFAULT_TENANT): - if params is None: - params = {} - return self._data_request(path, data, content_type, method='PUT', - params=params, user=user, tenant=tenant) - - def _patch(self, path, data, - content_type='application/murano-packages-json-patch', - params=None, user=DEFAULT_USER, tenant=DEFAULT_TENANT): - if params is None: - params = {} - return self._data_request(path, data, content_type, method='PATCH', - params=params, user=user, tenant=tenant) - - def _set_policy_rules(self, rules): - policy.set_rules(rules, default_rule='default') - - def expect_policy_check(self, action, target=None): - if target is None: - target = {} - self._policy_check_expectations.append((action, target)) - - def _assert_policy_checks(self): - self.assertEqual(self._policy_check_expectations, - self._actual_policy_checks) - - def tearDown(self): - self._assert_policy_checks() - policy.reset() - super(ControllerTest, self).tearDown() diff --git a/murano/tests/unit/api/cmd/__init__.py b/murano/tests/unit/api/cmd/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/cmd/test_package/Classes/Mytest1.yaml b/murano/tests/unit/api/cmd/test_package/Classes/Mytest1.yaml deleted file mode 100644 index 9d3236441..000000000 --- a/murano/tests/unit/api/cmd/test_package/Classes/Mytest1.yaml +++ /dev/null @@ -1,19 +0,0 @@ -Namespaces: - =: io.murano.test - -Extends: TestFixture - -Name: MyTest1 - -Methods: - setUp: - Body: - - testSimple1: - Body: - - testSimple2: - Body: - - tearDown: - Body: \ No newline at end of file diff --git a/murano/tests/unit/api/cmd/test_package/Classes/Mytest2.yaml b/murano/tests/unit/api/cmd/test_package/Classes/Mytest2.yaml deleted file mode 100644 index c2660252c..000000000 --- a/murano/tests/unit/api/cmd/test_package/Classes/Mytest2.yaml +++ /dev/null @@ -1,15 +0,0 @@ -Namespaces: - =: io.murano.test - -Extends: TestFixture - -Name: MyTest2 - -Methods: - testSimple1: - Body: - - testSimple2: - Body: - - thisIsNotAtestMethod: diff --git a/murano/tests/unit/api/cmd/test_package/Classes/Mytest3.yaml b/murano/tests/unit/api/cmd/test_package/Classes/Mytest3.yaml deleted file mode 100644 index 6040eac51..000000000 --- a/murano/tests/unit/api/cmd/test_package/Classes/Mytest3.yaml +++ /dev/null @@ -1,13 +0,0 @@ -Namespaces: - =: io.murano.test - -Name: MyTest3 - -Methods: - testSimple1: - Body: - - testSimple2: - Body: - - thisIsNotAtestMethod: diff --git a/murano/tests/unit/api/cmd/test_package/manifest.yaml b/murano/tests/unit/api/cmd/test_package/manifest.yaml deleted file mode 100644 index dc3a915cb..000000000 --- a/murano/tests/unit/api/cmd/test_package/manifest.yaml +++ /dev/null @@ -1,12 +0,0 @@ -Format: 1.0 -Type: Application -FullName: io.murano.test.MyTest1 -Name: Test case Example -Description: | - Example of test-case. Mocks are not used in this app -Author: 'Mirantis, Inc' -Tags: [] -Classes: - io.murano.test.MyTest1: Mytest1.yaml - io.murano.test.MyTest2: Mytest2.yaml - io.murano.test.MyTest3: Mytest3.yaml \ No newline at end of file diff --git a/murano/tests/unit/api/cmd/test_test_runner.py b/murano/tests/unit/api/cmd/test_test_runner.py deleted file mode 100644 index 74a164c3f..000000000 --- a/murano/tests/unit/api/cmd/test_test_runner.py +++ /dev/null @@ -1,171 +0,0 @@ -# Licensed 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 io import StringIO -import os -import sys -from unittest import mock - -import fixtures -from oslo_config import cfg -from oslo_log import log as logging -from oslo_utils import importutils -import testtools - -from murano.cmd import test_runner -from murano import version - -CONF = cfg.CONF -logging.register_options(CONF) -logging.setup(CONF, 'murano') - - -class TestCaseShell(testtools.TestCase): - def setUp(self): - super(TestCaseShell, self).setUp() - self.auth_params = {'username': 'test', - 'password': 'test', - 'project_name': 'test', - 'auth_url': 'http://localhost:5000'} - self.args = ['test-runner.py'] - for k, v in self.auth_params.items(): - k = '--os-' + k.replace('_', '-') - self.args.extend([k, v]) - - self.useFixture(fixtures.MonkeyPatch('keystoneclient.v3.client.Client', - mock.MagicMock)) - dirs = [os.path.dirname(__file__), - os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, - os.pardir, os.pardir, os.pardir, 'meta')] - self.override_config('load_packages_from', dirs, 'engine') - - def tearDown(self): - super(TestCaseShell, self).tearDown() - CONF.clear() - - def override_config(self, name, override, group=None): - CONF.set_override(name, override, group) - CONF.set_override('use_stderr', True) - self.addCleanup(CONF.clear_override, name, group) - - def shell(self, cmd_args=None, exitcode=0): - stdout = StringIO() - stderr = StringIO() - args = self.args - if cmd_args: - cmd_args = cmd_args.split() - args.extend(cmd_args) - with mock.patch.object(sys, 'stdout', stdout): - with mock.patch.object(sys, 'stderr', stderr): - with mock.patch.object(sys, 'argv', args): - result = self.assertRaises(SystemExit, test_runner.main) - self.assertEqual(result.code, exitcode, - 'Command finished with error.') - stdout = stdout.getvalue() - stderr = stderr.getvalue() - return (stdout, stderr) - - def test_help(self): - stdout, _ = self.shell('--help') - self.assertIn('', stdout) - self.assertIn('className.testMethod', stdout) - - def test_version(self): - stdout, stderr = self.shell('--version') - output = stdout - self.assertIn(version.version_string, output) - - @mock.patch.object(test_runner, 'LOG') - def test_increase_verbosity(self, mock_log): - self.shell('io.murano.test.MyTest1 -v') - mock_log.logger.setLevel.assert_called_with(logging.DEBUG) - - @mock.patch('keystoneclient.v3.client.Client') - def test_os_params_replaces_config(self, mock_client): - # Load keystone configuration parameters from config - importutils.import_module('keystonemiddleware.auth_token') - self.override_config('admin_user', 'new_value', 'keystone_authtoken') - - self.shell('io.murano.test.MyTest1 io.murano.test.MyTest2') - - mock_client.assert_has_calls([mock.call(**self.auth_params)]) - - def test_package_all_tests(self): - _, stderr = self.shell('io.murano.test.MyTest1 -v') - # NOTE(efedorova): May be, there is a problem with test-runner, since - # all logs are passed to stderr - self.assertIn('Test io.murano.test.MyTest1.testSimple1 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest1.testSimple2 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple1 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple2 successful', - stderr) - self.assertNotIn('thisIsNotAtestMethod', stderr) - - def test_package_by_class(self): - _, stderr = self.shell( - 'io.murano.test.MyTest1 io.murano.test.MyTest2 -v') - - self.assertNotIn('Test io.murano.test.MyTest1.testSimple1 successful', - stderr) - self.assertNotIn('Test io.murano.test.MyTest1.testSimple2 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple1 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple2 successful', - stderr) - - def test_package_by_test_name(self): - _, stderr = self.shell( - 'io.murano.test.MyTest1 testSimple1 -v') - - self.assertIn('Test io.murano.test.MyTest1.testSimple1 successful', - stderr) - self.assertNotIn('Test io.murano.test.MyTest1.testSimple2 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple1 successful', - stderr) - self.assertNotIn('Test io.murano.test.MyTest2.testSimple2 successful', - stderr) - - def test_package_by_test_and_class_name(self): - _, stderr = self.shell( - 'io.murano.test.MyTest1 io.murano.test.MyTest2.testSimple1 -v') - - self.assertNotIn('Test io.murano.test.MyTest1.testSimple1 successful', - stderr) - self.assertNotIn('Test io.murano.test.MyTest1.testSimple2 successful', - stderr) - self.assertIn('Test io.murano.test.MyTest2.testSimple1 successful', - stderr) - self.assertNotIn('Test io.murano.test.MyTest2.testSimple2 successful', - stderr) - - def test_service_methods(self): - _, stderr = self.shell( - 'io.murano.test.MyTest1 io.murano.test.MyTest1.testSimple1 -v') - self.assertIn('Executing: io.murano.test.MyTest1.setUp', stderr) - self.assertIn('Executing: io.murano.test.MyTest1.tearDown', stderr) - - def test_package_is_not_provided(self): - _, stderr = self.shell(exitcode=2) - err = 'the following arguments are required: ' - self.assertIn('murano-test-runner: error: %s' % err, stderr) - - def test_wrong_parent(self): - _, stderr = self.shell( - 'io.murano.test.MyTest1 io.murano.test.MyTest3 -v', exitcode=1) - self.assertIn('Class io.murano.test.MyTest3 is not inherited from' - ' io.murano.test.TestFixture. Skipping it.', stderr) - self.assertIn('No tests found for execution.', stderr) diff --git a/murano/tests/unit/api/middleware/__init__.py b/murano/tests/unit/api/middleware/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/middleware/test_context.py b/murano/tests/unit/api/middleware/test_context.py deleted file mode 100644 index 230167b25..000000000 --- a/murano/tests/unit/api/middleware/test_context.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 webob - -from murano.api.middleware import context -from murano.tests.unit import base - -from oslo_config import cfg - -CONF = cfg.CONF - - -class MiddlewareContextTest(base.MuranoTestCase): - - def test_middleware_context_default(self): - middleware = context.ContextMiddleware(None) - request_headers = { - 'X-Roles': 'admin', - 'X-User-Id': "", - 'X-Project-Id': "", - 'X-Configuration-Session': "", - } - request = webob.Request.blank('/environments', - headers=request_headers) - self.assertFalse(hasattr(request, 'context')) - middleware.process_request(request) - self.assertTrue(hasattr(request, 'context')) - - def test_factory_returns_filter(self): - middleware = context.ContextMiddleware(None) - result = middleware.factory(CONF) - self.assertIsNotNone(result) diff --git a/murano/tests/unit/api/middleware/test_ext_context.py b/murano/tests/unit/api/middleware/test_ext_context.py deleted file mode 100644 index ddf747f0a..000000000 --- a/murano/tests/unit/api/middleware/test_ext_context.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -import webob - -from keystoneauth1 import exceptions - -from murano.api.middleware import ext_context -from murano.tests.unit import base - -from oslo_serialization import base64 - - -class MiddlewareExtContextTest(base.MuranoTestCase): - - def test_middleware_ext_context_default(self): - middleware = ext_context.ExternalContextMiddleware(None) - middleware.get_keystone_token = mock.MagicMock(return_value="token?") - auth = 'Basic {}'.format(base64.encode_as_text(b'test:test')) - request_headers = { - 'Authorization': auth, - } - request = webob.Request.blank('/environments', - headers=request_headers) - middleware.process_request(request) - self.assertEqual(request.headers.get('X-Auth-Token'), "token?") - - def test_middleware_ext_context_except_key_error(self): - middleware = ext_context.ExternalContextMiddleware(None) - middleware.get_keystone_token = mock.MagicMock( - side_effect=KeyError('test key error') - ) - auth = 'Basic {}'.format(base64.encode_as_text(b'test:test')) - request_headers = { - 'Authorization': auth, - } - request = webob.Request.blank('/environments', - headers=request_headers) - self.assertRaises(webob.exc.HTTPUnauthorized, - middleware.process_request, request) - - def test_middleware_ext_context_except_unauthorized(self): - middleware = ext_context.ExternalContextMiddleware(None) - middleware.get_keystone_token = mock.MagicMock( - side_effect=exceptions.Unauthorized('') - ) - auth = 'Basic {}'.format(base64.encode_as_text(b'test:test')) - request_headers = { - 'Authorization': auth, - } - request = webob.Request.blank('/environments', - headers=request_headers) - self.assertRaises(webob.exc.HTTPUnauthorized, - middleware.process_request, request) - - @mock.patch('murano.api.middleware.ext_context.ks_session') - @mock.patch('murano.api.middleware.ext_context.v3') - @mock.patch('murano.api.middleware.ext_context.CONF') - def test_get_keystone_token(self, mock_conf, mock_v3, mock_ks_session): - mock_ks_session.Session().get_token.return_value = 'test_token' - test_auth_urls = ['test_url', 'test_url/v2.0', 'test_url/v3'] - - for url in test_auth_urls: - mock_conf.cfapi = mock.MagicMock(auth_url=url, - tenant='test_tenant', - user_domain_name='test_udn', - project_domain_name='test_pdn') - - middleware = ext_context.ExternalContextMiddleware(None) - token = middleware.get_keystone_token('test_user', 'test_password') - self.assertEqual('test_token', token) - - expected_kwargs = { - 'auth_url': 'test_url/v3', - 'username': 'test_user', - 'password': 'test_password', - 'project_name': mock_conf.cfapi.tenant, - 'user_domain_name': mock_conf.cfapi.user_domain_name, - 'project_domain_name': mock_conf.cfapi.project_domain_name - } - mock_v3.Password.assert_called_once_with(**expected_kwargs) - mock_v3.Password.reset_mock() - - def test_query_endpoints_except_endpoint_not_found(self): - middleware = ext_context.ExternalContextMiddleware(None) - if hasattr(middleware, '_murano_endpoint'): - setattr(middleware, '_murano_endpoint', None) - mock_auth = mock.MagicMock() - mock_auth.get_endpoint.side_effect = exceptions.EndpointNotFound - - middleware._query_endpoints(mock_auth, 'test_session') - mock_auth.get_endpoint.assert_any_call('test_session', - 'application-catalog') - - if hasattr(middleware, '_glare_endpoint'): - setattr(middleware, '_glare_endpoint', None) - setattr(middleware, '_murano_endpoint', 'test_endpoint') - - middleware._query_endpoints(mock_auth, 'test_session') - mock_auth.get_endpoint.assert_any_call('test_session', - 'artifact') diff --git a/murano/tests/unit/api/middleware/test_fault_wrapper.py b/murano/tests/unit/api/middleware/test_fault_wrapper.py deleted file mode 100644 index db23e4c3e..000000000 --- a/murano/tests/unit/api/middleware/test_fault_wrapper.py +++ /dev/null @@ -1,101 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from murano.api.middleware import fault -from murano.common import wsgi -from murano.packages import exceptions -from murano.tests.unit import base - -from oslo_serialization import jsonutils - -from webob import exc - - -class FaultWrapperTest(base.MuranoTestCase): - @mock.patch('traceback.format_exc') - def test_error_500(self, mock_trace): - mock_trace.return_value = "test trace" - fault_wrapper = fault.FaultWrapper(None) - result = fault_wrapper._error(exc.HTTPInternalServerError()) - self.assertEqual(result['code'], 500) - self.assertEqual(result['explanation'], - 'The server has either erred or is incapable' - ' of performing the requested operation.') - - @mock.patch('traceback.format_exc') - def test_error_value_error(self, mock_trace): - mock_trace.return_value = "test trace" - fault_wrapper = fault.FaultWrapper(None) - exception = exceptions.PackageClassLoadError("test") - exception.message = "Unable to load class 'test' from package" - result = fault_wrapper._error(exception) - self.assertEqual(result['code'], 400) - self.assertEqual(result['error']['message'], - "Unable to load class 'test' from package") - - @mock.patch('traceback.format_exc') - def test_fault_wrapper(self, mock_trace): - mock_trace.return_value = "test trace" - fault_wrapper = fault.FaultWrapper(None) - exception_disguise = fault.HTTPExceptionDisguise( - exc.HTTPInternalServerError()) - result = fault_wrapper._error(exception_disguise) - self.assertEqual(result['code'], 500) - self.assertEqual(result['explanation'], - 'The server has either erred or is incapable' - ' of performing the requested operation.') - - def test_process_request(self): - fault_wrapper = fault.FaultWrapper(None) - environ = { - 'SERVER_NAME': 'server.test', - 'SERVER_PORT': '8082', - 'SERVER_PROTOCOL': 'http', - 'SCRIPT_NAME': '/', - 'PATH_INFO': '/asdf/asdf/asdf/asdf', - 'wsgi.url_scheme': 'http', - 'QUERY_STRING': '', - 'CONTENT_TYPE': 'application/json', - 'REQUEST_METHOD': 'HEAD' - } - req = wsgi.Request(environ) - req.get_response = mock.MagicMock(side_effect=exc. - HTTPInternalServerError()) - self.assertRaises(exc.HTTPInternalServerError, - fault_wrapper.process_request, req) - - @mock.patch('traceback.format_exc') - def test_fault_call(self, mock_trace): - mock_trace.return_value = "test trace" - fault_wrapper = fault.FaultWrapper(None) - exception = exceptions.PackageClassLoadError("test") - exception.message = "Unable to load class 'test' from package" - test_fault = fault.Fault(fault_wrapper._error(exception)) - environ = { - 'SERVER_NAME': 'server.test', - 'SERVER_PORT': '8082', - 'SERVER_PROTOCOL': 'http', - 'SCRIPT_NAME': '/', - 'PATH_INFO': '/', - 'wsgi.url_scheme': 'http', - 'QUERY_STRING': '', - 'CONTENT_TYPE': 'application/json', - 'REQUEST_METHOD': 'HEAD' - } - req = wsgi.Request(environ) - response = jsonutils.loads(test_fault(req).body) - self.assertEqual(response['code'], 400) diff --git a/murano/tests/unit/api/middleware/test_version_negotiation.py b/murano/tests/unit/api/middleware/test_version_negotiation.py deleted file mode 100644 index 95cf9acdf..000000000 --- a/murano/tests/unit/api/middleware/test_version_negotiation.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -import webob - -from murano.api.middleware import version_negotiation -from murano.api import versions -from murano.tests.unit import base - - -class MiddlewareVersionNegotiationTest(base.MuranoTestCase): - - @mock.patch.object(version_negotiation, 'LOG') - def test_middleware_version_negotiation_default(self, mock_log): - middleware_vn = version_negotiation.VersionNegotiationFilter(None) - request = webob.Request.blank('/environments') - - result = middleware_vn.process_request(request) - - self.assertIsInstance(result, versions.Controller) - mock_log.warning.assert_called_once_with( - "Unknown version. Returning version choices.") - - @mock.patch.object(version_negotiation, 'LOG') - def test_process_request(self, mock_log): - """Test process_request using different valid paths.""" - middleware_vn = version_negotiation.VersionNegotiationFilter(None) - - for path in ('v1', '/v1', '///v1'): - request = webob.Request.blank(path) - request.method = 'GET' - request.environ = {} - - result = middleware_vn.process_request(request) - - self.assertIsNone(result) - self.assertIn('api.version', request.environ) - self.assertEqual(1, request.environ['api.version']) - self.assertEqual('/v1', request.path_info) - mock_log.debug.assert_any_call( - "Matched version: v{version}".format(version=1)) - mock_log.debug.assert_any_call( - 'new path {path}'.format(path='/v1')) - - request = webob.Request.blank('/v1/') - request.method = 'GET' - request.environ = {} - - result = middleware_vn.process_request(request) - - self.assertIsNone(result) - self.assertIn('api.version', request.environ) - self.assertEqual(1, request.environ['api.version']) - self.assertEqual('/v1/', request.path_info) - mock_log.debug.assert_any_call( - "Matched version: v{version}".format(version=1)) - mock_log.debug.assert_any_call( - 'new path {path}'.format(path='/v1/')) - - @mock.patch.object(version_negotiation, 'LOG') - def test_process_request_without_path(self, mock_log): - middleware_vn = version_negotiation.VersionNegotiationFilter(None) - - request = webob.Request.blank('') - request.method = 'GET' - request.environ = {} - - result = middleware_vn.process_request(request) - - self.assertIsInstance(result, versions.Controller) - mock_log.warning.assert_called_once_with( - "Unknown version. Returning version choices.") - - def test_factory(self): - app = version_negotiation.VersionNegotiationFilter.factory(None) - self.assertIsNotNone(app) - self.assertEqual( - version_negotiation.VersionNegotiationFilter, type(app(None))) diff --git a/murano/tests/unit/api/v1/__init__.py b/murano/tests/unit/api/v1/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/v1/cloudfoundry/__init__.py b/murano/tests/unit/api/v1/cloudfoundry/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/api/v1/cloudfoundry/test_cfapi.py b/murano/tests/unit/api/v1/cloudfoundry/test_cfapi.py deleted file mode 100644 index d63eafd01..000000000 --- a/murano/tests/unit/api/v1/cloudfoundry/test_cfapi.py +++ /dev/null @@ -1,546 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# Copyright 2017 AT&T Corporation -# -# Licensed 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 json -from unittest import mock - -from oslo_serialization import base64 -from webob import response - -from murano.cfapi import cfapi as api -from murano.common import wsgi -from murano.db import models -from murano.tests.unit import base - -from muranoclient.common import exceptions - - -class TestController(base.MuranoTestCase): - def setUp(self): - super(TestController, self).setUp() - self.controller = api.Controller() - - self.request = mock.MagicMock() - auth = 'Basic {}'.format(base64.encode_as_text(b'test:test')) - self.request.headers = {'Authorization': auth, - 'X-Auth-Token': 'foo-bar', - 'X-Project-Id': 'bar-baz'} - self.addCleanup(mock.patch.stopall) - - @mock.patch('murano.common.policy.check_is_admin') - @mock.patch('murano.cfapi.cfapi._get_muranoclient') - def test_list(self, mock_client, mock_policy): - - pkg0 = mock.MagicMock() - pkg0.id = 'xxx' - pkg0.name = 'foo' - pkg0.description = 'stub pkg' - - mock_client.return_value.packages.filter =\ - mock.MagicMock(return_value=[pkg0]) - - answer = {'services': [{'bindable': True, - 'description': pkg0.description, - 'id': pkg0.id, - 'name': pkg0.name, - 'plans': [{'description': ('Default plan for ' - 'the service ' - '{name}').format( - name=pkg0.name), - 'id': 'xxx-1', - 'name': 'default'}], - 'tags': []}]} - - resp = self.controller.list(self.request) - self.assertEqual(answer, resp) - - @mock.patch('murano.common.policy.check_is_admin') - @mock.patch('murano.cfapi.cfapi._get_muranoclient') - @mock.patch('murano.db.services.cf_connections.set_instance_for_service') - @mock.patch('murano.db.services.cf_connections.get_environment_for_space') - @mock.patch('murano.db.services.cf_connections.get_tenant_for_org') - def test_provision_from_scratch(self, mock_get_tenant, - mock_get_environment, mock_is, mock_client, - mock_policy): - - body = {"space_guid": "s1-p1", - "organization_guid": "o1-r1", - "plan_id": "p1-l1", - "service_id": "s1-e1", - "parameters": {'some_parameter': 'value', - '?': {}}} - self.request.body = json.dumps(body) - - mock_get_environment.return_value = '555-555' - mock_client.return_value = mock.MagicMock() - - resp = self.controller.provision(self.request, {}, '111-111') - - self.assertIsInstance(resp, response.Response) - - @mock.patch('murano.common.policy.check_is_admin') - @mock.patch('murano.cfapi.cfapi._get_muranoclient') - @mock.patch('murano.db.services.cf_connections.set_instance_for_service') - @mock.patch('murano.db.services.cf_connections.set_environment_for_space') - @mock.patch('murano.db.services.cf_connections.set_tenant_for_org') - @mock.patch('murano.db.services.cf_connections.get_environment_for_space') - @mock.patch('murano.db.services.cf_connections.get_tenant_for_org') - def test_provision_existent(self, mock_get_tenant, - mock_get_environment, mock_set_tenant, - mock_set_environment, mock_is, mock_client, - mock_policy): - - body = {"space_guid": "s1-p1", - "organization_guid": "o1-r1", - "plan_id": "p1-l1", - "service_id": "s1-e1", - "parameters": {'some_parameter': 'value', - '?': {}}} - self.request.body = json.dumps(body) - - mock_package = mock_client.return_value.packages.get - mock_package.return_value = mock.MagicMock() - mock_get_environment.side_effect = AttributeError - mock_get_tenant.side_effect = AttributeError - - resp = self.controller.provision(self.request, {}, '111-111') - - self.assertIsInstance(resp, response.Response) - - @mock.patch('murano.cfapi.cfapi._get_muranoclient') - @mock.patch('murano.db.services.cf_connections.get_service_for_instance') - def test_deprovision(self, mock_get_si, mock_client): - service = mock.MagicMock() - service.service_id = '111-111' - service.tenant_id = '222-222' - service.env_id = '333-333' - mock_get_si.return_value = service - - resp = self.controller.deprovision(self.request, '555-555') - - self.assertIsInstance(resp, response.Response) - - @mock.patch('murano.cfapi.cfapi._get_muranoclient') - @mock.patch('murano.db.services.cf_connections.get_service_for_instance') - def test_bind(self, mock_get_si, mock_client): - service = mock.MagicMock() - service.service_id = '111-111' - service.tenant_id = '222-222' - service.env_id = '333-333' - mock_get_si.return_value = service - - services = [{'id': 'xxx-xxx-xxx', - '?': {'id': '111-111', - '_actions': { - 'dafsa_getCredentials': { - 'name': 'getCredentials'}}}, - 'instance': {}}] - mock_client.return_value.environments.get =\ - mock.MagicMock(return_value=mock.MagicMock(services=services)) - - mock_client.return_value.actions.get_result =\ - mock.MagicMock(return_value={'result': {'smthg': 'nothing'}}) - nice_resp = {'credentials': {'smthg': 'nothing'}} - resp = self.controller.bind(self.request, {}, '555-555', '666-666') - - self.assertEqual(nice_resp, resp) - - def test_package_to_service(self): - mock_package = mock.Mock( - spec_set=models.Package, id='foo_package_id', - description='a' * 257, tags=[mock.sentinel.package_tag]) - mock_package.configure_mock(name=mock.sentinel.package_name) - - expected_service = { - 'id': 'foo_package_id', - 'name': mock.sentinel.package_name, - 'description': 'a' * 253 + ' ...', - 'bindable': True, - 'tags': [mock.sentinel.package_tag], - 'plans': [{ - 'id': 'foo_package_id-1', - 'name': 'default', - 'description': - 'Default plan for the service sentinel.package_name' - }] - } - service = self.controller._package_to_service(mock_package) - - for key, val in expected_service.items(): - if key != 'plans': - self.assertEqual(val, service[key]) - else: - self.assertEqual(1, len(service['plans'])) - for plan_key, plan_val in expected_service['plans'][0].items(): - self.assertEqual(plan_val, service['plans'][0][plan_key]) - - def test_get_service_return_none(self): - mock_env = mock.Mock(services=[]) - self.assertIsNone(self.controller._get_service(mock_env, None)) - - @mock.patch.object(api, 'LOG', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - @mock.patch.object(api, '_get_muranoclient', autospec=True) - def test_provision_except_http_not_found(self, mock_get_muranoclient, - mock_db_cf, mock_log): - test_body = { - 'space_guid': 'foo_space_guid', - 'organization_guid': 'foo_organization_guid', - 'plan_id': 'foo_plan_id', - 'service_id': 'foo_service_id', - 'parameters': {} - } - test_headers = { - 'X-Auth-Token': mock.sentinel.token - } - mock_request = mock.Mock(body=json.dumps(test_body), - headers=test_headers) - mock_muranoclient = mock.Mock() - mock_muranoclient.environments.get.side_effect = \ - exceptions.HTTPNotFound - mock_muranoclient.environments.create.return_value = \ - mock.Mock(id=mock.sentinel.alt_environment_id) - mock_get_muranoclient.return_value = mock_muranoclient - mock_db_cf.get_environment_for_space.return_value = \ - mock.sentinel.environment_id - - resp = self.controller.provision(mock_request, None, None) - - self.assertEqual(202, resp.status_code) - self.assertEqual({}, resp.json_body) - mock_db_cf.get_environment_for_space.assert_called_once_with( - 'foo_space_guid') - mock_muranoclient.environments.get.assert_called_once_with( - mock.sentinel.environment_id) - mock_log.info.assert_has_calls([ - mock.call("Can not find environment_id sentinel.environment_id" - ", will create a new one."), - mock.call("Cloud Foundry foo_space_guid remapped to " - "sentinel.alt_environment_id") - ]) - - @mock.patch.object(api, 'uuid', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - @mock.patch.object(api, '_get_muranoclient', autospec=True) - def test_provision_with_dict_parameters(self, mock_get_muranoclient, - mock_db_cf, mock_uuid): - test_body = { - 'space_guid': 'foo_space_guid', - 'organization_guid': 'foo_organization_guid', - 'plan_id': 'foo_plan_id', - 'service_id': 'foo_service_id', - 'parameters': { - 'foo': { - '?': { - 'bar': 'baz' - } - } - } - } - test_headers = { - 'X-Auth-Token': mock.sentinel.token - } - mock_request = mock.Mock(body=json.dumps(test_body), - headers=test_headers) - mock_package = mock_get_muranoclient.return_value.packages.get.\ - return_value - mock_service = mock.MagicMock() - self.controller._make_service = mock.Mock(return_value=mock_service) - mock_uuid.uuid4.return_value.hex = mock.sentinel.uuid - - resp = self.controller.provision(mock_request, None, None) - - self.assertEqual(202, resp.status_code) - self.assertEqual({}, resp.json_body) - self.controller._make_service.assert_called_once_with( - 'foo_space_guid', mock_package, 'foo_plan_id') - mock_service.update.assert_called_once_with( - {'foo': {'?': {'bar': 'baz', 'id': mock.sentinel.uuid}}}) - - @mock.patch.object(api, 'db_cf', autospec=True) - def test_deprovision_with_missing_service(self, mock_db_cf): - mock_db_cf.get_service_for_instance.return_value = None - self.assertEqual({}, self.controller.deprovision(None, None)) - mock_db_cf.get_service_for_instance.assert_called_once_with(None) - - @mock.patch.object(api, 'db_cf', autospec=True) - def test_bind_with_missing_service(self, mock_db_cf): - mock_db_cf.get_service_for_instance.return_value = None - self.assertEqual({}, self.controller.bind(None, None, None, None)) - mock_db_cf.get_service_for_instance.assert_called_once_with(None) - - @mock.patch.object(api, 'LOG', autospec=True) - @mock.patch.object(api.Controller, '_get_service', autospec=True) - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_bind_except_key_error(self, mock_db_cf, mock_get_muranoclient, - mock_get_service, mock_log): - mock_db_service = mock.Mock( - service_id=mock.sentinel.service_id, - environment_id=mock.sentinel.environment_id) - test_service = {} - test_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - - mock_db_cf.get_service_for_instance.return_value = mock_db_service - mock_get_service.return_value = test_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - - resp = self.controller.bind(test_request, None, None, None) - - self.assertEqual(500, resp.status_code) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id, mock.ANY) - mock_get_service.assert_called_once_with(self.controller, m_env, - mock.sentinel.service_id) - mock_log.warning.assert_called_once_with( - "This application doesn't have actions at all") - - @mock.patch.object(api, 'LOG', autospec=True) - @mock.patch.object(api.Controller, '_get_service', autospec=True) - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_bind_without_get_credentials(self, mock_db_cf, - mock_get_muranoclient, - mock_get_service, mock_log): - mock_db_service = mock.Mock( - service_id=mock.sentinel.service_id, - environment_id=mock.sentinel.environment_id) - test_service = {'?': {'_actions': []}} - test_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - - mock_db_cf.get_service_for_instance.return_value = mock_db_service - mock_get_service.return_value = test_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - - resp = self.controller.bind(test_request, None, None, None) - - self.assertEqual(500, resp.status_code) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id, mock.ANY) - mock_get_service.assert_called_once_with(self.controller, m_env, - mock.sentinel.service_id) - mock_log.warning.assert_called_once_with( - "This application doesn't have action getCredentials") - - def test_unbind(self): - self.assertEqual({}, self.controller.unbind(None, None, None)) - - @mock.patch.object(api, 'LOG', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_get_last_operation_without_service(self, mock_db_cf, mock_log): - mock_db_cf.get_service_for_instance.return_value = None - - resp = self.controller.get_last_operation( - None, mock.sentinel.instance_id) - - self.assertEqual(410, resp.status_code) - self.assertEqual({}, resp.json_body) - mock_log.warning.assert_called_once_with( - 'Requested service for instance sentinel.instance_id is not ' - 'found') - - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_get_last_operation_succeeded(self, mock_db_cf, - mock_get_muranoclient): - mock_service = mock.Mock( - environment_id=mock.sentinel.environment_id) - mock_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - mock_db_cf.get_service_for_instance.return_value = mock_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - m_env.status = 'ready' - - resp = self.controller.get_last_operation( - mock_request, mock.sentinel.instance_id) - - self.assertEqual(200, resp.status_code) - self.assertEqual( - {'state': 'succeeded', 'description': 'operation succeed'}, - resp.json_body) - mock_get_muranoclient.assert_called_once_with(mock.sentinel.auth_token, - mock_request) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id) - - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_get_last_operation_in_progress(self, mock_db_cf, - mock_get_muranoclient): - mock_service = mock.Mock( - environment_id=mock.sentinel.environment_id) - mock_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - mock_db_cf.get_service_for_instance.return_value = mock_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - - progress_statuses = ['pending', 'deleting', 'deploying'] - for status in progress_statuses: - m_env.status = status - - resp = self.controller.get_last_operation( - mock_request, mock.sentinel.instance_id) - - self.assertEqual(202, resp.status_code) - self.assertEqual( - {'state': 'in progress', - 'description': 'operation in progress'}, - resp.json_body) - mock_get_muranoclient.assert_called_once_with( - mock.sentinel.auth_token, mock_request) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id) - mock_get_muranoclient.reset_mock() - m_cli.environments.get.reset_mock() - - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_get_last_operation_failed(self, mock_db_cf, - mock_get_muranoclient): - mock_service = mock.Mock( - environment_id=mock.sentinel.environment_id) - mock_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - mock_db_cf.get_service_for_instance.return_value = mock_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - - failed_statuses = ['deploy failure', 'delete failure'] - for status in failed_statuses: - m_env.status = status - - resp = self.controller.get_last_operation( - mock_request, mock.sentinel.instance_id) - - self.assertEqual(200, resp.status_code) - self.assertEqual( - {'state': 'failed', - 'description': '{0}. Please correct it manually' - .format(status)}, - resp.json_body) - mock_get_muranoclient.assert_called_once_with( - mock.sentinel.auth_token, mock_request) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id) - mock_get_muranoclient.reset_mock() - m_cli.environments.get.reset_mock() - - @mock.patch.object(api, '_get_muranoclient', autospec=True) - @mock.patch.object(api, 'db_cf', autospec=True) - def test_get_last_operation_unknown_status(self, mock_db_cf, - mock_get_muranoclient): - mock_service = mock.Mock( - environment_id=mock.sentinel.environment_id) - mock_request = mock.Mock( - headers={'X-Auth-Token': mock.sentinel.auth_token}) - mock_db_cf.get_service_for_instance.return_value = mock_service - - m_cli = mock_get_muranoclient.return_value - m_env = m_cli.environments.get.return_value - - m_env.status = 'unknown' - - resp = self.controller.get_last_operation( - mock_request, mock.sentinel.instance_id) - - self.assertEqual(500, resp.status_code) - self.assertEqual( - {'state': 'unknown', 'description': 'operation unknown'}, - resp.json_body) - mock_get_muranoclient.assert_called_once_with( - mock.sentinel.auth_token, mock_request) - m_cli.environments.get.assert_called_once_with( - mock.sentinel.environment_id) - - @mock.patch.object(api, 'muranoclient', autospec=True) - @mock.patch.object(api, 'glare_client', autospec=True) - def test_get_muranoclient(self, mock_glare_client, mock_muranoclient): - self._override_conf() - - m_artifacts_client = mock.Mock() - m_muranoclient = mock.Mock() - mock_glare_client.Client.return_value = m_artifacts_client - mock_muranoclient.Client.return_value = m_muranoclient - - client = api._get_muranoclient(mock.sentinel.token_id, None) - - self.assertEqual(m_muranoclient, client) - mock_glare_client.Client.assert_called_once_with( - endpoint='foo_glare_url', token=mock.sentinel.token_id, - insecure=True, key_file='foo_key_file', ca_file='foo_ca_file', - cert_file='foo_cert_file', type_name='murano', type_version=1) - mock_muranoclient.Client.assert_called_once_with( - 1, 'foo_murano_url', token=mock.sentinel.token_id, - artifacts_client=m_artifacts_client) - - @mock.patch.object(api, 'LOG', autospec=True) - @mock.patch.object(api, 'muranoclient', autospec=True) - @mock.patch.object(api, 'glare_client', autospec=True) - def test_get_muranoclient_without_urls(self, mock_glare_client, - mock_muranoclient, mock_log): - self._override_conf(without_urls=True) - - m_artifacts_client = mock.Mock() - m_muranoclient = mock.Mock() - mock_glare_client.Client.return_value = m_artifacts_client - mock_muranoclient.Client.return_value = m_muranoclient - mock_request = mock.Mock(endpoints={'murano': None}) - - client = api._get_muranoclient(mock.sentinel.token_id, mock_request) - - self.assertEqual(m_muranoclient, client) - mock_glare_client.Client.assert_called_once_with( - endpoint=None, token=mock.sentinel.token_id, - insecure=True, key_file='foo_key_file', ca_file='foo_ca_file', - cert_file='foo_cert_file', type_name='murano', type_version=1) - mock_muranoclient.Client.assert_called_once_with( - 1, None, token=mock.sentinel.token_id, - artifacts_client=m_artifacts_client) - mock_log.error.assert_has_calls([ - mock.call('No glare url is specified and no "artifact" ' - 'service is registered in keystone.'), - mock.call('No murano url is specified and no ' - '"application-catalog" service is registered in ' - 'keystone.') - ]) - - def _override_conf(self, without_urls=False): - if without_urls: - self.override_config('url', None, group='glare') - self.override_config('url', None, group='murano') - else: - self.override_config('url', 'foo_glare_url', group='glare') - self.override_config('url', 'foo_murano_url', group='murano') - for arg, group, override_val in ( - ('packages_service', 'engine', 'glare'), - ('insecure', 'glare', True), - ('key_file', 'glare', 'foo_key_file'), - ('ca_file', 'glare', 'foo_ca_file'), - ('cert_file', 'glare', 'foo_cert_file')): - self.override_config(arg, override_val, group=group) - - def test_resource(self): - self.assertIsInstance(api.create_resource(), wsgi.Resource) diff --git a/murano/tests/unit/api/v1/test_actions.py b/murano/tests/unit/api/v1/test_actions.py deleted file mode 100644 index 5c9ffe9be..000000000 --- a/murano/tests/unit/api/v1/test_actions.py +++ /dev/null @@ -1,282 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 unittest import mock - -from oslo_utils import timeutils -from webob import exc - -from murano.api.v1 import actions -from murano.common import policy -from murano.db import models -from murano.db import session as db_session -from murano.services import states -import murano.tests.unit.api.base as tb -import murano.tests.unit.utils as test_utils - - -@mock.patch.object(policy, 'check') -class TestActionsApi(tb.ControllerTest, tb.MuranoApiTestCase): - - def setUp(self): - super(TestActionsApi, self).setUp() - self.controller = actions.Controller() - - def test_execute_action(self, mock_policy_check): - """Test that action execution results in the correct rpc call.""" - self._set_policy_rules( - {'execute_action': '@'} - ) - - fake_now = timeutils.utcnow() - expected = dict( - id='12345', - name='my-env', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '12345', - '_actions': { - 'actionsID_action': { - 'enabled': True, - 'name': 'Testaction' - } - }} - }, - 'Attributes': {} - } - ) - e = models.Environment(**expected) - test_utils.save_models(e) - - rpc_task = { - 'action': { - 'args': '{}', - 'method': 'Testaction', - 'object_id': '12345' - }, - 'project_id': self.tenant, - 'user_id': self.user, - 'model': { - 'Attributes': {}, - 'Objects': { - 'applications': [], - '?': { - '_actions': { - 'actionsID_action': { - 'enabled': True, - 'name': 'Testaction' - } - }, - 'id': '12345' - } - } - }, - 'token': None, - 'id': '12345' - } - - req = self._post('/environments/12345/actions/actionID_action', b'{}') - result = self.controller.execute(req, '12345', 'actionsID_action', - '{}') - - self.mock_engine_rpc.handle_task.assert_called_once_with(rpc_task) - - self.assertIn('task_id', result) - - def _create_session_with_state(self, environment, user_id, state): - unit = db_session.get_session() - session = models.Session() - session.environment_id = environment.id - session.user_id = user_id - session.state = state - session.version = environment.version - with unit.begin(): - unit.add(session) - - def test_execute_action_with_session_in_deploying_state(self, _): - """Test whether session in the deploying state throws error.""" - self._set_policy_rules( - {'execute_action': '@'} - ) - - fake_now = timeutils.utcnow() - expected = dict( - id='12345', - name='my-env', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '12345', - '_actions': { - 'actionsID_action': { - 'enabled': True, - 'name': 'Testaction' - } - }} - }, - 'Attributes': {} - } - ) - environment = models.Environment(**expected) - test_utils.save_models(environment) - req = self._post('/environments/12345/actions/actionID_action', b'{}') - user_id = req.context.user - - self._create_session_with_state(environment, user_id, - states.SessionState.DEPLOYING) - self.assertRaises(exc.HTTPForbidden, self.controller.execute, - req, '12345', 'actionsID_action', {}) - - def test_execute_action_with_session_in_deleting_state(self, _): - """Test whether session in deleting state throws error.""" - self._set_policy_rules( - {'execute_action': '@'} - ) - - fake_now = timeutils.utcnow() - expected = dict( - id='12345', - name='my-env', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '12345', - '_actions': { - 'actionsID_action': { - 'enabled': True, - 'name': 'Testaction' - } - }} - }, - 'Attributes': {} - } - ) - environment = models.Environment(**expected) - test_utils.save_models(environment) - req = self._post('/environments/12345/actions/actionID_action', b'{}') - user_id = req.context.user - - self._create_session_with_state(environment, user_id, - states.SessionState.DELETING) - self.assertRaises(exc.HTTPForbidden, self.controller.execute, - req, '12345', 'actionsID_action', {}) - - @mock.patch('murano.db.services.sessions.SessionServices.validate') - def test_execute_action_with_invalid_session_version(self, mocked_function, - _): - """Test whether validate session function throws error.""" - self._set_policy_rules( - {'execute_action': '@'} - ) - - fake_now = timeutils.utcnow() - expected = dict( - id='12345', - name='my-env', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '12345', - '_actions': { - 'actionsID_action': { - 'enabled': True, - 'name': 'Testaction' - } - }} - }, - 'Attributes': {} - } - ) - environment = models.Environment(**expected) - test_utils.save_models(environment) - req = self._post('/environments/12345/actions/actionID_action', b'{}') - - mocked_function.return_value = False - self.assertRaises(exc.HTTPForbidden, self.controller.execute, - req, '12345', 'actionsID_action', {}) - - def test_get_result(self, _): - """Result of task with given id and environment id is returned.""" - now = timeutils.utcnow() - expected_environment_id = 'test_environment' - expected_task_id = 'test_task' - expected_result = {'test_result': 'test_result'} - - environment = models.Environment( - id=expected_environment_id, - name='test_environment', created=now, updated=now, - tenant_id=self.tenant - ) - - task = models.Task( - id=expected_task_id, - started=now, - finished=now, - result=expected_result, - environment_id=expected_environment_id - ) - - test_utils.save_models(environment, task) - - request = self._get( - '/environments/{environment_id}/actions/{task_id}' - .format(environment_id=expected_environment_id, - task_id=expected_task_id), - ) - - response = request.get_response(self.api) - - self.assertEqual(200, response.status_code) - self.assertEqual(expected_result, response.json) - - def test_get_result_not_found(self, _): - """Return 404 on null task - - If task does not exist, it should be handled correctly - and API should return 404. - """ - expected_environment_id = 'test_environment' - - environment = models.Environment( - id=expected_environment_id, - name='test_environment', - tenant_id=self.tenant - ) - test_utils.save_models(environment) - - request = self._get( - '/environments/{environment_id}/actions/{task_id}' - .format(environment_id=expected_environment_id, - task_id='not_existent_task_id'), - ) - - response = request.get_response(self.api) - - self.assertEqual(404, response.status_code) diff --git a/murano/tests/unit/api/v1/test_catalog.py b/murano/tests/unit/api/v1/test_catalog.py deleted file mode 100644 index a775cce4f..000000000 --- a/murano/tests/unit/api/v1/test_catalog.py +++ /dev/null @@ -1,1440 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. - -# Licensed 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 cgi -from datetime import datetime -import imghdr -from io import StringIO -import os -import tempfile -from unittest import mock -import uuid - -from oslo_config import cfg -from oslo_serialization import jsonutils -from oslo_utils import timeutils -from webob import exc - -from murano.api.v1 import catalog -from murano.api.v1 import PKG_PARAMS_MAP -from murano.common import exceptions as common_exc -from murano.db.catalog import api as db_catalog_api -from murano.db import models -from murano.packages import exceptions -from murano.packages import load_utils -import murano.tests.unit.api.base as test_base -import murano.tests.unit.utils as test_utils - -CONF = cfg.CONF - - -class TestCatalogApi(test_base.ControllerTest, test_base.MuranoApiTestCase): - - def setUp(self): - super(TestCatalogApi, self).setUp() - self.controller = catalog.Controller() - _, self.test_package = self._test_package() - - def _add_pkg(self, tenant_name, public=False, classes=None, **kwargs): - package_to_upload = self.test_package.copy() - package_to_upload['is_public'] = public - package_to_upload['fully_qualified_name'] = str(uuid.uuid4()) - if classes: - package_to_upload['class_definitions'] = classes - else: - package_to_upload['class_definitions'] = [] - package_to_upload.update(kwargs) - return db_catalog_api.package_upload( - package_to_upload, tenant_name) - - def test_packages_filtering_admin(self): - self.is_admin = True - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - for dummy in range(7): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - pkg = self._add_pkg('test_tenant', type='Library') - self._add_pkg('test_tenant') - self._add_pkg('test_tenant2', public=True, type='Library') - self._add_pkg('test_tenant3') - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'False', - 'owned': 'False'})) - self.assertEqual(4, len(result['packages'])) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'False', - 'owned': 'True'})) - self.assertEqual(2, len(result['packages'])) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True', - 'owned': 'False'})) - self.assertEqual(3, len(result['packages'])) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True', - 'owned': 'True'})) - self.assertEqual(2, len(result['packages'])) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'owned': 'True', - 'fqn': pkg.fully_qualified_name})) - self.assertEqual(1, len(result['packages'])) - self.assertEqual(pkg.fully_qualified_name, - result['packages'][0]['fully_qualified_name']) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'owned': 'True', - 'type': 'Library'})) - self.assertEqual(1, len(result['packages'])) - self.assertEqual(pkg.fully_qualified_name, - result['packages'][0]['fully_qualified_name']) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'type': 'Library'})) - self.assertEqual(2, len(result['packages'])) - - def test_packages_filtering_non_admin(self): - self.is_admin = False - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - for dummy in range(8): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - pkg = self._add_pkg('test_tenant', type='Library') - self._add_pkg('test_tenant') - self._add_pkg('test_tenant2', public=True, type='Library') - self._add_pkg('test_tenant3') - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'False', - 'owned': 'False'})) - self.assertEqual(3, len(result['packages'])) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'False', - 'owned': 'True'})) - self.assertEqual(2, len(result['packages'])) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True', - 'owned': 'False'})) - self.assertEqual(3, len(result['packages'])) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True', - 'owned': 'True'})) - self.assertEqual(2, len(result['packages'])) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'owned': 'True', - 'fqn': pkg.fully_qualified_name})) - self.assertEqual(1, len(result['packages'])) - self.assertEqual(pkg.fully_qualified_name, - result['packages'][0]['fully_qualified_name']) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'owned': 'True', - 'type': 'Library'})) - self.assertEqual(1, len(result['packages'])) - self.assertEqual(pkg.fully_qualified_name, - result['packages'][0]['fully_qualified_name']) - - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={ - 'type': 'Library'})) - self.assertEqual(2, len(result['packages'])) - - self._set_policy_rules({'get_package': '', - 'manage_public_package': '!'}) - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'False'})) - self.assertEqual(2, len(result['packages'])) - - def test_packages_filter_by_id(self): - """Test that packages are filtered by ID - - GET /catalog/packages with parameter "id" returns packages - filtered by id. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - _, package1_data = self._test_package() - _, package2_data = self._test_package() - - package1_data['fully_qualified_name'] += '_1' - package1_data['name'] += '_1' - package1_data['class_definitions'] = (u'test.mpl.v1.app.Thing1',) - package2_data['fully_qualified_name'] += '_2' - package2_data['name'] += '_2' - package2_data['class_definitions'] = (u'test.mpl.v1.app.Thing2',) - - expected_package = db_catalog_api.package_upload(package1_data, '') - db_catalog_api.package_upload(package2_data, '') - - req = self._get('/catalog/packages', - params={'id': expected_package.id}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - - self.assertEqual(1, len(res.json['packages'])) - - found_package = res.json['packages'][0] - - self.assertEqual(expected_package.id, found_package['id']) - - def test_packages_filter_by_in_category(self): - """Test that packages are filtered by in:cat1,cat2,cat3 - - GET /catalog/packages with parameter "category=in:cat1,cat2,cat3" - returns packages filtered by category. - """ - names = ['cat1', 'cat2', 'cat3', 'cat4'] - for name in names: - db_catalog_api.category_add(name) - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - _, package1_data = self._test_package() - _, package2_data = self._test_package() - _, package3_data = self._test_package() - - package1_data['fully_qualified_name'] += '_1' - package1_data['name'] += '_1' - package1_data['class_definitions'] = (u'test.mpl.v1.app.Thing1',) - package1_data['categories'] = ('cat1', 'cat2') - - package2_data['fully_qualified_name'] += '_2' - package2_data['name'] += '_2' - package2_data['class_definitions'] = (u'test.mpl.v1.app.Thing2',) - package2_data['categories'] = ('cat2', 'cat3') - - package3_data['fully_qualified_name'] += '_3' - package3_data['name'] += '_3' - package3_data['class_definitions'] = (u'test.mpl.v1.app.Thing3',) - package3_data['categories'] = ('cat2', 'cat4') - - expected_packages = [db_catalog_api.package_upload(package1_data, ''), - db_catalog_api.package_upload(package2_data, '')] - db_catalog_api.package_upload(package3_data, '') - - categories_in = "in:cat1,cat3" - - req = self._get('/catalog/packages', - params={'category': categories_in}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - self.assertEqual(2, len(res.json['packages'])) - - found_packages = res.json['packages'] - - self.assertEqual([pack.id for pack in expected_packages], - [pack['id'] for pack in found_packages]) - - def test_packages_filter_by_in_tag(self): - """Test that packages are filtered by in:tag1,tag2,tag3 - - GET /catalog/packages with parameter "tag=in:tag1,tag2,tag3" - returns packages filtered by category. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - _, package1_data = self._test_package() - _, package2_data = self._test_package() - _, package3_data = self._test_package() - - package1_data['fully_qualified_name'] += '_1' - package1_data['name'] += '_1' - package1_data['class_definitions'] = (u'test.mpl.v1.app.Thing1',) - package1_data['tags'] = ('tag1', 'tag2') - - package2_data['fully_qualified_name'] += '_2' - package2_data['name'] += '_2' - package2_data['class_definitions'] = (u'test.mpl.v1.app.Thing2',) - package2_data['tags'] = ('tag2', 'tag3') - - package3_data['fully_qualified_name'] += '_3' - package3_data['name'] += '_3' - package3_data['class_definitions'] = (u'test.mpl.v1.app.Thing3',) - package3_data['tags'] = ('tag2', 'tag4') - - expected_packages = [db_catalog_api.package_upload(package1_data, ''), - db_catalog_api.package_upload(package2_data, '')] - db_catalog_api.package_upload(package3_data, '') - - tag_in = "in:tag1,tag3" - - req = self._get('/catalog/packages', - params={'tag': tag_in}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - self.assertEqual(2, len(res.json['packages'])) - - found_packages = res.json['packages'] - - self.assertEqual([pack.id for pack in expected_packages], - [pack['id'] for pack in found_packages]) - - def test_packages_filter_by_in_id(self): - """Test that packages are filtered by in:id1,id2,id3 - - GET /catalog/packages with parameter "id=in:id1,id2" returns packages - filtered by id. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - _, package1_data = self._test_package() - _, package2_data = self._test_package() - _, package3_data = self._test_package() - - package1_data['fully_qualified_name'] += '_1' - package1_data['name'] += '_1' - package1_data['class_definitions'] = (u'test.mpl.v1.app.Thing1',) - package2_data['fully_qualified_name'] += '_2' - package2_data['name'] += '_2' - package2_data['class_definitions'] = (u'test.mpl.v1.app.Thing2',) - package3_data['fully_qualified_name'] += '_3' - package3_data['name'] += '_3' - package3_data['class_definitions'] = (u'test.mpl.v1.app.Thing3',) - - expected_packages = [db_catalog_api.package_upload(package1_data, ''), - db_catalog_api.package_upload(package2_data, '')] - db_catalog_api.package_upload(package3_data, '') - - id_in = "in:" + ",".join(pack.id for pack in expected_packages) - - req = self._get('/catalog/packages', - params={'id': id_in}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - - self.assertEqual(2, len(res.json['packages'])) - - found_packages = res.json['packages'] - - self.assertEqual([pack.id for pack in expected_packages], - [pack['id'] for pack in found_packages]) - - def test_packages_filter_by_in_id_empty(self): - """Test that packages are filtered by "id=in:" - - GET /catalog/packages with parameter "id=in:" returns packages - filtered by id, in this case no packages should be returned. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - _, package1_data = self._test_package() - - db_catalog_api.package_upload(package1_data, '') - - req = self._get('/catalog/packages', params={'id': "in:"}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - - self.assertEqual(0, len(res.json['packages'])) - - def test_packages_filter_by_name(self): - """Test that packages are filtered by name - - GET /catalog/packages with parameter "name" returns packages - filtered by name. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - - expected_pkg1 = self._add_pkg("test_tenant", name="test_pkgname_1") - expected_pkg2 = self._add_pkg("test_tenant", name="test_pkgname_2") - - req_pkgname1 = self._get('/catalog/packages', - params={'name': expected_pkg1.name}) - req_pkgname2 = self._get('/catalog/packages', - params={'name': expected_pkg2.name}) - - for dummy in range(2): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res_pkgname1 = req_pkgname1.get_response(self.api) - self.assertEqual(200, res_pkgname1.status_code) - self.assertEqual(1, len(res_pkgname1.json['packages'])) - self.assertEqual(expected_pkg1.name, - res_pkgname1.json['packages'][0]['name']) - - res_pkgname2 = req_pkgname2.get_response(self.api) - self.assertEqual(200, res_pkgname2.status_code) - self.assertEqual(1, len(res_pkgname2.json['packages'])) - self.assertEqual(expected_pkg2.name, - res_pkgname2.json['packages'][0]['name']) - - def test_packages_filter_by_type(self): - """Test that packages are filtered by type - - GET /catalog/packages with parameter "type" returns packages - filtered by type. - """ - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - excepted_pkg1 = self._add_pkg("test_tenant", type='Library') - excepted_pkg2 = self._add_pkg("test_tenant", type='Library') - excepted_pkg3 = self._add_pkg("test_tenant", type='Application') - - # filter by type=Library can see 2 pkgs - req_lib = self._get('/catalog/packages', - params={'type': 'Library'}) - - # filter by type=Application only see 1 pkgs - req_app = self._get('/catalog/packages', - params={'type': 'Application'}) - - for dummy in range(2): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res_lib = req_lib.get_response(self.api) - - self.assertEqual(200, res_lib.status_code) - self.assertEqual(2, len(res_lib.json['packages'])) - - self.assertEqual('Library', res_lib.json['packages'][0]['type']) - self.assertEqual(excepted_pkg1.name, - res_lib.json['packages'][0]['name']) - - self.assertEqual('Library', res_lib.json['packages'][1]['type']) - self.assertEqual(excepted_pkg2.name, - res_lib.json['packages'][1]['name']) - - res_app = req_app.get_response(self.api) - - self.assertEqual(200, res_app.status_code) - self.assertEqual(1, len(res_app.json['packages'])) - - self.assertEqual('Application', res_app.json['packages'][0]['type']) - self.assertEqual(excepted_pkg3.name, - res_app.json['packages'][0]['name']) - - @mock.patch('murano.api.v1.catalog.LOG') - def test_packages_filter_by_order_by(self, mock_log): - warnings = [] - mock_log.warning = lambda msg: warnings.append(msg) - - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - self._add_pkg("test_tenant", type='Library') - - # Test whether a valid order by value works. - order_by = 'name' - request = self._get('/catalog/packages', - params={'order_by': order_by}) - - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - self.controller.search(request) - self.assertEqual(len(warnings), 0) - - # Test whether an invalid order by value fails. - order_by = 'TEST ORDER BY' - request = self._get('/catalog/packages', params={'order_by': order_by}) - - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - self.controller.search(request) - self.assertEqual(len(warnings), 1) - self.assertIn('parameter is not valid', warnings[0]) - - def test_packages_filter_by_limit(self): - """Test that packages are filtered by limit.""" - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - pkg = self._add_pkg("test_tenant", type='Library') - - request = self._get('/catalog/packages', params={'limit': '1'}) - self.expect_policy_check('get_package') - - self.expect_policy_check('manage_public_package') - res = self.controller.search(request) - - self.assertIn('next_marker', res) - self.assertEqual(res['next_marker'], pkg['id']) - - def test_packages_filter_by_limit_negative_cases(self): - """Test whether invalid limit values throw expected exceptions.""" - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - self._add_pkg("test_tenant", type='Library') - - # Test wheter non-number value throws exception - request = self._get('/catalog/packages', - params={'limit': 'not a number'}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - e = self.assertRaises(exc.HTTPBadRequest, self.controller.search, - request) - self.assertEqual(e.explanation, 'Limit param must be an integer') - - # Test whether below-zero value throws exception - request = self._get('/catalog/packages', params={'limit': '-1'}) - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - e = self.assertRaises(exc.HTTPBadRequest, self.controller.search, - request) - self.assertEqual(e.explanation, 'Limit param must be positive') - - @mock.patch('murano.common.utils.split_for_quotes') - @mock.patch('murano.api.v1.catalog.LOG') - def test_packages_filter_handle_value_error(self, mock_log, mock_func): - warnings = [] - mock_func.side_effect = ValueError - mock_log.warning = lambda msg: warnings.append(msg) - - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - self._add_pkg("test_tenant", type='Library') - tag_in = "in:tag1,tag3" - self._get('/catalog/packages', params={'tag': tag_in}) - request = self._get('/catalog/packages', - params={'tag': tag_in}) - - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - self.controller.search(request) - self.assertEqual(len(warnings), 1) - self.assertIn("Search by parameter 'tag' caused an error", - warnings[0]) - - def test_packages_filter_by_search(self): - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - excepted_pkg1 = self._add_pkg("test_tenant", - type='Application', - name='awcloud', - description='some context') - excepted_pkg2 = self._add_pkg("test_tenant", - type='Application', - name='mysql', - description='awcloud product') - excepted_pkg3 = self._add_pkg("test_tenant", - type='Application', - name='package3', - author='mysql author') - - # filter by search=awcloud can see 2 pkgs - req_awc = self._get('/catalog/packages', - params={'search': 'awcloud'}) - - # filter by search=mysql only see 2 pkgs - req_mysql = self._get('/catalog/packages', - params={'search': 'mysql'}) - - for dummy in range(2): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - - res_awc = req_awc.get_response(self.api) - - self.assertEqual(200, res_awc.status_code) - self.assertEqual(2, len(res_awc.json['packages'])) - - self.assertEqual(excepted_pkg1.name, - res_awc.json['packages'][0]['name']) - - self.assertEqual(excepted_pkg2.name, - res_awc.json['packages'][1]['name']) - - res_mysql = req_mysql.get_response(self.api) - - self.assertEqual(200, res_mysql.status_code) - self.assertEqual(2, len(res_mysql.json['packages'])) - - self.assertEqual(excepted_pkg2.name, - res_mysql.json['packages'][0]['name']) - self.assertEqual(excepted_pkg3.name, - res_mysql.json['packages'][1]['name']) - - def test_packages(self): - self._set_policy_rules( - {'get_package': '', - 'manage_public_package': ''} - ) - for dummy in range(9): - self.expect_policy_check('get_package') - self.expect_policy_check('manage_public_package') - result = self.controller.search(self._get('/v1/catalog/packages/')) - self.assertEqual(0, len(result['packages'])) - - self._add_pkg('test_tenant') - self._add_pkg('test_tenant') - self._add_pkg('other_tenant') - self._add_pkg('other_tenant') - - # non-admin should only see 2 pkgs he can edit. - self.is_admin = False - result = self.controller.search(self._get('/v1/catalog/packages/')) - self.assertEqual(2, len(result['packages'])) - # can only deploy his + public - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True'})) - self.assertEqual(2, len(result['packages'])) - - # admin can edit anything - self.is_admin = True - result = self.controller.search(self._get('/v1/catalog/packages/')) - self.assertEqual(4, len(result['packages'])) - # admin can only deploy his + public - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True'})) - self.assertEqual(2, len(result['packages'])) - - self._add_pkg('test_tenant', public=True) - self._add_pkg('other_tenant', public=True) - - # non-admin are allowed to edit public packages by policy - self.is_admin = False - result = self.controller.search(self._get('/v1/catalog/packages/')) - self.assertEqual(4, len(result['packages'])) - # can deploy mine + other public - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True'})) - self.assertEqual(4, len(result['packages'])) - - # admin can edit anything - self.is_admin = True - result = self.controller.search(self._get('/v1/catalog/packages/')) - self.assertEqual(6, len(result['packages'])) - # can deploy mine + public - result = self.controller.search(self._get( - '/v1/catalog/packages/', params={'catalog': 'True'})) - self.assertEqual(4, len(result['packages'])) - - def _test_package(self, manifest='manifest.yaml'): - package_dir = os.path.abspath( - os.path.join( - __file__, - '../../../packages/test_packages/test.mpl.v1.app', - ) - ) - pkg = load_utils.load_from_dir( - package_dir, filename=manifest - ) - package = { - 'fully_qualified_name': pkg.full_name, - 'type': pkg.package_type, - 'author': pkg.author, - 'supplier': pkg.supplier, - 'name': pkg.display_name, - 'description': pkg.description, - 'is_public': True, - 'tags': pkg.tags, - 'logo': pkg.logo, - 'supplier_logo': pkg.supplier_logo, - 'ui_definition': pkg.ui, - 'class_definitions': tuple(pkg.classes), - 'archive': pkg.blob, - 'categories': [], - } - return pkg, package - - def test_modify_package_tags(self): - self._set_policy_rules( - {'modify_package': '', - 'manage_public_package': '', - 'publicize_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - saved_package_pub = self._add_pkg('test_tenant', public=True) - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - - url = '/v1/catalog/packages/' + str(saved_package.id) - - data = [] - data.append({'op': 'add', 'path': ['tags'], 'value': ["foo", "bar"]}) - - req = self._patch(url, jsonutils.dump_as_bytes(data)) - result = self.controller.update(req, data, saved_package.id) - - self.assertIn('foo', result['tags']) - self.assertIn('bar', result['tags']) - - self.expect_policy_check('modify_package', - {'package_id': saved_package_pub.id}) - self.expect_policy_check('manage_public_package', {}) - url = '/v1/catalog/packages/' + str(saved_package_pub.id) - - data = [] - data.append({'op': 'add', 'path': ['tags'], 'value': ["foo", "bar"]}) - - req = self._patch(url, jsonutils.dump_as_bytes(data)) - result = self.controller.update(req, data, saved_package_pub.id) - - self.assertIn('foo', result['tags']) - self.assertIn('bar', result['tags']) - - def test_modify_package_name(self): - self._set_policy_rules( - {'modify_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - url = '/v1/catalog/packages/' + str(saved_package.id) - - data = [] - data.append({'op': 'replace', 'path': ['name'], 'value': 'test_name'}) - req = self._patch(url, jsonutils.dump_as_bytes(data)) - result = self.controller.update(req, data, saved_package.id) - self.assertEqual('test_name', result['name']) - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - - data = [] - data.append({'op': 'replace', 'path': ['name'], 'value': 'a' * 81}) - req = self._patch(url, jsonutils.dump_as_bytes(data)) - self.assertRaises(exc.HTTPBadRequest, self.controller.update, - req, data, saved_package.id) - - def test_modify_package_no_body(self): - self._set_policy_rules( - {'modify_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - url = '/v1/catalog/packages/' + str(saved_package.id) - req = self._patch(url, jsonutils.dump_as_bytes(None)) - self.assertRaises(exc.HTTPBadRequest, self.controller.update, - req, None, saved_package.id) - - def test_modify_package_is_public(self): - self._set_policy_rules( - {'modify_package': '', - 'manage_public_package': '', - 'publicize_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - url = '/v1/catalog/packages/' + str(saved_package.id) - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - self.expect_policy_check('publicize_package', {}) - data = [] - data.append({'op': 'replace', 'path': ['is_public'], 'value': True}) - req = self._patch(url, jsonutils.dump_as_bytes(data)) - result = self.controller.update(req, data, saved_package.id) - self.assertTrue(result['is_public']) - - def test_modify_package_bad_content_type(self): - self._set_policy_rules( - {'modify_package': '', - 'manage_public_package': '', - 'publicize_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - url = '/v1/catalog/packages/' + str(saved_package.id) - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - self.expect_policy_check('publicize_package', {}) - data = [] - data.append({'op': 'replace', 'path': ['is_public'], 'value': True}) - req = self._patch(url, jsonutils.dump_as_bytes(data)) - result = self.controller.update(req, data, saved_package.id) - self.assertTrue(result['is_public']) - - def test_modify_package_with_incorrect_content_type(self): - self._set_policy_rules( - {'modify_package': ''} - ) - saved_package = self._add_pkg('test_tenant') - self.expect_policy_check('modify_package', - {'package_id': saved_package.id}) - url = '/v1/catalog/packages/' + str(saved_package.id) - data = [] - data.append({'op': 'replace', 'path': ['name'], 'value': 'test_name'}) - - req = self._patch(url, jsonutils.dump_as_bytes(data)) - req.get_content_type = mock.MagicMock( - side_effect=common_exc.UnsupportedContentType) - self.assertRaises(exc.HTTPBadRequest, - self.controller.update, req, data, saved_package.id) - - def test_not_valid_logo(self): - self.assertRaises(exceptions.PackageLoadError, - self._test_package, 'manifest_with_broken_logo.yaml') - - def test_load_package_with_supplier_info(self): - self._set_policy_rules( - {'get_package': '@'} - ) - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - self.expect_policy_check('get_package', - {'package_id': saved_package.id}) - - req = self._get('/v1/catalog/packages/%s' % saved_package.id) - result = self.controller.get(req, saved_package.id) - - self.assertEqual(package['supplier'], result['supplier']) - - req = self._get( - '/v1/catalog/packages/%s/supplier_logo' % saved_package.id - ) - result = self.controller.get_supplier_logo(req, saved_package.id) - - self.assertEqual('png', imghdr.what('', result)) - - def test_download_package(self): - self._set_policy_rules( - {'download_package': '@'} - ) - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - self.expect_policy_check('download_package', - {'package_id': saved_package.id}) - - req = self._get_with_accept('/catalog/packages/%s/download' - % saved_package.id, - accept='application/octet-stream') - - result = req.get_response(self.api) - - self.assertEqual(200, result.status_code) - - def test_download_package_negative(self): - - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - req = self._get_with_accept('/catalog/packages/%s/download' - % saved_package.id, - accept='application/foo') - - result = req.get_response(self.api) - - self.assertEqual(406, result.status_code) - self.assertIn(b'Acceptable response can not be provided', - result.body) - - @mock.patch('murano.api.v1.catalog._validate_body') - @mock.patch('murano.common.policy.check') - def test_upload_package_with_invalid_schema(self, mock_policy_check, - mock_validate_body): - invalid_pkg_upload_schema = {"type": None} - mock_policy_check.return_value = True - mock_validate_body.return_value = (None, invalid_pkg_upload_schema) - mock_request = mock.MagicMock(context={}) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request) - self.assertIn( - "Package schema is not valid", - e.explanation - ) - - @mock.patch('murano.api.v1.catalog._validate_body') - @mock.patch('murano.common.policy.check') - def test_upload_package_with_invalid_file_content(self, mock_policy_check, - mock_validate_body): - with tempfile.NamedTemporaryFile(delete=True) as temp_file: - mock_policy_check.return_value = True - mock_validate_body.return_value = (mock.MagicMock(file=temp_file), - None) - mock_request = mock.MagicMock(context={}) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request) - self.assertIn( - "Uploading file can't be empty", - e.explanation - ) - - @mock.patch('murano.api.v1.catalog.PKG_PARAMS_MAP') - @mock.patch('murano.packages.load_utils.load_from_file') - @mock.patch('murano.api.v1.catalog._validate_body') - @mock.patch('murano.common.policy.check') - def test_upload_package_with_oversized_file_name(self, mock_policy_check, - mock_validate_body, - mock_load_from_file, - mock_pkg_params_map): - mock_policy_check.return_value = True - mock_load_from_file.return_value = mock.MagicMock( - __exit__=lambda obj, type, value, tb: False) - mock_pkg_params_map.return_value = {} - mock_request = mock.MagicMock(context=mock.MagicMock( - tenant=self.tenant)) - test_package_meta = {'name': 'a' * 81} - with tempfile.NamedTemporaryFile(delete=True) as temp_file: - temp_file.write(b"Random test content\n") - temp_file.seek(0) - mock_validate_body.return_value = \ - (mock.MagicMock(file=temp_file), test_package_meta) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request) - self.assertIn( - "Package name should be 80 characters maximum", - e.explanation - ) - - @mock.patch('murano.packages.load_utils.load_from_file') - @mock.patch('murano.api.v1.catalog._validate_body') - @mock.patch('murano.common.policy.check') - def test_upload_package_handle_package_load_error(self, mock_policy_check, - mock_validate_body, - mock_load_from_file): - pkg_to_upload = mock.MagicMock( - __exit__=lambda obj, type, value, tb: False) - mock_request = mock.MagicMock(context=mock.MagicMock( - tenant=self.tenant)) - mock_load_from_file.return_value = pkg_to_upload - mock_load_from_file.side_effect = exceptions.PackageLoadError - mock_policy_check.return_value = True - with tempfile.NamedTemporaryFile(delete=True) as temp_file: - temp_file.write(b"Random test content\n") - temp_file.seek(0) - mock_validate_body.return_value = \ - (mock.MagicMock(file=temp_file), None) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request) - self.assertIn( - "Couldn't load package from file", - e.explanation - ) - - @mock.patch('murano.packages.load_utils.load_from_file') - @mock.patch('murano.api.v1.catalog._validate_body') - @mock.patch('murano.common.policy.check') - def test_upload_package_handle_duplicate_exception(self, mock_policy_check, - mock_validate_body, - mock_load_from_file): - """Test whether duplicate error is correctly thrown.""" - # Save the first package entry to the DB - test_package_meta = self.test_package.copy() - test_package_meta['name'] = 'test_package' - test_package_meta['fully_qualified_name'] = str(uuid.uuid4()) - test_package_meta['description'] = 'test_description' - test_package_meta['enabled'] = False - test_package_meta['is_public'] = False - db_catalog_api.package_upload(test_package_meta, self.tenant) - - # Reverse the operation performed by upload for copying values from - # pkg_to_upload into package_meta dict. - pkg_to_upload = mock.MagicMock( - __exit__=lambda obj, type, value, tb: False) - for k, v in PKG_PARAMS_MAP.items(): - if v in test_package_meta.keys(): - val = test_package_meta[v] - setattr(pkg_to_upload.__enter__(), k, val) - - # Delete extra properties so validation in upload passes. - for attr in ['fully_qualified_name', 'ui_definition', 'author', - 'supplier_logo', 'supplier', 'logo', 'type', 'archive']: - if attr in test_package_meta.keys(): - del test_package_meta[attr] - - mock_request = mock.MagicMock(context=mock.MagicMock( - project_id=self.tenant)) - mock_load_from_file.return_value = pkg_to_upload - mock_policy_check.return_value = True - - with tempfile.NamedTemporaryFile(delete=True) as temp_file: - temp_file.write(b"Random test content\n") - temp_file.seek(0) - mock_validate_body.return_value = \ - (mock.MagicMock(file=temp_file), test_package_meta) - e = self.assertRaises(exc.HTTPConflict, self.controller.upload, - mock_request) - self.assertIn( - "Package with specified full name is already registered", - e.detail - ) - - @mock.patch('murano.common.policy.check') - def test_upload_package_with_oversized_body(self, mock_policy_check): - mock_policy_check.return_value = True - packages_to_upload = {'a': 0, 'b': 1, 'c': 2} - mock_request = mock.MagicMock(context={}) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request, body=packages_to_upload) - self.assertIn( - "'multipart/form-data' request body should contain 1 or 2 " - "parts: json string and zip archive.", - e.explanation - ) - - @mock.patch('murano.common.policy.check') - def test_upload_package_with_empty_body(self, mock_policy_check): - mock_policy_check.return_value = True - packages_to_upload = {} - mock_request = mock.MagicMock(context={}) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.upload, - mock_request, body=packages_to_upload) - self.assertEqual( - 'There is no file package with application description', - e.explanation) - - def test_get_ui_definition(self): - self._set_policy_rules( - {'get_package': '@'} - ) - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - self.expect_policy_check('get_package', - {'package_id': saved_package.id}) - - req = self._get_with_accept('/catalog/packages/%s/ui' - % saved_package.id, - accept="text/plain") - - result = req.get_response(self.api) - - self.assertEqual(200, result.status_code) - - def test_get_ui_definition_negative(self): - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - req = self._get_with_accept('/catalog/packages/%s/ui' - % saved_package.id, - accept='application/foo') - - result = req.get_response(self.api) - - self.assertEqual(406, result.status_code) - self.assertIn(b'Acceptable response can not be provided', - result.body) - - def test_get_logo(self): - self._set_policy_rules( - {'get_package': '@'} - ) - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - self.expect_policy_check('get_package', - {'package_id': saved_package.id}) - - req = self._get_with_accept('/catalog/packages/%s/logo' - % saved_package.id, - accept="application/octet-stream") - - result = req.get_response(self.api) - - self.assertEqual(200, result.status_code) - self.assertEqual(package['logo'], result.body) - - def test_get_logo_negative(self): - _, package = self._test_package() - - saved_package = db_catalog_api.package_upload(package, '') - - req = self._get_with_accept('/catalog/packages/%s/logo' - % saved_package.id, - accept='application/foo') - - result = req.get_response(self.api) - - self.assertEqual(406, result.status_code) - self.assertIn(b'Acceptable response can not be provided', - result.body) - - def test_add_public_unauthorized(self): - self._set_policy_rules({ - 'upload_package': '@', - 'publicize_package': 'is_admin:True', - 'delete_package': 'is_admin:True', - }) - - self.expect_policy_check('upload_package') - self.expect_policy_check('delete_package', mock.ANY) - self.expect_policy_check('upload_package') - self.expect_policy_check('publicize_package') - self.expect_policy_check('upload_package') - self.expect_policy_check('publicize_package') - - file_obj_str = StringIO("This is some dummy data") - file_obj = mock.MagicMock(cgi.FieldStorage) - file_obj.file = file_obj_str - package_from_dir, _ = self._test_package() - - body_fmt = '''\ - ---BOUNDARY -Content-Disposition: form-data; name="__metadata__" - -{0} ---BOUNDARY -Content-Disposition: form-data; name="ziparchive"; filename="file.zip" - -This is a fake zip archive ---BOUNDARY--''' - - def format_body(content): - content = jsonutils.dumps(content) - body = body_fmt.format(content) - body = body.encode('utf-8') - return body - - with mock.patch('murano.packages.load_utils.load_from_file') as lff: - ctxmgr = mock.Mock() - ctxmgr.__enter__ = mock.Mock(return_value=package_from_dir) - ctxmgr.__exit__ = mock.Mock(return_value=False) - lff.return_value = ctxmgr - - # Uploading a non-public package - req = self._post( - '/catalog/packages', - format_body({'is_public': False}), - content_type='multipart/form-data; ; boundary=BOUNDARY', - ) - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - - self.is_admin = True - app_id = jsonutils.loads(res.body)['id'] - req = self._delete('/catalog/packages/{0}'.format(app_id)) - res = req.get_response(self.api) - - self.is_admin = False - # Uploading a public package fails - req = self._post( - '/catalog/packages', - format_body({'is_public': True}), - content_type='multipart/form-data; ; boundary=BOUNDARY', - ) - res = req.get_response(self.api) - self.assertEqual(403, res.status_code) - - # Uploading a public package passes for admin - self.is_admin = True - req = self._post( - '/catalog/packages', - format_body({'is_public': True}), - content_type='multipart/form-data; ; boundary=BOUNDARY', - ) - res = req.get_response(self.api) - self.assertEqual(200, res.status_code) - - def test_upload_pkg_with_tag(self): - """Check upload package with tags successfully""" - - self._set_policy_rules({'upload_package': '@'}) - self.expect_policy_check('upload_package') - file_obj_str = StringIO("This is some dummy data") - file_obj = mock.MagicMock(cgi.FieldStorage) - file_obj.file = file_obj_str - package_from_dir, _ = self._test_package() - - body_fmt = '''\ - ---BOUNDARY -Content-Disposition: form-data; name="__metadata__" - -{0} ---BOUNDARY -Content-Disposition: form-data; name="ziparchive"; filename="file.zip" - -This is a fake zip archive ---BOUNDARY--''' - - def format_body(content): - content = jsonutils.dumps(content) - body = body_fmt.format(content) - body = body.encode('utf-8') - return body - - with mock.patch('murano.packages.load_utils.load_from_file') as lff: - ctxmgr = mock.Mock() - ctxmgr.__enter__ = mock.Mock(return_value=package_from_dir) - ctxmgr.__exit__ = mock.Mock(return_value=False) - lff.return_value = ctxmgr - - # Uploading a package with foo_tag - req = self._post( - '/catalog/packages', - format_body({'tags': ['foo_tag']}), - content_type='multipart/form-data; ; boundary=BOUNDARY', - ) - res = req.get_response(self.api) - - processed_result = jsonutils.loads(res.body) - # check user set foo_tag in result - self.assertIn('foo_tag', processed_result["tags"]) - # check tag Linux from package in result - self.assertIn('Linux', processed_result["tags"]) - - def test_add_category(self): - """Check that category added successfully""" - - self._set_policy_rules({'add_category': '@'}) - self.expect_policy_check('add_category') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = { - 'name': 'new_category', - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7], - 'package_count': 0, - } - - body = {'name': 'new_category'} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - processed_result = jsonutils.loads(result.body) - self.assertIn('id', processed_result.keys()) - expected['id'] = processed_result['id'] - self.assertEqual(expected, processed_result) - - def test_get_category(self): - """Check that get category executed successfully""" - - self._set_policy_rules({'add_category': '@', 'get_category': '@'}) - self.expect_policy_check('add_category') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = { - 'name': 'new_category', - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7], - 'package_count': 0, - 'packages': [] - } - - body = {'name': 'new_category'} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - expected['id'] = jsonutils.loads(result.body)['id'] - - self.expect_policy_check('get_category') - req = self._get('/catalog/categories/%s' % expected['id']) - retrieved_category = jsonutils.loads(req.get_response(self.api).body) - self.assertEqual(retrieved_category, expected) - - def test_delete_category(self): - """Check that category deleted successfully""" - self._set_policy_rules({'delete_category': '@'}) - self.expect_policy_check('delete_category', - {'category_id': '12345'}) - - fake_now = timeutils.utcnow() - expected = {'name': 'new_category', - 'created': fake_now, - 'updated': fake_now, - 'id': '12345'} - - e = models.Category(**expected) - test_utils.save_models(e) - - req = self._delete('/catalog/categories/12345') - processed_result = req.get_response(self.api) - self.assertEqual(b'', processed_result.body) - self.assertEqual(200, processed_result.status_code) - - @mock.patch('murano.db.catalog.api.category_get') - def test_delete_category_with_packages_negative(self, mock_get_category): - """Check that deleting category that has assigned packages fails.""" - mock_get_category.return_value = mock.MagicMock( - packages=['test_package']) - - self._set_policy_rules({'delete_category': '@'}) - self.expect_policy_check('delete_category', - {'category_id': '12345'}) - - fake_now = timeutils.utcnow() - expected = {'name': 'new_category', - 'created': fake_now, - 'updated': fake_now, - 'id': '12345'} - - category = models.Category(**expected) - test_utils.save_models(category) - - req = self._delete('/catalog/categories/12345') - e = self.assertRaises(exc.HTTPForbidden, - self.controller.delete_category, req, '12345') - self.assertEqual(e.explanation, - "It's impossible to delete categories assigned to the" - " package, uploaded to the catalog") - - def test_add_category_without_name(self): - """Test whether adding a category without a name throws exception.""" - self._set_policy_rules({'add_category': '@'}) - self.expect_policy_check('add_category') - - body = {'name': ''} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - e = self.assertRaises(exc.HTTPBadRequest, self.controller.add_category, - req, body) - self.assertEqual("Please, specify a name of the category to create", - e.explanation) - - def test_add_category_handle_duplicate_exception(self): - """Test whether creating duplicate categories throws exception.""" - self._set_policy_rules({'add_category': '@'}) - self.expect_policy_check('add_category') - self.expect_policy_check('add_category') - - body = {'name': 'new_category'} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - self.controller.add_category(req, body) - - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - e = self.assertRaises(exc.HTTPConflict, self.controller.add_category, - req, body) - self.assertEqual("Category with specified name is already exist", - e.explanation) - - def test_add_category_failed_for_non_admin(self): - """Check that non admin user couldn't add new category""" - self._set_policy_rules({'add_category': 'role:context_admin'}) - self.is_admin = False - self.expect_policy_check('add_category') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - body = {'name': 'new_category'} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(403, result.status_code) - - def test_add_long_category(self): - """Test that category name does not exceed 80 characters - - Check that a category that contains more than 80 characters - fails to be added - """ - - self._set_policy_rules({'add_category': '@'}) - self.expect_policy_check('add_category') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - body = {'name': 'cat' * 80} - req = self._post('/catalog/categories', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_message = result.text.replace('\n', '') - self.assertIn('Category name should be 80 characters maximum', - result_message) - - def test_list_categories(self): - names = ['cat1', 'cat2'] - for name in names: - db_catalog_api.category_add(name) - - self._set_policy_rules({'get_category': '@'}) - self.expect_policy_check('get_category') - - req = self._get('/catalog/categories') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - result_categories = jsonutils.loads(result.body)['categories'] - self.assertEqual(2, len(result_categories)) - self.assertEqual(names, [c['name'] for c in result_categories]) - - params = {'sort_keys': 'created, id'} - req = self._get('/catalog/categories', params) - self.expect_policy_check('get_category') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - result_categories = jsonutils.loads(result.body)['categories'] - self.assertEqual(names, [c['name'] for c in result_categories]) - - names.reverse() - - params = {'sort_dir': 'desc'} - req = self._get('/catalog/categories', params) - self.expect_policy_check('get_category') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - result_categories = jsonutils.loads(result.body)['categories'] - self.assertEqual(names, [c['name'] for c in result_categories]) - - def test_list_categories_negative(self): - self._set_policy_rules({'get_category': '@'}) - self.expect_policy_check('get_category') - - req = self._get('/catalog/categories', {'sort_dir': 'test'}) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - self.expect_policy_check('get_category') - req = self._get('/catalog/categories', {'sort_keys': 'test'}) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - self.expect_policy_check('get_category') - req = self._get('/catalog/categories', {'test': ['test']}) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) diff --git a/murano/tests/unit/api/v1/test_deployments.py b/murano/tests/unit/api/v1/test_deployments.py deleted file mode 100644 index 0171e9120..000000000 --- a/murano/tests/unit/api/v1/test_deployments.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (c) 2016 AT&T Inc. -# -# Licensed 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 murano.tests.unit.api.base as tb -from unittest import mock - -from oslo_config import fixture as config_fixture -from oslo_serialization import jsonutils - -from murano.api.v1 import deployments -from murano.api.v1 import environments - -from webob import exc - - -class TestDeploymentsApi(tb.ControllerTest, tb.MuranoApiTestCase): - def setUp(self): - super(TestDeploymentsApi, self).setUp() - self.environments_controller = environments.Controller() - self.deployments_controller = deployments.Controller() - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - def test_deployments_index(self): - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@', - 'list_deployments': '@'} - ) - - # Create an environment. - self.expect_policy_check('create_environment') - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], response_body['tenant_id']) - self.assertEqual('test_environment_1', response_body['name']) - ENVIRONMENT_ID = response_body['id'] - - # Verify that the environment has not yet been deployed. - self.expect_policy_check('list_deployments', - {'environment_id': ENVIRONMENT_ID}) - result = self.deployments_controller.index(request, ENVIRONMENT_ID) - self.assertEqual([], result['deployments']) - - # Deploy the environment. - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', **CREDENTIALS) - response_body = jsonutils.loads(request.get_response(self.api).body) - - SESSION_ID = response_body['id'] - - request = self._post('/environments/{environment_id}/sessions/' - '{session_id}/deploy' - .format(environment_id=ENVIRONMENT_ID, - session_id=SESSION_ID), - b'', **CREDENTIALS) - result = request.get_response(self.api) - self.assertEqual('200 OK', result.status) - - # Verify that the environment was deployed. - self.expect_policy_check('list_deployments', - {'environment_id': ENVIRONMENT_ID}) - result = self.deployments_controller.index(request, ENVIRONMENT_ID) - deployment_id = result['deployments'][0]['id'] - self.assertEqual(1, len(result['deployments'])) - self.assertIsNotNone(deployment_id) - - def test_deployments_all_environments(self): - """Test list deployments for all environments. - - Create 2 environments, deploy both, and check that 2 deployments exist. - """ - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@', - 'list_deployments_all_environments': '@'} - ) - - for count in range(2): - # Create environment. - self.expect_policy_check('create_environment') - request = self._post( - '/environments', - jsonutils.dump_as_bytes( - {'name': 'test_environment_{0}'.format(count)}), - **CREDENTIALS - ) - response_body = jsonutils.loads( - request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], response_body['tenant_id']) - self.assertEqual('test_environment_{0}'.format(count), - response_body['name']) - ENVIRONMENT_ID = response_body['id'] - - # Deploy environment. - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', **CREDENTIALS) - response_body = jsonutils.loads( - request.get_response(self.api).body) - SESSION_ID = response_body['id'] - request = self._post('/environments/{environment_id}/sessions/' - '{session_id}/deploy' - .format(environment_id=ENVIRONMENT_ID, - session_id=SESSION_ID), - b'', **CREDENTIALS) - result = request.get_response(self.api) - self.assertEqual('200 OK', result.status) - - # Check that 2 deployments exist. - self.expect_policy_check('list_deployments_all_environments') - result = self.deployments_controller.index(request, None) - self.assertEqual(2, len(result['deployments'])) - for deployment in result['deployments']: - self.assertIsNotNone(deployment) - self.assertNotEqual(result['deployments'][0], result['deployments'][1]) - - def test_deployments_all_environments_different_tenants(self): - """Test list deployments for all environments in different tenants. - - Should only return return environments for current tenant. - """ - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - ALT_CREDENTIALS = {'tenant': 'test_tenant_2', 'user': 'test_user_2'} - self._set_policy_rules( - {'create_environment': '@', - 'list_deployments_all_environments': '@'} - ) - - deployments = [] - - # Create the first environment inside first tenant and the second - # environments inside the alternate tenant. Then deploy both. - for count, creds in enumerate([CREDENTIALS, ALT_CREDENTIALS]): - # Create each environment. - self.expect_policy_check('create_environment') - request = self._post( - '/environments', - jsonutils.dump_as_bytes( - {'name': 'test_environment_{0}'.format(count)}), - **creds - ) - response_body = jsonutils.loads( - request.get_response(self.api).body) - self.assertEqual(creds['tenant'], response_body['tenant_id']) - self.assertEqual('test_environment_{0}'.format(count), - response_body['name']) - ENVIRONMENT_ID = response_body['id'] - - # Deploy each environment. - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', **creds) - response_body = jsonutils.loads( - request.get_response(self.api).body) - SESSION_ID = response_body['id'] - request = self._post('/environments/{environment_id}/sessions/' - '{session_id}/deploy' - .format(environment_id=ENVIRONMENT_ID, - session_id=SESSION_ID), - b'', **creds) - result = request.get_response(self.api) - self.assertEqual('200 OK', result.status) - - # Check that each tenant only returns one deployment. - self.expect_policy_check('list_deployments_all_environments') - result = self.deployments_controller.index(request, None) - self.assertEqual(1, len(result['deployments'])) - deployment_id = result['deployments'][0]['id'] - self.assertIsNotNone(deployment_id) - deployments.append(deployment_id) - - self.assertNotEqual(deployments[0], deployments[1]) - - def test_deployments_not_found_statuses(self): - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@', - 'statuses_deployments': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post('/environments', jsonutils. - dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], - response_body['tenant_id']) - - ENVIRONMENT_ID = response_body['id'] - deploy_id = '1' - self.expect_policy_check('statuses_deployments', - {'deployment_id': deploy_id, - 'environment_id': ENVIRONMENT_ID}) - self.assertRaises(exc.HTTPNotFound, - self.deployments_controller.statuses, request, - ENVIRONMENT_ID, deploy_id) - - def test_deployments_status(self): - CREDENTIALS = {'tenant': 'test_tenant', 'user': 'test_user'} - self._set_policy_rules( - {'create_environment': '@', - 'statuses_deployments': '@', - 'list_deployments': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment'}), - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], - response_body['tenant_id']) - - ENVIRONMENT_ID = response_body['id'] - - # Create session - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', **CREDENTIALS) - response_body = jsonutils.loads(request.get_response(self.api).body) - - SESSION_ID = response_body['id'] - - request = self._post('/environments/{environment_id}/sessions/' - '{session_id}/deploy' - .format(environment_id=ENVIRONMENT_ID, - session_id=SESSION_ID), - b'', **CREDENTIALS) - request.get_response(self.api) - - self.expect_policy_check('list_deployments', - {'environment_id': ENVIRONMENT_ID}) - result = self.deployments_controller.index(request, ENVIRONMENT_ID) - deploy_id = result['deployments'][0]['id'] - - self.expect_policy_check('statuses_deployments', - {'deployment_id': deploy_id, - 'environment_id': ENVIRONMENT_ID}) - result = self.deployments_controller.statuses(request, ENVIRONMENT_ID, - deploy_id) - self.assertNotEqual(result['reports'], []) - - request.GET['service_id'] = "12" - self.expect_policy_check('statuses_deployments', - {'deployment_id': deploy_id, - 'environment_id': ENVIRONMENT_ID}) - result = self.deployments_controller.statuses(request, ENVIRONMENT_ID, - deploy_id) - self.assertEqual(result['reports'], []) - - self.expect_policy_check('statuses_deployments', - {'deployment_id': deploy_id, - 'environment_id': '12'}) - self.assertRaises(exc.HTTPBadRequest, - self.deployments_controller.statuses, request, - "12", deploy_id) - - def test_set_dep_state(self): - deployment = mock.Mock() - deployment.id = "1" - deployment.description = {'applications': []} - unit = mock.Mock() - query_result = mock.Mock() - filter_by_result = mock.Mock() - filter_by_result.count.return_value = 0 - query_result.filter_by.return_value = filter_by_result - unit.query.return_value = query_result - - result = deployments.set_dep_state(deployment, unit) - self.assertEqual('success', result.state) - - deployment.description = None - result = deployments.set_dep_state(deployment, unit) - self.assertEqual('success', result.state) - - filter_by_result.count.return_value = 1 - query_result.filter_by.return_value = filter_by_result - result = deployments.set_dep_state(deployment, unit) - self.assertEqual('completed_w_errors', result.state) - - filter_by_result.count.return_value = 0 - query_result.filter_by.return_value = filter_by_result - deployment.finished = False - result = deployments.set_dep_state(deployment, unit) - self.assertEqual('running', result.state) - - filter_by_result.count.return_value = 1 - query_result.filter_by.return_value = filter_by_result - result = deployments.set_dep_state(deployment, unit) - self.assertEqual('running_w_errors', result.state) diff --git a/murano/tests/unit/api/v1/test_env_templates.py b/murano/tests/unit/api/v1/test_env_templates.py deleted file mode 100644 index 1ffd9ec9d..000000000 --- a/murano/tests/unit/api/v1/test_env_templates.py +++ /dev/null @@ -1,972 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D. -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 datetime import datetime -from unittest import mock - -from oslo_config import fixture as config_fixture -from oslo_db import exception as db_exc -from oslo_serialization import jsonutils -from oslo_utils import timeutils - -from murano.api.v1 import templates -from murano.db import models -import murano.tests.unit.api.base as tb -import murano.tests.unit.utils as test_utils - - -class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase): - - def setUp(self): - super(TestEnvTemplateApi, self).setUp() - self.controller = templates.Controller() - self.uuids = ['env_template_id', 'other', 'network_id', - 'environment_id', 'session_id'] - self.mock_uuid = self._stub_uuid(self.uuids) - - def test_list_empty_env_templates(self): - """Check that with no templates an empty list is returned.""" - self._set_policy_rules( - {'list_env_templates': '@'} - ) - self.expect_policy_check('list_env_templates') - - req = self._get('/templates') - result = req.get_response(self.api) - self.assertEqual({'templates': []}, jsonutils.loads(result.body)) - - def test_create_env_templates(self): - """Create an template, test template.show().""" - self._set_policy_rules( - {'list_env_templates': '@', - 'create_env_template': '@', - 'show_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = {'tenant_id': self.tenant, - 'id': 'env_template_id', - 'is_public': False, - 'name': 'mytemp', - 'description_text': 'description', - 'version': 0, - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7]} - - body = {'name': 'mytemp', 'description_text': 'description'} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - - self.assertEqual(expected, jsonutils.loads(result.body)) - - # Reset the policy expectation - self.expect_policy_check('list_env_templates') - - req = self._get('/templates') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual({'templates': [expected]}, - jsonutils.loads(result.body)) - - expected['services'] = [] - - self.expect_policy_check('show_env_template', - {'env_template_id': self.uuids[0]}) - req = self._get('/templates/%s' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(expected, jsonutils.loads(result.body)) - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices.' - 'create') - def test_create_env_templates_handle_duplicate_exc(self, mock_function): - """Create an template, test template.show().""" - self._set_policy_rules( - {'create_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - mock_function.side_effect = db_exc.DBDuplicateEntry - - body = {'name': 'mytemp', 'description_text': 'description'} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(409, result.status_code) - - def test_list_public_env_templates(self): - """Create an template, test templates.public().""" - self._set_policy_rules( - {'create_env_template': '@', - 'list_env_templates': '@'} - ) - - self.expect_policy_check('create_env_template') - - body = {'name': 'mytemp2', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertTrue(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('list_env_templates') - req = self._get('/templates', {'is_public': True}) - - result = req.get_response(self.api) - data = jsonutils.loads(result.body) - self.assertEqual(1, len(data)) - self.assertTrue(data['templates'][0]['is_public']) - - def test_clone_env_templates(self): - """Create an template, test templates.public().""" - self._set_policy_rules( - {'create_env_template': '@', - 'clone_env_template': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp2', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - env_template_id = jsonutils.loads(result.body)['id'] - self.assertTrue(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('clone_env_template') - body = {'name': 'clone', 'is_public': False} - req = self._post('/templates/%s/clone' % env_template_id, - jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertFalse(jsonutils.loads(result.body)['is_public']) - self.assertEqual('clone', jsonutils.loads(result.body)['name']) - - def test_clone_env_templates_private(self): - """Create an template, test templates.public().""" - self._set_policy_rules( - {'create_env_template': '@', - 'clone_env_template': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp2', 'is_public': False} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - env_template_id = jsonutils.loads(result.body)['id'] - self.assertFalse(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('clone_env_template') - body = {'name': 'clone', 'is_public': False} - req = self._post('/templates/%s/clone' % env_template_id, - jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(result.status_code, 403) - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices.' - 'clone') - def test_clone_env_templates_handle_duplicate_exc(self, mock_function): - """Test whether clone duplication exception is handled correctly.""" - mock_function.side_effect = db_exc.DBDuplicateEntry - - self._set_policy_rules( - {'create_env_template': '@', - 'clone_env_template': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp2', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - env_template_id = jsonutils.loads(result.body)['id'] - - self.expect_policy_check('clone_env_template') - body = {'name': 'clone', 'is_public': False} - req = self._post('/templates/%s/clone' % env_template_id, - jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(409, result.status_code) - - def test_list_public_env_templates_default(self): - """Test listing public templates when there aren't any - - Create a template; test list public with no - public templates. - """ - self._set_policy_rules( - {'create_env_template': '@', - 'list_env_templates': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp'} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertFalse(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('list_env_templates') - req = self._get('/templates', {'is_public': True}) - result = req.get_response(self.api) - - self.assertFalse(0, len(jsonutils.loads(result.body))) - - def test_list_private_env_templates(self): - """Test listing private templates - - Create a public template and a private template; - test list private templates. - """ - self._set_policy_rules( - {'create_env_template': '@', - 'list_env_templates': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp', 'is_public': False} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertFalse(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp1', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertTrue(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('list_env_templates') - req = self._get('/templates', {'is_public': False}) - result = req.get_response(self.api) - self.assertEqual(1, len(jsonutils.loads(result.body)['templates'])) - - def test_list_env_templates(self): - """Test listing public templates when there aren't any - - Create a template; test list public with no - public templates. - """ - self._set_policy_rules( - {'create_env_template': '@', - 'list_env_templates': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp', 'is_public': False} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertFalse(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp1', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertTrue(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('list_env_templates') - req = self._get('/templates') - result = req.get_response(self.api) - - self.assertEqual(2, len(jsonutils.loads(result.body)['templates'])) - - def test_list_env_templates_with_different_tenant(self): - """Test listing public template from another tenant - - Create two template in two different tenants; - test list public template from another tenant. - """ - self._set_policy_rules( - {'create_env_template': '@', - 'list_env_templates': '@'} - ) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp', 'is_public': False} - req = self._post('/templates', jsonutils.dump_as_bytes(body), - tenant='first_tenant') - result = req.get_response(self.api) - self.assertFalse(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('create_env_template') - body = {'name': 'mytemp1', 'is_public': True} - req = self._post('/templates', jsonutils.dump_as_bytes(body), - tenant='second_tenant') - result = req.get_response(self.api) - self.assertTrue(jsonutils.loads(result.body)['is_public']) - - self.expect_policy_check('list_env_templates') - req = self._get('/templates', tenant='first_tenant') - result = req.get_response(self.api) - - self.assertEqual(2, len(jsonutils.loads(result.body)['templates'])) - - def test_illegal_template_name_create(self): - """Check that an illegal temp name results in an HTTPClientError.""" - self._set_policy_rules( - {'list_env_templates': '@', - 'create_env_template': '@', - 'show_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - - body = {'name': ' '} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - def test_too_long_template_name_create(self): - """Check that a long template name results in an HTTPBadResquest.""" - self._set_policy_rules( - {'list_env_templates': '@', - 'create_env_template': '@', - 'show_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - - body = {'name': 'a' * 256} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('Environment template name should be 255 characters ' - 'maximum', - result_msg) - - def test_mallformed_body(self): - """Check that an illegal temp name results in an HTTPClientError.""" - self._set_policy_rules( - {'create_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - - body = {'invalid': 'test'} - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - def test_missing_env_template(self): - """Check that a missing env template results in an HTTPNotFound.""" - self._set_policy_rules( - {'show_env_template': '@'} - ) - self.expect_policy_check('show_env_template', - {'env_template_id': 'no-such-id'}) - - req = self._get('/templates/no-such-id') - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - def test_update_env_template(self): - """Check that environment rename works.""" - self._set_policy_rules( - {'show_env_template': '@', - 'update_env_template': '@'} - ) - self.expect_policy_check('update_env_template', - {'env_template_id': '12345'}) - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = dict( - id='12345', - is_public=False, - name='my-temp', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description_text='', - description={ - 'name': 'my-temp', - '?': {'id': '12345'} - } - ) - e = models.EnvironmentTemplate(**expected) - test_utils.save_models(e) - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - del expected['description'] - expected['services'] = [] - expected['name'] = 'renamed_temp' - expected['updated'] = fake_now - - body = { - 'name': 'renamed_temp' - } - req = self._put('/templates/12345', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - self.expect_policy_check('show_env_template', - {'env_template_id': '12345'}) - req = self._get('/templates/12345') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - expected['created'] = datetime.isoformat(expected['created'])[:-7] - expected['updated'] = datetime.isoformat(expected['updated'])[:-7] - - self.assertEqual(expected, jsonutils.loads(result.body)) - - def test_update_env_template_with_inappropriate_name(self): - """Check that environment rename works.""" - self._set_policy_rules( - {'show_env_template': '@', - 'update_env_template': '@'} - ) - self.expect_policy_check('update_env_template', - {'env_template_id': '12345'}) - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = dict( - id='12345', - is_public=False, - name='my-temp', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant - ) - e = models.EnvironmentTemplate(**expected) - test_utils.save_models(e) - - # Attempt to update the environment template with invalid name. - body = {'name': ''} - req = self._put('/templates/12345', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - self.assertIn(b'EnvTemplate body is incorrect', result.body) - - # Verify that the name was not changed. - self.expect_policy_check('show_env_template', - {'env_template_id': '12345'}) - req = self._get('/templates/12345') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(expected['name'], - jsonutils.loads(result.body)['name']) - - def test_delete_env_templates(self): - """Test that environment deletion results in the correct rpc call.""" - self._set_policy_rules( - {'delete_env_template': '@'} - ) - self.expect_policy_check( - 'delete_env_template', {'env_template_id': '12345'} - ) - - fake_now = timeutils.utcnow() - expected = dict( - id='12345', - name='my-temp', - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'name': 'my-temp', - '?': {'id': '12345'} - } - ) - e = models.EnvironmentTemplate(**expected) - test_utils.save_models(e) - - req = self._delete('/templates/12345') - result = req.get_response(self.api) - - # Should this be expected behavior? - self.assertEqual(b'', result.body) - self.assertEqual(200, result.status_code) - - def test_create_env_templates_with_applications(self): - """Create an template, test template.show().""" - self._set_policy_rules( - {'list_env_templates': '@', - 'create_env_template': '@', - 'show_env_template': '@'} - ) - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - expected = {'tenant_id': self.tenant, - 'id': self.uuids[0], - 'is_public': False, - 'name': 'env_template_name', - 'description_text': '', - 'version': 0, - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7]} - - services = [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.Linux", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - expected['services'] = services - - body = { - "name": "env_template_name", - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.Linux", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - } - - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(expected, jsonutils.loads(result.body)) - - # Reset the policy expectation - self.expect_policy_check('list_env_templates') - - req = self._get('/templates') - result = req.get_response(self.api) - del expected['services'] - self.assertEqual(200, result.status_code) - self.assertEqual({'templates': [expected]}, - jsonutils.loads(result.body)) - - # Reset the policy expectation - self.expect_policy_check('show_env_template', - {'env_template_id': self.uuids[0]}) - expected['services'] = services - req = self._get('/templates/%s' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(expected, jsonutils.loads(result.body)) - - def test_add_application_to_template(self): - """Create an template, test template.show().""" - self._set_policy_rules( - {'create_env_template': '@', - 'add_application': '@'} - ) - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - services = [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.Linux", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - - body = { - "name": "template_name", - } - - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - body = services - req = self._post('/templates/%s/services' % self.uuids[0], - jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - - self.assertEqual(200, result.status_code) - self.assertEqual(services, jsonutils.loads(result.body)) - req = self._get('/templates/%s/services' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(1, len(jsonutils.loads(result.body))) - - service_no_instance = [ - { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "name": "tomcat", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - - req = self._post('/templates/%s/services' % self.uuids[0], - jsonutils.dump_as_bytes(service_no_instance)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - req = self._get('/templates/%s/services' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(2, len(jsonutils.loads(result.body))) - - def test_delete_application_in_template(self): - """Create an template, test template.show().""" - self._set_policy_rules( - {'create_env_template': '@', - 'delete_env_application': '@'} - ) - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - body = { - "name": "mytemplate", - "services": [ - { - "name": "tomcat", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "54cea43d-5970-4c73-b9ac-fea656f3c722" - } - } - ] - } - - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(1, len(jsonutils.loads(result.body)['services'])) - - req = self._get('/templates/%s/services' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(1, len(jsonutils.loads(result.body))) - - service_id = '54cea43d-5970-4c73-b9ac-fea656f3c722' - req = self._get('/templates/' + self.uuids[0] + - '/services/' + service_id) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - req = self._delete('/templates/' + self.uuids[0] + - '/services/' + service_id) - result = req.get_response(self.api) - - self.assertEqual(200, result.status_code) - self.assertEqual(0, len(jsonutils.loads(result.body)['services'])) - - req = self._get('/templates/' + self.uuids[0] + - '/services/' + service_id) - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - def test_create_environment(self): - """Test that environment is created, session configured.""" - - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - self._set_policy_rules( - {'create_env_template': '@', - 'create_environment': '@'} - ) - - self._create_env_template_no_service() - body_env = {'name': 'my_template'} - - self.expect_policy_check('create_environment', - {'env_template_id': self.uuids[0]}) - req = self._post('/templates/%s/create-environment' % - self.uuids[0], jsonutils.dump_as_bytes(body_env)) - session_result = req.get_response(self.api) - self.assertEqual(200, session_result.status_code) - self.assertIsNotNone(session_result) - body_returned = jsonutils.loads(session_result.body) - self.assertEqual(self.uuids[4], body_returned['session_id']) - self.assertEqual(self.uuids[3], body_returned['environment_id']) - - @mock.patch('murano.db.services.environments.EnvironmentServices.create') - def test_create_environment_handle_duplicate_exc(self, mock_function): - """Test that duplicate entry exception is correctly handled.""" - mock_function.side_effect = db_exc.DBDuplicateEntry - - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - self._set_policy_rules( - {'create_env_template': '@', - 'create_environment': '@'} - ) - - self._create_env_template_no_service() - body_env = {'name': 'my_template'} - - self.expect_policy_check('create_environment', - {'env_template_id': self.uuids[0]}) - req = self._post('/templates/%s/create-environment' % - self.uuids[0], jsonutils.dump_as_bytes(body_env)) - session_result = req.get_response(self.api) - self.assertEqual(409, session_result.status_code) - - def test_create_env_with_template_and_services(self): - """Test env and session creation with services - - Test that environment is created and session with template - with services. - """ - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - self._set_policy_rules( - {'create_env_template': '@', - 'create_environment': '@'} - ) - self._create_env_template_services() - - self.expect_policy_check('create_environment', - {'env_template_id': self.uuids[0]}) - body = {'name': 'my_template'} - req = self._post('/templates/%s/create-environment' % - self.uuids[0], jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - - self.assertIsNotNone(result) - self.assertEqual(200, result.status_code) - body_returned = jsonutils.loads(result.body) - self.assertEqual(self.uuids[4], body_returned['session_id']) - self.assertEqual(self.uuids[3], body_returned['environment_id']) - - def test_create_env_with_template_no_services(self): - """Test env and session creation without services - - Test that environment is created and session with template - without services. - """ - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - self._set_policy_rules( - {'create_env_template': '@', - 'create_environment': '@'} - ) - self._create_env_template_no_service() - - self.expect_policy_check('create_environment', - {'env_template_id': self.uuids[0]}) - body = {'name': 'my_template'} - - req = self._post('/templates/%s/create-environment' % - self.uuids[0], jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertIsNotNone(result) - self.assertEqual(200, result.status_code) - body_returned = jsonutils.loads(result.body) - self.assertEqual(self.uuids[4], body_returned['session_id']) - self.assertEqual(self.uuids[3], body_returned['environment_id']) - - def test_update_service_in_template(self): - """Test the service is updated in the environment template""" - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - self._set_policy_rules( - {'create_env_template': '@', - 'update_service_env_template': '@'} - ) - updated_env = "UPDATED_ENV" - env_template = self._create_env_template_services() - self.expect_policy_check('update_service_env_template') - env_template["name"] = updated_env - - req = self._put('/templates/{0}/services/{1}'. - format(self.uuids[0], "service_id"), - jsonutils.dump_as_bytes(env_template)) - result = req.get_response(self.api) - self.assertIsNotNone(result) - self.assertEqual(200, result.status_code) - body_returned = jsonutils.loads(result.body) - self.assertEqual(updated_env, body_returned['name']) - - def test_mallformed_env_body(self): - """Check that an illegal temp name results in an HTTPClientError.""" - self._set_policy_rules( - {'create_env_template': '@', - 'create_environment': '@'} - ) - self._create_env_template_no_service() - - self.expect_policy_check('create_environment', - {'env_template_id': self.uuids[0]}) - body = {'invalid': 'test'} - req = self._post('/templates/%s/create-environment' % - self.uuids[0], jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - def test_delete_notexisting_service(self): - """Check deleting a not existing service, return a 404 error.""" - self._set_policy_rules( - {'create_env_template': '@', - 'delete_env_application': '@'} - ) - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - body = { - "name": "mytemplate", - "services": [ - { - "name": "tomcat", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "ID1" - } - } - ] - } - - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(1, len(jsonutils.loads(result.body)['services'])) - - req = self._delete('/templates/{0}/services/{1}'.format(self.uuids[0], - "NO_EXIST")) - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices.' - 'get_env_template') - def test_validate_request_handle_forbidden_exc(self, mock_function): - """Test whether forbidden exception is thrown with different tenant.""" - self._set_policy_rules( - {'create_env_template': '@', - 'show_env_template': '@', - 'show_env_template': '@'} - ) - - # If is_public is False, then exception should be thrown. - mock_env_template = mock.MagicMock(is_public=False, tenant_id=-1) - mock_function.return_value = mock_env_template - - self._create_env_template_no_service() - self.expect_policy_check('show_env_template', - {'env_template_id': self.uuids[0]}) - req = self._get('/templates/%s' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(result.status_code, 403) - - # If is_public is True, then no exception should be thrown. - mock_env_template = mock.MagicMock(is_public=True, tenant_id=-1) - mock_function.return_value = mock_env_template - - self.expect_policy_check('show_env_template', - {'env_template_id': self.uuids[0]}) - req = self._get('/templates/%s' % self.uuids[0]) - result = req.get_response(self.api) - self.assertEqual(result.status_code, 200) - - def test_create_env_notexisting_templatebody(self): - """Check that an illegal temp name results in an HTTPClientError.""" - self._set_policy_rules( - {'create_environment': '@'} - ) - env_template_id = 'noexisting' - self.expect_policy_check('create_environment', - {'env_template_id': env_template_id}) - - body = {'name': 'test'} - req = self._post('/templates/%s/create-environment' - % env_template_id, jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - def _create_env_template_no_service(self): - self.expect_policy_check('create_env_template') - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - req = self._post('/templates', - jsonutils.dump_as_bytes({'name': 'name'})) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - def _create_env_template_services(self): - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - self.expect_policy_check('create_env_template') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - body = { - "name": "env_template_name", - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.Linux", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "port": "8080", - "?": { - "type": "io.murano.apps.apache.Tomcat", - "id": "service_id" - } - } - ] - } - - req = self._post('/templates', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - return result.json diff --git a/murano/tests/unit/api/v1/test_environments.py b/murano/tests/unit/api/v1/test_environments.py deleted file mode 100644 index d67f7a721..000000000 --- a/murano/tests/unit/api/v1/test_environments.py +++ /dev/null @@ -1,825 +0,0 @@ -# -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 datetime import datetime - -from oslo_config import fixture as config_fixture -from oslo_serialization import jsonutils -from oslo_utils import timeutils - -from murano.api.v1 import environments -from murano.api.v1 import sessions -from murano.db import models -from murano.services import states -import murano.tests.unit.api.base as tb -import murano.tests.unit.utils as test_utils - - -class TestEnvironmentApi(tb.ControllerTest, tb.MuranoApiTestCase): - def setUp(self): - super(TestEnvironmentApi, self).setUp() - self.controller = environments.Controller() - self.sessions_controller = sessions.Controller() - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - def test_list_empty_environments(self): - """Check that with no environments an empty list is returned.""" - self._set_policy_rules( - {'list_environments': '@'} - ) - self.expect_policy_check('list_environments') - - req = self._get('/environments') - result = req.get_response(self.api) - self.assertEqual({'environments': []}, jsonutils.loads(result.body)) - - def test_list_all_tenants(self): - """Check whether all_tenants param is taken into account.""" - - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'list_environments_all_tenants': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'name': 'my_env'} - req = self._post('/environments', jsonutils.dump_as_bytes(body), - tenant="other") - req.get_response(self.api) - - self._check_listing(False, None, 'list_environments', 0) - self._check_listing(True, None, 'list_environments_all_tenants', 1) - - def test_list_given_tenant(self): - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'list_environments_all_tenants': '@'} - ) - self.expect_policy_check('create_environment') - self.expect_policy_check('create_environment') - self.expect_policy_check('create_environment') - - body = {'name': 'my_env1'} - req = self._post('/environments', jsonutils.dump_as_bytes(body), - tenant="foo") - req.get_response(self.api) - body = {'name': 'my_env2'} - req = self._post('/environments', jsonutils.dump_as_bytes(body), - tenant="bar") - req.get_response(self.api) - - body = {'name': 'my_env3'} - req = self._post('/environments', jsonutils.dump_as_bytes(body), - tenant="bar") - req.get_response(self.api) - - self._check_listing(False, "foo", 'list_environments_all_tenants', 1) - self._check_listing(False, "bar", 'list_environments_all_tenants', 2) - self._check_listing(False, "other", 'list_environments_all_tenants', 0) - - def _check_listing(self, all_tenants, tenant, expected_check, - expected_count): - self.expect_policy_check(expected_check) - params = {'all_tenants': all_tenants} - if tenant: - params['tenant'] = tenant - req = self._get('/environments', params) - response = req.get_response(self.api) - body = jsonutils.loads(response.body) - self.assertEqual(200, response.status_code) - self.assertEqual(expected_count, len(body['environments'])) - - def test_create_environment(self): - """Create an environment, test environment.show().""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - uuids = ('env_object_id', 'network_id', 'environment_id') - mock_uuid = self._stub_uuid(uuids) - - expected = {'tenant_id': self.tenant, - 'id': 'environment_id', - 'name': 'my_env', - 'description_text': 'description', - 'version': 0, - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7], - } - - body = {'name': 'my_env', 'description_text': 'description'} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(expected, jsonutils.loads(result.body)) - - expected['status'] = 'ready' - - # Reset the policy expectation - self.expect_policy_check('list_environments') - - req = self._get('/environments') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual({'environments': [expected]}, - jsonutils.loads(result.body)) - - expected['services'] = [] - expected['acquired_by'] = None - - # Reset the policy expectation - self.expect_policy_check('show_environment', - {'environment_id': uuids[-1]}) - - req = self._get('/environments/%s' % uuids[-1]) - result = req.get_response(self.api) - - self.assertEqual(expected, jsonutils.loads(result.body)) - self.assertEqual(3, mock_uuid.call_count) - - def test_illegal_environment_name_create(self): - """Check that an illegal env name results in an HTTPClientError.""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'name': ' '} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - - def test_unicode_environment_name_create(self): - """Check that an unicode env name doesn't raise an HTTPClientError.""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'name': u'$yaql \u2665 unicode'} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - def test_no_environment_name_create(self): - """Check that no env name provided results in an HTTPBadResquest.""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'no_name': 'fake'} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('Please, specify a name of the environment to create', - result_msg) - - def test_too_long_environment_name_create(self): - """Check that a too long env name results in an HTTPBadResquest.""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'name': 'a' * 256} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('Environment name should be 255 characters maximum', - result_msg) - - def test_create_environment_with_empty_body(self): - """Check that empty request body results in an HTTPBadResquest.""" - body = b'' - req = self._post('/environments', body) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('The server could not comply with the request since it ' - 'is either malformed or otherwise incorrect.', - result_msg) - - def test_create_environment_duplicate_name(self): - """Check that duplicate names results in HTTPConflict""" - self._set_policy_rules( - {'list_environments': '@', - 'create_environment': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - body = {'name': u'my_env_dup'} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - self.expect_policy_check('create_environment') - body = {'name': u'my_env_dup'} - req = self._post('/environments', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(409, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('Environment with specified name already exists', - result_msg) - - def test_missing_environment(self): - """Check that a missing environment results in an HTTPNotFound. - - Environment check will be made in the decorator and raises, - no need to check policy in this testcase. - """ - req = self._get('/environments/no-such-id') - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - def test_update_environment(self): - """Check that environment rename works.""" - self._set_policy_rules( - {'show_environment': '@', - 'update_environment': '@'} - ) - self.expect_policy_check('update_environment', - {'environment_id': '12345'}) - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - expected = dict( - id='12345', - name='my-env', - version=0, - description_text='', - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '12345'} - }, - 'Attributes': [] - } - ) - e = models.Environment(**expected) - test_utils.save_models(e) - - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - del expected['description'] - expected['services'] = [] - expected['status'] = 'ready' - expected['name'] = 'renamed_env' - expected['updated'] = fake_now - - body = { - 'name': 'renamed_env' - } - req = self._put('/environments/12345', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - self.expect_policy_check('show_environment', - {'environment_id': '12345'}) - req = self._get('/environments/12345') - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - expected['created'] = datetime.isoformat(expected['created'])[:-7] - expected['updated'] = datetime.isoformat(expected['updated'])[:-7] - expected['acquired_by'] = None - - self.assertEqual(expected, jsonutils.loads(result.body)) - - def test_update_environment_with_invalid_name(self): - """Test that invalid env name returns HTTPBadRequest - - Check that update an invalid env name results in - an HTTPBadRequest. - """ - self._set_policy_rules( - {'update_environment': '@'} - ) - - self._create_fake_environment('env1', '111') - - self.expect_policy_check('update_environment', - {'environment_id': '111'}) - - body = { - 'name': ' ' - } - req = self._put('/environments/111', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = ('Environment name must contain at least one ' - 'non-white space symbol') - self.assertIn(msg, result_msg) - - def test_update_environment_with_existing_name(self): - self._set_policy_rules( - {'update_environment': '@'} - ) - - self._create_fake_environment('env1', '111') - self._create_fake_environment('env2', '222') - - self.expect_policy_check('update_environment', - {'environment_id': '111'}) - - body = { - 'name': 'env2' - } - req = self._put('/environments/111', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(409, result.status_code) - - def test_too_long_environment_name_update(self): - """Test updating too long env name - - Check that update a too long env name results in - an HTTPBadResquest. - """ - self._set_policy_rules( - {'update_environment': '@'} - ) - - self._create_fake_environment('env1', '111') - - self.expect_policy_check('update_environment', - {'environment_id': '111'}) - new_name = 'env1' * 64 - - body = { - 'name': new_name - } - req = self._put('/environments/111', jsonutils.dump_as_bytes(body)) - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - self.assertIn('Environment name should be 255 characters maximum', - result_msg) - - def test_delete_environment(self): - """Test that environment deletion results in the correct rpc call.""" - result = self._test_delete_or_abandon(abandon=False) - self.assertEqual(b'', result.body) - self.assertEqual(200, result.status_code) - - def test_abandon_environment(self): - """Check that abandon feature works""" - result = self._test_delete_or_abandon(abandon=True) - self.assertEqual(b'', result.body) - self.assertEqual(200, result.status_code) - - def test_abandon_environment_of_different_tenant(self): - """Test abandon environment belongs to another tenant.""" - result = self._test_delete_or_abandon(abandon=True, tenant='not_match') - self.assertEqual(403, result.status_code) - self.assertIn((b'User is not authorized to access these' - b' tenant resources'), result.body) - - def test_get_last_status_of_different_tenant(self): - """Test get last services status of env belongs to another tenant.""" - self._create_fake_environment('env1', '111') - req = self._get('/environments/111/lastStatus', tenant='not_match') - result = req.get_response(self.api) - self.assertEqual(403, result.status_code) - self.assertIn((b'User is not authorized to access these' - b' tenant resources'), result.body) - - def test_get_environment(self): - """Test GET request of an environment in ready status""" - self._set_policy_rules( - {'show_environment': '@'} - ) - self.expect_policy_check('show_environment', - {'environment_id': '123'}) - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - env_id = '123' - self._create_fake_environment(env_id=env_id) - req = self._get('/environments/{0}'.format(env_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - expected = {'tenant_id': self.tenant, - 'id': env_id, - 'name': 'my-env', - 'version': 0, - 'description_text': '', - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7], - 'acquired_by': None, - 'services': [], - 'status': 'ready', - } - self.assertEqual(expected, jsonutils.loads(result.body)) - - def test_get_environment_acquired(self): - """Test GET request of an environment in deploying status""" - self._set_policy_rules( - {'show_environment': '@'} - ) - self.expect_policy_check('show_environment', - {'environment_id': '1234'}) - fake_now = timeutils.utcnow() - timeutils.utcnow.override_time = fake_now - - env_id = '1234' - self._create_fake_environment(env_id=env_id) - - sess_id = '321' - expected = dict( - id=sess_id, - environment_id=env_id, - version=0, - state=states.SessionState.DEPLOYING, - user_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '{0}'.format(env_id)} - }, - 'Attributes': {} - } - ) - s = models.Session(**expected) - test_utils.save_models(s) - - req = self._get('/environments/{0}'.format(env_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - expected = {'tenant_id': self.tenant, - 'id': env_id, - 'name': 'my-env', - 'version': 0, - 'description_text': '', - 'created': datetime.isoformat(fake_now)[:-7], - 'updated': datetime.isoformat(fake_now)[:-7], - 'acquired_by': sess_id, - 'services': [], - 'status': states.EnvironmentStatus.DEPLOYING, - } - self.assertEqual(expected, jsonutils.loads(result.body)) - - def _create_fake_environment(self, env_name='my-env', env_id='123'): - fake_now = timeutils.utcnow() - expected = dict( - id=env_id, - name=env_name, - version=0, - created=fake_now, - updated=fake_now, - tenant_id=self.tenant, - description={ - 'Objects': { - '?': {'id': '{0}'.format(env_id)} - }, - 'Attributes': {} - } - ) - e = models.Environment(**expected) - test_utils.save_models(e) - - def _test_delete_or_abandon(self, abandon, env_name='my-env', - env_id='123', tenant=None): - self._set_policy_rules( - {'delete_environment': '@'} - ) - self.expect_policy_check( - 'delete_environment', - {'environment_id': '{0}'.format(env_id)} - ) - - self._create_fake_environment(env_name, env_id) - - path = '/environments/{0}'.format(env_id) - - req = self._delete(path, params={'abandon': abandon}, - tenant=tenant or self.tenant) - result = req.get_response(self.api) - - return result - - def test_last_status_session(self): - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - - self._set_policy_rules({'create_environment': '@'}) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], - response_body['tenant_id']) - ENVIRONMENT_ID = response_body['id'] - - # Create session - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - SESSION_ID = response_body['id'] - - # Test getting last status doesn't error - request = self._get( - '/environments/{environment_id}/lastStatus' - .format(environment_id=ENVIRONMENT_ID), - **CREDENTIALS - ) - request.headers['X-Configuration-Session'] = str(SESSION_ID) - request.context.session = SESSION_ID - - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertIsNotNone(response_body) - - def test_show_environments_session(self): - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - - self._set_policy_rules( - {'create_environment': '@', - 'list_environments': '@', - 'show_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], - response_body['tenant_id']) - ENVIRONMENT_ID = response_body['id'] - - # Create session - self.expect_policy_check( - 'show_environment', {'environment_id': ENVIRONMENT_ID}) - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - SESSION_ID = response_body['id'] - - # Show the environment and test that it is correct. - request = self._get( - '/environments/{environment_id}' - .format(environment_id=ENVIRONMENT_ID), - **CREDENTIALS - ) - request.headers['X-Configuration-Session'] = str(SESSION_ID) - request.context.session = SESSION_ID - - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(ENVIRONMENT_ID, response_body['id']) - - def _create_env_and_session(self): - creds = {'tenant': 'test_tenant', 'user': 'test_user'} - - self._set_policy_rules( - {'show_environment': '@', - 'update_environment': '@'} - ) - - env_id = '123' - self._create_fake_environment(env_id=env_id) - - # Create session - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=env_id), b'', - **creds) - response_body = jsonutils.loads(request.get_response(self.api).body) - session_id = response_body['id'] - return env_id, session_id - - def test_get_and_update_environment_model(self): - """Test GET and PATCH requests of an environment object model""" - env_id, session_id = self._create_env_and_session() - - # Get entire env's model - self.expect_policy_check('show_environment', - {'environment_id': '123'}) - req = self._get('/environments/{0}/model/'.format(env_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - expected = {'?': {'id': '{0}'.format(env_id)}} - self.assertEqual(expected, jsonutils.loads(result.body)) - - # Add some data to the '?' section of env's model - self.expect_policy_check('update_environment', - {'environment_id': '123'}) - data = [{ - "op": "add", - "path": "/?/name", - "value": 'my_env' - }] - - expected = { - 'id': '{0}'.format(env_id), - 'name': 'my_env' - } - - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - observed = jsonutils.loads(result.body)['?'] - self.assertEqual(expected, observed) - - # Check that changes are stored in session - self.expect_policy_check('show_environment', - {'environment_id': '123'}) - req = self._get('/environments/{0}/model/{1}'.format( - env_id, '/?')) - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - self.assertEqual(expected, jsonutils.loads(result.body)) - - # Check that actual model remains unchanged - self.expect_policy_check('show_environment', - {'environment_id': '123'}) - req = self._get('/environments/{0}/model/{1}'.format( - env_id, '/?')) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - expected = {'id': '{0}'.format(env_id)} - self.assertEqual(expected, jsonutils.loads(result.body)) - - def test_get_environment_model_non_existing_path(self): - env_id, session_id = self._create_env_and_session() - - # Try to get non-existing section of env's model - self.expect_policy_check('show_environment', - {'environment_id': '123'}) - path = 'foo/bar' - req = self._get('/environments/{0}/model/{1}'.format( - env_id, path)) - result = req.get_response(self.api) - self.assertEqual(404, result.status_code) - - def test_update_environment_model_empty_body(self): - env_id, session_id = self._create_env_and_session() - data = None - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = "JSON-patch must be a list." - self.assertIn(msg, result_msg) - - def test_update_environment_model_no_patch(self): - env_id, session_id = self._create_env_and_session() - data = ["foo"] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = "Operations must be JSON objects." - self.assertIn(msg, result_msg) - - def test_update_environment_model_no_op(self): - env_id, session_id = self._create_env_and_session() - data = [{ - "path": "/?/name", - "value": 'my_env' - }] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = "Unable to find 'op' in JSON Schema change" - self.assertIn(msg, result_msg) - - def test_update_environment_model_no_path(self): - env_id, session_id = self._create_env_and_session() - data = [{ - "op": "add", - "value": 'my_env' - }] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = "Unable to find 'path' in JSON Schema change" - self.assertIn(msg, result_msg) - - def test_update_environment_model_no_value(self): - env_id, session_id = self._create_env_and_session() - data = [{ - "op": "add", - "path": "/?/name" - }] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = 'Operation "add" requires a member named "value".' - self.assertIn(msg, result_msg) - - def test_update_environment_model_forbidden_operation(self): - env_id, session_id = self._create_env_and_session() - data = [{ - "op": "add", - "path": "/?/id", - "value": "foo" - }] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(403, result.status_code) - result_msg = result.text.replace('\n', '') - msg = ("Method 'add' is not allowed for a path with name '?/id'. " - "Allowed operations are: no operations") - self.assertIn(msg, result_msg) - - def test_update_environment_model_invalid_schema(self): - env_id, session_id = self._create_env_and_session() - data = [{ - "op": "add", - "path": "/?/name", - "value": 111 - }] - req = self._patch('/environments/{0}/model/'.format(env_id), - jsonutils.dump_as_bytes(data), - content_type='application/env-model-json-patch') - req.headers['X-Configuration-Session'] = str(session_id) - req.context.session = session_id - result = req.get_response(self.api) - self.assertEqual(400, result.status_code) - result_msg = result.text.replace('\n', '') - msg = "111 is not of type 'string'" - self.assertIn(msg, result_msg) diff --git a/murano/tests/unit/api/v1/test_instance_statistics.py b/murano/tests/unit/api/v1/test_instance_statistics.py deleted file mode 100644 index a7371c6d6..000000000 --- a/murano/tests/unit/api/v1/test_instance_statistics.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 murano.api.v1 import instance_statistics -import murano.tests.unit.api.base as tb - - -class TestInstanceStatistics(tb.ControllerTest, tb.MuranoApiTestCase): - def setUp(self): - super(TestInstanceStatistics, self).setUp() - self.controller = instance_statistics.Controller() - - def test_get_aggregated(self): - self._set_policy_rules( - {"get_aggregated_statistics": "@"} - ) - self.expect_policy_check("get_aggregated_statistics", - {'environment_id': u'12344'}) - env_id = 12344 - - req = self._get('/environments/{env_id}/' - 'instance-statistics/aggregated' - .format(env_id=env_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - def test_get_for_instance(self): - self._set_policy_rules( - {"get_instance_statistics": "@"} - ) - self.expect_policy_check("get_instance_statistics", - {'environment_id': u'12345', - 'instance_id': u'12'}) - env_id = 12345 - ins_id = 12 - - req = self._get('/environments/{env_id}/' - 'instance-statistics/raw/{ins_id}' - .format(env_id=env_id, ins_id=ins_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) - - def test_get_for_env(self): - self._set_policy_rules( - {"get_statistics": "@"} - ) - self.expect_policy_check("get_statistics", - {'environment_id': u'12346'}) - env_id = 12346 - - req = self._get('/environments/{env_id}/instance-statistics/raw' - .format(env_id=env_id)) - result = req.get_response(self.api) - self.assertEqual(200, result.status_code) diff --git a/murano/tests/unit/api/v1/test_schemas.py b/murano/tests/unit/api/v1/test_schemas.py deleted file mode 100644 index 5e27725b5..000000000 --- a/murano/tests/unit/api/v1/test_schemas.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 unittest import mock - -from oslo_messaging.rpc import client -from webob import exc - -from murano.api.v1 import schemas -import murano.tests.unit.base as test_base -from murano.tests.unit import utils as test_utils - - -@mock.patch('murano.api.v1.schemas.policy') -@mock.patch('murano.api.v1.schemas.request_statistics.update_error_count') -@mock.patch('murano.api.v1.schemas.request_statistics.update_count') -class TestSchemas(test_base.MuranoTestCase): - - @classmethod - def setUpClass(cls): - super(TestSchemas, cls).setUpClass() - cls.controller = schemas.Controller() - - @mock.patch('murano.api.v1.schemas.rpc') - def test_get_schema(self, mock_rpc, *args): - dummy_context = test_utils.dummy_context() - dummy_context.GET = { - 'classVersion': 'test_class_version', - 'packageName': 'test_package_name' - } - mock_request = mock.MagicMock(context=dummy_context) - mock_rpc.engine().generate_schema.return_value = 'test_schema' - - result = self.controller.get_schema(mock_request, 'test_class') - self.assertEqual('test_schema', result) - - @mock.patch('murano.api.v1.schemas.rpc') - def test_get_schema_negative(self, mock_rpc, *args): - dummy_context = test_utils.dummy_context() - dummy_context.GET = { - 'classVersion': 'test_class_version', - 'packageName': 'test_package_name' - } - mock_request = mock.MagicMock(context=dummy_context) - - # Test exception handling for pre-defined exception types. - exc_types = ('NoClassFound', - 'NoPackageForClassFound', - 'NoPackageFound') - for exc_type in exc_types: - dummy_error = client.RemoteError(exc_type=exc_type, - value='dummy_value') - mock_rpc.engine().generate_schema.side_effect = dummy_error - with self.assertRaisesRegex(exc.HTTPNotFound, - dummy_error.value): - self.controller.get_schema(mock_request, 'test_class') - - # Test exception handling for miscellaneous exception type. - dummy_error = client.RemoteError(exc_type='TestExcType', - value='dummy_value') - mock_rpc.engine().generate_schema.side_effect = dummy_error - with self.assertRaisesRegex(client.RemoteError, - dummy_error.value): - self.controller.get_schema(mock_request, 'test_class') diff --git a/murano/tests/unit/api/v1/test_services.py b/murano/tests/unit/api/v1/test_services.py deleted file mode 100644 index c75ba11ff..000000000 --- a/murano/tests/unit/api/v1/test_services.py +++ /dev/null @@ -1,225 +0,0 @@ -# Copyright (c) 2016 AT&T Inc. -# -# Licensed 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 oslo_config import fixture as config_fixture -from oslo_serialization import jsonutils -from webob import exc - -from murano.api.v1 import environments -from murano.api.v1 import services -from murano.api.v1 import sessions -import murano.tests.unit.api.base as tb - - -class TestServicesApi(tb.ControllerTest, tb.MuranoApiTestCase): - - def setUp(self): - super(TestServicesApi, self).setUp() - self.environments_controller = environments.Controller() - self.sessions_controller = sessions.Controller() - self.services_controller = services.Controller() - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - def test_can_post(self): - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - - environment_id = response_body['id'] - - # Create session - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=environment_id), b'', - **CREDENTIALS_1) - response_body = jsonutils.loads(request.get_response(self.api).body) - - session_id = response_body['id'] - - path = "/" - - request = self._post('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - self.assertRaises(exc.HTTPBadRequest, self.services_controller.post, - request, environment_id, path) - - response = self.services_controller.post(request, environment_id, - path, "test service") - self.assertEqual("test service", response) - - def test_can_put(self): - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - - environment_id = response_body['id'] - - # Create session - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=environment_id), b'', - **CREDENTIALS_1) - response_body = jsonutils.loads(request.get_response(self.api).body) - - session_id = response_body['id'] - - path = "/" - - request = self._put('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - # Check that empty body can be put - response = self.services_controller.put(request, environment_id, - path, []) - self.assertEqual([], response) - - response = self.services_controller.put(request, environment_id, - path, "test service") - self.assertEqual("test service", response) - - def test_can_get(self): - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - - environment_id = response_body['id'] - - # Create session - request = self._post('/environments/{environment_id}/configure' - .format(environment_id=environment_id), b'', - **CREDENTIALS_1) - response_body = jsonutils.loads(request.get_response(self.api).body) - - session_id = response_body['id'] - - # Create service - path = '/' - request = self._post('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - response = self.services_controller.post(request, environment_id, - path, "test service") - # Get service - request = self._get('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - - response = self.services_controller.get(request, environment_id, path) - self.assertEqual([], response) - - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - response = self.services_controller.get(request, environment_id, path) - self.assertNotEqual([], response) - - def test_can_delete(self): - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - - environment_id = response_body['id'] - - # Create session - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=environment_id), - b'', **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - - session_id = response_body['id'] - - # Create service - path = '/' - request = self._post('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - self.services_controller.post(request, environment_id, - path, "test service") - - # Delete service - request = self._delete('/v1/environments/{0}/services'. - format(environment_id), b'', - **CREDENTIALS_1) - - self.assertRaises(exc.HTTPBadRequest, self.services_controller.delete, - request, environment_id, path) - - request.headers['X-Configuration-Session'] = str(session_id) - request.context.session = session_id - - self.assertRaises(exc.HTTPNotFound, self.services_controller.delete, - request, environment_id, path) diff --git a/murano/tests/unit/api/v1/test_sessions.py b/murano/tests/unit/api/v1/test_sessions.py deleted file mode 100644 index cec8923a1..000000000 --- a/murano/tests/unit/api/v1/test_sessions.py +++ /dev/null @@ -1,328 +0,0 @@ -# Copyright (c) 2015 Mirantis Inc. -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 unittest import mock - -from oslo_config import fixture as config_fixture -from oslo_serialization import jsonutils -from webob import exc - -from murano.api.v1 import environments -from murano.api.v1 import sessions -from murano.services import states -import murano.tests.unit.api.base as tb -from murano.tests.unit import utils as test_utils - - -class TestSessionsApi(tb.ControllerTest, tb.MuranoApiTestCase): - def setUp(self): - super(TestSessionsApi, self).setUp() - self.environments_controller = environments.Controller() - self.sessions_controller = sessions.Controller() - self.fixture = self.useFixture(config_fixture.Config()) - self.fixture.conf(args=[]) - - def test_cant_deploy_from_another_tenant(self): - """Test to prevent deployment under another tenant user's creds - - If user from one tenant uses session id and environment id - of user from another tenant - he is not able to deploy - the environment. - - Bug: #1382026 - """ - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - CREDENTIALS_2 = {'tenant': 'test_tenant_2', 'user': 'test_user_2'} - - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment for user #1 - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - - ENVIRONMENT_ID = response_body['id'] - - # Create session of user #1 - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - - SESSION_ID = response_body['id'] - - # Deploy the environment using environment id and session id of user #1 - # by user #2 - request = self._post( - '/environments/{environment_id}/sessions/' - '{session_id}/deploy' - .format(environment_id=ENVIRONMENT_ID, session_id=SESSION_ID), - b'', - **CREDENTIALS_2 - ) - response = request.get_response(self.api) - - # Should be forbidden! - self.assertEqual(403, response.status_code) - - def test_session_show(self): - CREDENTIALS_1 = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - CREDENTIALS_2 = {'tenant': 'test_tenant_2', 'user': 'test_user_2'} - - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment for user #1 - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS_1['tenant'], - response_body['tenant_id']) - ENVIRONMENT_ID = response_body['id'] - - # Create session of user #1 - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - SESSION_ID = response_body['id'] - - # Show environment with correct credentials - request = self._get( - '/environments/{environment_id}/sessions/{session_id}' - .format(environment_id=ENVIRONMENT_ID, session_id=SESSION_ID), - b'', - **CREDENTIALS_1 - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(SESSION_ID, response_body['id']) - - # Show environment with incorrect credentials - request = self._get( - '/environments/{environment_id}/sessions/{session_id}' - .format(environment_id=ENVIRONMENT_ID, session_id=SESSION_ID), - b'', - **CREDENTIALS_2 - ) - response = request.get_response(self.api) - self.assertEqual(403, response.status_code) - - def test_session_delete(self): - CREDENTIALS = {'tenant': 'test_tenant_1', 'user': 'test_user_1'} - - self._set_policy_rules( - {'create_environment': '@'} - ) - self.expect_policy_check('create_environment') - - # Create environment - request = self._post( - '/environments', - jsonutils.dump_as_bytes({'name': 'test_environment_1'}), - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - self.assertEqual(CREDENTIALS['tenant'], - response_body['tenant_id']) - ENVIRONMENT_ID = response_body['id'] - - # Create session - request = self._post( - '/environments/{environment_id}/configure' - .format(environment_id=ENVIRONMENT_ID), - b'', - **CREDENTIALS - ) - response_body = jsonutils.loads(request.get_response(self.api).body) - SESSION_ID = response_body['id'] - - # Delete session - request = self._delete( - '/environments/{environment_id}/delete/{session_id}' - .format(environment_id=ENVIRONMENT_ID, session_id=SESSION_ID), - b'', - **CREDENTIALS - ) - response = self.sessions_controller.delete( - request, ENVIRONMENT_ID, SESSION_ID) - - # Make sure the session was deleted - request = self._get( - '/environments/{environment_id}/sessions/{session_id}' - .format(environment_id=ENVIRONMENT_ID, session_id=SESSION_ID), - b'', - **CREDENTIALS - ) - response = request.get_response(self.api) - self.assertEqual(404, response.status_code) - - @mock.patch('murano.api.v1.sessions.sessions.SessionServices') - @mock.patch('murano.api.v1.sessions.envs') - @mock.patch('murano.api.v1.sessions.check_env') - def test_configure(self, _, mock_envs, mock_session_services): - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_session = mock.MagicMock(to_dict=mock.MagicMock( - return_value={'test_env_id', 'test_user_id'})) - mock_session_services.create.return_value = mock_session - - result = self.sessions_controller.configure( - mock_request, 'test_env_id') - self.assertEqual({'test_env_id', 'test_user_id'}, result) - - @mock.patch('murano.api.v1.sessions.envs') - @mock.patch('murano.api.v1.sessions.check_env') - def test_configure_with_env_in_illegal_state(self, _, mock_envs): - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - illegal_states = [states.EnvironmentStatus.DEPLOYING, - states.EnvironmentStatus.DELETING] - expected_error_msg = 'Could not open session for environment '\ - ', environment has '\ - 'deploying or deleting status.'\ - .format(env_id='test_env_id') - - for state in illegal_states: - mock_envs.EnvironmentServices.get_status.return_value = state - - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_msg): - self.sessions_controller.configure(mock_request, 'test_env_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.db_session') - def test_show_with_invalid_user(self, mock_db_session, _): - mock_session = mock.MagicMock(user_id='test_user_id') - mock_db_session.get_session().query().get.return_value = mock_session - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.user = 'another_test_user_id' - expected_error_msg = 'User is not authorized to '\ - 'access session .'\ - .format(mock_request.context.user, 'test_sess_id') - - with self.assertRaisesRegex(exc.HTTPUnauthorized, - expected_error_msg): - self.sessions_controller.show(mock_request, None, 'test_sess_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.sessions.SessionServices') - @mock.patch('murano.api.v1.sessions.db_session') - def test_show_with_invalid_session(self, mock_db_session, - mock_session_services, _): - mock_session = mock.MagicMock(user_id='test_user_id') - mock_db_session.get_session().query().get.return_value = mock_session - mock_session_services.validate.return_value = False - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.user = 'test_user_id' - expected_error_msg = 'Session is invalid: environment'\ - ' has been updated or updating right now with'\ - ' other session'.format('test_sess_id') - - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_msg): - self.sessions_controller.show(mock_request, None, 'test_sess_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.db_session') - def test_delete_with_invalid_user(self, mock_db_session, _): - mock_session = mock.MagicMock(user_id='test_user_id') - mock_db_session.get_session().query().get.return_value = mock_session - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.user = 'another_test_user_id' - expected_error_msg = 'User is not authorized to '\ - 'access session .'\ - .format(mock_request.context.user, 'test_sess_id') - - with self.assertRaisesRegex(exc.HTTPUnauthorized, - expected_error_msg): - self.sessions_controller.delete(mock_request, None, 'test_sess_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.sessions.SessionServices') - @mock.patch('murano.api.v1.sessions.db_session') - def test_delete_with_deploying_session(self, mock_db_session, - mock_session_services, _): - mock_session = mock.MagicMock(user_id='test_user_id', - state=states.SessionState.DEPLOYING) - mock_db_session.get_session().query().get.return_value = mock_session - mock_session_services.validate.return_value = False - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.user = 'test_user_id' - expected_error_msg = 'Session is in deploying '\ - 'state and could not be deleted'\ - .format('test_sess_id') - - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_msg): - self.sessions_controller.delete(mock_request, None, 'test_sess_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.sessions.SessionServices') - @mock.patch('murano.api.v1.sessions.db_session') - def test_deploy_with_invalid_session(self, mock_db_session, - mock_session_services, _): - mock_db_session.get_session().query().get.return_value = None - mock_session_services.validate.return_value = False - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - expected_error_msg = 'Session is invalid: environment'\ - ' has been updated or updating right now with'\ - ' other session'.format('test_sess_id') - - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_msg): - self.sessions_controller.deploy(mock_request, None, 'test_sess_id') - - @mock.patch('murano.api.v1.sessions.check_session') - @mock.patch('murano.api.v1.sessions.sessions.SessionServices') - @mock.patch('murano.api.v1.sessions.db_session') - def test_deploy_with_session_in_invalid_state(self, mock_db_session, - mock_session_services, _): - mock_session_services.validate.return_value = True - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - expected_error_msg = 'Session is already deployed or '\ - 'deployment is in progress'.format('test_sess_id') - - invalid_states = [states.SessionState.DEPLOYING, - states.SessionState.DEPLOYED, - states.SessionState.DEPLOY_FAILURE, - states.SessionState.DELETING, - states.SessionState.DELETE_FAILURE] - - for state in invalid_states: - mock_session = mock.MagicMock(state=state) - mock_db_session.get_session().query().get.return_value =\ - mock_session - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_msg): - self.sessions_controller.deploy( - mock_request, None, 'test_sess_id') diff --git a/murano/tests/unit/api/v1/test_static_actions.py b/murano/tests/unit/api/v1/test_static_actions.py deleted file mode 100644 index 726d5871f..000000000 --- a/murano/tests/unit/api/v1/test_static_actions.py +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 unittest import mock - -from oslo_messaging.rpc import client -from oslo_serialization import jsonutils -from webob import exc - -from murano.api.v1 import static_actions -from murano.common import policy -import murano.tests.unit.api.base as tb - - -@mock.patch.object(policy, 'check') -class TestStaticActionsApi(tb.ControllerTest, tb.MuranoApiTestCase): - - def setUp(self): - super(TestStaticActionsApi, self).setUp() - self.controller = static_actions.Controller() - - def test_execute_static_action(self, mock_policy_check): - """Test that action execution results in the correct rpc call.""" - self._set_policy_rules( - {'execute_action': '@'} - ) - - action = { - 'method': 'TestAction', - 'args': {'name': 'John'}, - 'class_name': 'TestClass', - 'pkg_name': 'TestPackage', - 'class_version': '=0' - } - rpc_task = { - 'action': action, - 'token': None, - 'project_id': 'test_tenant', - 'user_id': 'test_user', - 'id': mock.ANY - } - - request_data = { - "className": 'TestClass', - "methodName": 'TestAction', - "packageName": 'TestPackage', - "classVersion": '=0', - "parameters": {'name': 'John'} - } - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - try: - self.controller.execute(req, request_data) - except TypeError: - pass - self.mock_engine_rpc.call_static_action.assert_called_once_with( - rpc_task) - - def test_execute_static_action_handle_bad_data_exc(self, _): - request_data = { - "className": None, - "methodName": 'TestAction' - } - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPBadRequest, self.controller.execute, req, - request_data) - - request_data = { - "className": 'TestClass', - "methodName": None - } - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPBadRequest, self.controller.execute, req, - request_data) - - @mock.patch('murano.services.static_actions.StaticActionServices.execute') - def test_execute_static_action_handle_execute_excs(self, mock_execute, _): - """Test whether execute handles all exceptions thrown correctly.""" - request_data = { - "className": 'TestClass', - "methodName": 'TestAction', - "packageName": 'TestPackage', - "classVersion": '=0', - "parameters": {'name': 'John'} - } - - exc_types = ['NoClassFound', 'NoMethodFound', - 'NoPackageFound', 'NoPackageForClassFound', - 'MethodNotExposed', 'NoMatchingMethodException'] - for exc_type in exc_types: - mock_execute.side_effect = client.RemoteError(exc_type=exc_type) - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPNotFound, self.controller.execute, req, - request_data) - self.assertEqual(mock_execute.call_count, len(exc_types)) - - exc_type = 'ContractViolationException' - mock_execute.side_effect = client.RemoteError(exc_type=exc_type) - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPBadRequest, self.controller.execute, req, - request_data) - exc_types.append(exc_type) - self.assertEqual(mock_execute.call_count, len(exc_types)) - - exc_type = 'ThisIsARandomTestException' - mock_execute.side_effect = client.RemoteError(exc_type=exc_type) - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPServiceUnavailable, self.controller.execute, - req, request_data) - exc_types.append(exc_type) - self.assertEqual(mock_execute.call_count, len(exc_types)) - - try: - int('this will throw a value error') - except ValueError as e: - setattr(e, 'message', None) - exc_type = e - mock_execute.side_effect = exc_type - req = self._post('/actions', jsonutils.dump_as_bytes(request_data)) - self.assertRaises(exc.HTTPBadRequest, self.controller.execute, - req, request_data) - exc_types.append(exc_type) - self.assertEqual(mock_execute.call_count, len(exc_types)) diff --git a/murano/tests/unit/base.py b/murano/tests/unit/base.py deleted file mode 100644 index 3d58dfd21..000000000 --- a/murano/tests/unit/base.py +++ /dev/null @@ -1,55 +0,0 @@ -# Licensed 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 fixtures -from oslo_log import log as logging -import testtools - -from murano.common import config -from murano.db import api as db_api - -CONF = config.CONF -logging.register_options(CONF) -logging.setup(CONF, 'murano') - - -class MuranoTestCase(testtools.TestCase): - - def setUp(self): - super(MuranoTestCase, self).setUp() - self.useFixture(fixtures.FakeLogger('murano')) - - def override_config(self, name, override, group=None): - CONF.set_override(name, override, group) - self.addCleanup(CONF.clear_override, name, group) - - -class MuranoWithDBTestCase(MuranoTestCase): - - def setUp(self): - super(MuranoWithDBTestCase, self).setUp() - self.override_config('connection', "sqlite://", group='database') - db_api.setup_db() - self.addCleanup(db_api.drop_db) - - self.override_config('env_audit_enabled', False, group='stats') - - -class MuranoNotifyWithDBTestCase(MuranoWithDBTestCase): - - def setUp(self): - super(MuranoNotifyWithDBTestCase, self).setUp() - self.override_config('connection', "sqlite://", group='database') - db_api.setup_db() - self.addCleanup(db_api.drop_db) - self.override_config('env_audit_enabled', True, group='stats') diff --git a/murano/tests/unit/cmd/__init__.py b/murano/tests/unit/cmd/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/cmd/test_api_workers.py b/murano/tests/unit/cmd/test_api_workers.py deleted file mode 100644 index f274d2f1b..000000000 --- a/murano/tests/unit/cmd/test_api_workers.py +++ /dev/null @@ -1,70 +0,0 @@ -# Licensed 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 sys -from unittest import mock - -from oslo_concurrency import processutils -from oslo_log import log as logging - -from murano.cmd import api -from murano.common import app_loader -from murano.common import config -from murano.common import policy -from murano.tests.unit import base - - -class TestAPIWorkers(base.MuranoTestCase): - - def setUp(self): - super(TestAPIWorkers, self).setUp() - sys.argv = ['murano'] - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch.object(policy, 'init') - @mock.patch.object(config, 'set_middleware_defaults') - @mock.patch.object(app_loader, 'load_paste_app') - @mock.patch('oslo_service.service.launch') - def test_workers_default(self, launch, setup, parse_args, init, - load_paste_app, set_middleware_defaults): - api.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, - workers=processutils.get_worker_count(), - restart_method='mutate') - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch.object(policy, 'init') - @mock.patch.object(config, 'set_middleware_defaults') - @mock.patch.object(app_loader, 'load_paste_app') - @mock.patch('oslo_service.service.launch') - def test_workers_good_setting(self, launch, setup, parse_args, init, - load_paste_app, set_middleware_defaults): - self.override_config("api_workers", 8, "murano") - api.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, workers=8, - restart_method='mutate') - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch.object(policy, 'init') - @mock.patch.object(config, 'set_middleware_defaults') - @mock.patch.object(app_loader, 'load_paste_app') - @mock.patch('oslo_service.service.launch') - def test_workers_zero_setting(self, launch, setup, parse_args, init, - load_paste_app, set_middleware_defaults): - self.override_config("api_workers", 0, "murano") - api.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, - workers=processutils.get_worker_count(), - restart_method='mutate') diff --git a/murano/tests/unit/cmd/test_engine_workers.py b/murano/tests/unit/cmd/test_engine_workers.py deleted file mode 100644 index 2b14b4361..000000000 --- a/murano/tests/unit/cmd/test_engine_workers.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2016 NEC Corporation. All rights reserved. -# -# Licensed 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 unittest import mock - -from oslo_concurrency import processutils -from oslo_log import log as logging - -from murano.cmd import engine -from murano.common import config -from murano.tests.unit import base - - -class TestEngineWorkers(base.MuranoTestCase): - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch('oslo_service.service.launch') - def test_workers_default(self, launch, setup, parse_args): - engine.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, - workers=processutils.get_worker_count(), - restart_method='mutate') - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch('oslo_service.service.launch') - def test_workers_good_setting(self, launch, setup, parse_args): - self.override_config("engine_workers", 8, "engine") - engine.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, workers=8, - restart_method='mutate') - - @mock.patch.object(config, 'parse_args') - @mock.patch.object(logging, 'setup') - @mock.patch('oslo_service.service.launch') - def test_workers_zero_setting(self, launch, setup, parse_args): - self.override_config("engine_workers", 0, "engine") - engine.main() - launch.assert_called_once_with(mock.ANY, mock.ANY, - workers=processutils.get_worker_count(), - restart_method='mutate') diff --git a/murano/tests/unit/cmd/test_manage.py b/murano/tests/unit/cmd/test_manage.py deleted file mode 100644 index 86fc33eb8..000000000 --- a/murano/tests/unit/cmd/test_manage.py +++ /dev/null @@ -1,231 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from oslo_config import cfg - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - -from murano.cmd import manage -from murano.db.catalog import api as db_catalog_api -from murano.db import models -from murano.db import session as db_session -from murano.tests.unit import base as test_base - -CONF = cfg.CONF - - -class TestManage(test_base.MuranoWithDBTestCase): - - def setUp(self): - super(TestManage, self).setUp() - - session = db_session.get_session() - # Create environment. - self.test_environment = models.Environment( - name=b'test_environment', tenant_id=b'test_tenant_id', - version=1 - ) - # Create categories. - self.test_categories = [ - models.Category(name=b'test_category_1'), - models.Category(name=b'test_category_2') - ] - # Create tags. - self.test_tags = [ - models.Tag(name=b'test_tag_1'), - models.Tag(name=b'test_tag_2') - ] - # Add environment, categories and tags to DB. - with session.begin(): - session.add(self.test_environment) - session.add_all(self.test_categories) - session.add_all(self.test_tags) - # Create package. - self.test_package = models.Package( - fully_qualified_name=b'test_fqn', name=b'test_name', - logo=b'test_logo', supplier_logo=b'test_supplier_logo', - type=b'test_type', description=b'test_desc', is_public=True, - archive=b'test_archive', ui_definition=b'test_ui_definition', - categories=self.test_categories, tags=self.test_tags, - owner_id=self.test_environment.tenant_id,) - # Add the package to the DB. - with session.begin(): - session.add(self.test_package) - # Create class definitions and assign their FKs to test_package.id. - self.test_class_definitions = [ - models.Class(name=b'test_class_definition_1', - package_id=self.test_package.id), - models.Class(name=b'test_class_definition_2', - package_id=self.test_package.id) - ] - # Add the class definitions to the DB and update the FK reference for - # test_package.class_definitions. - with session.begin(): - session.add_all(self.test_class_definitions) - self.test_package.class_definitions = self.test_class_definitions - session.add(self.test_package) - # Create mock object that resembles loaded package from - # load_utils.load_from_dir - self.mock_loaded_package = mock.MagicMock( - full_name=self.test_package.fully_qualified_name, - display_name=self.test_package.name, - package_type=self.test_package.type, - author=self.test_package.author, - supplier=self.test_package.supplier, - description=self.test_package.description, - tags=[tag.name for tag in self.test_package.tags], - classes=[cls.name for cls in self.test_package.class_definitions], - logo=self.test_package.logo, - supplier_logo=self.test_package.supplier_logo, - ui=self.test_package.ui_definition, - blob=self.test_package.archive) - - @mock.patch('murano.cmd.manage.LOG') - @mock.patch('murano.cmd.manage.load_utils') - def test_do_import_package(self, mock_load_utils, mock_log): - manage.CONF = mock.MagicMock() - manage.CONF.command = mock.MagicMock( - directory='test_dir', - categories=[cat.name for cat in self.test_package.categories], - update=True) - mock_load_utils.load_from_dir.return_value = self.mock_loaded_package - - manage.do_import_package() - - # Assert that the function ran to completion. - self.assertIn("Finished import of package", - str(mock_log.info.mock_calls[0])) - - # Check that the package was uploaded to the DB. - filter_params = { - 'name': self.test_package.name, - 'fully_qualified_name': self.test_package.fully_qualified_name, - 'type': self.test_package.type, - 'description': self.test_package.description - } - retrieved_package = None - session = db_session.get_session() - with session.begin(): - retrieved_package = session.query(models.Package)\ - .filter_by(**filter_params).first() - self.assertIsNotNone(retrieved_package) - self.assertNotEqual(self.test_package.id, retrieved_package.id) - - @mock.patch('murano.cmd.manage.LOG') - @mock.patch('murano.cmd.manage.load_utils') - @mock.patch('murano.cmd.manage.db_catalog_api') - def test_do_import_package_without_update(self, mock_db_catalog_api, - mock_load_utils, mock_log): - mock_db_catalog_api.package_search.return_value =\ - [self.test_package] - mock_load_utils.load_from_dir.return_value =\ - mock.MagicMock(full_name='test_full_name') - manage.CONF = mock.MagicMock() - manage.CONF.command = mock.MagicMock( - directory='test_dir', - categories=[], - update=False) - - manage.do_import_package() - - mock_log.error.assert_called_once_with( - "Package '{name}' exists ({pkg_id}). Use --update." - .format(name='test_full_name', pkg_id=self.test_package.id)) - - @mock.patch('sys.stdout', new_callable=StringIO) - def test_do_list_categories(self, mock_stdout): - expected_output = ">> Murano package categories:* "\ - "test_category_1* test_category_2" - - manage.do_list_categories() - self.assertEqual(expected_output, - mock_stdout.getvalue().replace('\n', '') - .replace('b\'', '').replace('\'', '')) - - @mock.patch('murano.cmd.manage.db_catalog_api') - @mock.patch('sys.stdout', new_callable=StringIO) - def test_do_list_categories_with_no_categories(self, mock_stdout, - mock_db_catalog_api): - mock_db_catalog_api.category_get_names.return_value = [] - expected_output = "No categories were found" - - manage.do_list_categories() - - self.assertEqual( - expected_output, mock_stdout.getvalue().replace('\n', '')) - - @mock.patch('sys.stdout', new_callable=StringIO) - def test_do_add_category(self, mock_stdout): - manage.CONF = mock.MagicMock() - manage.CONF.command.category_name = 'test_category_name' - - expected_output = ">> Successfully added category test_category_name" - - manage.do_add_category() - - self.assertEqual(expected_output, - mock_stdout.getvalue().replace('\n', '')) - - @mock.patch('sys.stdout', new_callable=StringIO) - def test_do_add_category_except_duplicate_error(self, mock_stdout): - manage.CONF = mock.MagicMock() - manage.CONF.command.category_name = 'test_category_name' - - expected_output = ">> ERROR: Category \'test_category_name\' already "\ - "exists" - - db_catalog_api.category_add('test_category_name') - manage.do_add_category() - - self.assertEqual(expected_output, - mock_stdout.getvalue().replace('\n', '')) - - def test_add_command_parsers(self): - mock_parser = mock.MagicMock() - mock_subparsers = mock.MagicMock() - mock_subparsers.add_parser.return_value = mock_parser - - manage.add_command_parsers(mock_subparsers) - - mock_subparsers.add_parser.assert_any_call('import-package') - mock_subparsers.add_parser.assert_any_call('category-list') - mock_subparsers.add_parser.assert_any_call('category-add') - - mock_parser.set_defaults.assert_any_call(func=manage.do_import_package) - mock_parser.set_defaults.assert_any_call( - func=manage.do_list_categories) - mock_parser.set_defaults.assert_any_call(func=manage.do_add_category) - self.assertEqual(4, mock_parser.add_argument.call_count) - - @mock.patch('murano.cmd.manage.CONF') - def test_main_except_runtime_error(self, mock_conf): - mock_conf.side_effect = RuntimeError - - with self.assertRaisesRegex(SystemExit, 'ERROR:'): - manage.main() - - @mock.patch('murano.cmd.manage.CONF') - def test_main_except_general_exception(self, mock_conf): - mock_conf.command.func.side_effect = Exception - - expected_err_msg = "murano-manage command failed:" - - with self.assertRaisesRegex(SystemExit, expected_err_msg): - manage.main() diff --git a/murano/tests/unit/cmd/test_status.py b/murano/tests/unit/cmd/test_status.py deleted file mode 100644 index a39928b6c..000000000 --- a/murano/tests/unit/cmd/test_status.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (c) 2018 NEC, Corp. -# -# Licensed 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 oslo_config import cfg -from oslo_upgradecheck.upgradecheck import Code - -from murano.cmd import status -from murano.tests.unit import base - - -class TestUpgradeChecks(base.MuranoTestCase): - - def setUp(self): - super(TestUpgradeChecks, self).setUp() - self.cmd = status.Checks() - - def test_checks(self): - cfg.CONF(args=[], project='murano') - for name, func in self.cmd._upgrade_checks: - if isinstance(func, tuple): - func_name, kwargs = func - result = func_name(self, **kwargs) - else: - result = func(self) - self.assertEqual(Code.SUCCESS, result.code) diff --git a/murano/tests/unit/common/__init__.py b/murano/tests/unit/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/common/helpers/__init__.py b/murano/tests/unit/common/helpers/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/common/helpers/test_token_sanitizer.py b/murano/tests/unit/common/helpers/test_token_sanitizer.py deleted file mode 100644 index ce628a70d..000000000 --- a/murano/tests/unit/common/helpers/test_token_sanitizer.py +++ /dev/null @@ -1,48 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 murano.common.helpers import token_sanitizer -from murano.tests.unit import base - - -class TokenSanitizerTests(base.MuranoTestCase): - sanitizer = token_sanitizer.TokenSanitizer() - - def test_dict_with_one_value(self): - source = {'token': 'value'} - value = self.sanitizer.sanitize(source) - self.assertEqual(self.sanitizer.message, value['token']) - - def test_dict_with_few_value(self): - source = {'token': 'value', 'pass': 'value', 'TrustId': 'value'} - value = self.sanitizer.sanitize(source) - - self.assertEqual(self.sanitizer.message, value['token']) - self.assertEqual(self.sanitizer.message, value['pass']) - self.assertEqual(self.sanitizer.message, value['TrustId']) - - def test_dict_with_nested_dict(self): - source = {'obj': {'pass': 'value'}} - value = self.sanitizer.sanitize(source) - self.assertEqual(self.sanitizer.message, value['obj']['pass']) - - def test_dict_with_nested_list(self): - source = {'obj': [{'pass': 'value'}]} - value = self.sanitizer.sanitize(source) - self.assertEqual(self.sanitizer.message, value['obj'][0]['pass']) - - def test_leave_out_other_values(self): - source = {'obj': ['value']} - value = self.sanitizer.sanitize(source) - self.assertEqual('value', value['obj'][0]) diff --git a/murano/tests/unit/common/messaging/__init__.py b/murano/tests/unit/common/messaging/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/common/messaging/test_mqclient.py b/murano/tests/unit/common/messaging/test_mqclient.py deleted file mode 100644 index ad9e2d30d..000000000 --- a/murano/tests/unit/common/messaging/test_mqclient.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from oslo_config import cfg -from oslo_serialization import jsonutils -import ssl as ssl_module - -from murano.common.i18n import _ -from murano.common.messaging import mqclient -from murano.tests.unit import base - -CONF = cfg.CONF - - -class MQClientTest(base.MuranoTestCase): - - @mock.patch('murano.common.messaging.mqclient.kombu') - def setUp(self, mock_kombu): - super(MQClientTest, self).setUp() - self.ssl_client = mqclient.MqClient(login='test_login', - password='test_password', - host='test_host', port='test_port', - virtual_host='test_virtual_host', - ssl=True, ca_certs=['cert1'], - insecure=False) - - mock_kombu.Connection.assert_called_once_with( - 'amqp://{0}:{1}@{2}:{3}/{4}'.format('test_login', 'test_password', - 'test_host', 'test_port', - 'test_virtual_host'), - ssl={'ca_certs': ['cert1'], 'cert_reqs': ssl_module.CERT_REQUIRED}) - self.assertEqual(mock_kombu.Connection(), self.ssl_client._connection) - self.assertIsNone(self.ssl_client._channel) - self.assertFalse(self.ssl_client._connected) - - @mock.patch('murano.common.messaging.mqclient.kombu', autospec=True) - def test_client_initialization_with_ssl_version(self, mock_kombu): - ssl_versions = ( - ('tlsv1', getattr(ssl_module, 'PROTOCOL_TLSv1', None)), - ('tlsv1_1', getattr(ssl_module, 'PROTOCOL_TLSv1_1', None)), - ('tlsv1_2', getattr(ssl_module, 'PROTOCOL_TLSv1_2', None)), - ('sslv2', getattr(ssl_module, 'PROTOCOL_SSLv2', None)), - ('sslv23', getattr(ssl_module, 'PROTOCOL_SSLv23', None)), - ('sslv3', getattr(ssl_module, 'PROTOCOL_SSLv3', None))) - exception_count = 0 - - for ssl_name, ssl_version in ssl_versions: - ssl_kwargs = { - 'login': 'test_login', - 'password': 'test_password', - 'host': 'test_host', - 'port': 'test_port', - 'virtual_host': 'test_virtual_host', - 'ssl': True, - 'ssl_version': ssl_name, - 'ca_certs': ['cert1'], - 'insecure': False - } - - # If a ssl_version is not valid, a RuntimeError is thrown. - # According to the ssl_version docs in config.py, certain versions - # of TLS may be available depending on the system. So, just - # check that at least 1 ssl_version works. - if ssl_version is None: - e = self.assertRaises(RuntimeError, mqclient.MqClient, - **ssl_kwargs) - self.assertEqual(_('Invalid SSL version: %s') % ssl_name, - e.__str__()) - exception_count += 1 - continue - - self.ssl_client = mqclient.MqClient(**ssl_kwargs) - - mock_kombu.Connection.assert_called_once_with( - 'amqp://{0}:{1}@{2}:{3}/{4}'.format( - 'test_login', 'test_password', 'test_host', 'test_port', - 'test_virtual_host'), - ssl={'ca_certs': ['cert1'], - 'cert_reqs': ssl_module.CERT_REQUIRED, - 'ssl_version': ssl_version}) - self.assertEqual( - mock_kombu.Connection(), self.ssl_client._connection) - self.assertIsNone(self.ssl_client._channel) - self.assertFalse(self.ssl_client._connected) - mock_kombu.Connection.reset_mock() - - # Check that at least one ssl_version worked. - self.assertGreater(len(ssl_versions), exception_count) - - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_alternate_client_initializations(self, mock_kombu): - for ca_cert in ['cert1', None]: - client = mqclient.MqClient(login='test_login', - password='test_password', - host='test_host', port='test_port', - virtual_host='test_virtual_host', - ssl=True, - ca_certs=ca_cert, - insecure=True) - - mock_kombu.Connection.assert_called_once_with( - 'amqp://{0}:{1}@{2}:{3}/{4}'.format('test_login', - 'test_password', - 'test_host', 'test_port', - 'test_virtual_host'), - ssl={'ca_certs': ca_cert, - 'cert_reqs': ssl_module.CERT_OPTIONAL - if ca_cert else ssl_module.CERT_NONE}) - self.assertEqual(mock_kombu.Connection(), - client._connection) - mock_kombu.Connection.reset_mock() - - client = mqclient.MqClient(login='test_login', - password='test_password', - host='test_host', port='test_port', - virtual_host='test_virtual_host', - ssl=False, - ca_certs=None, - insecure=False) - - mock_kombu.Connection.assert_called_once_with( - 'amqp://{0}:{1}@{2}:{3}/{4}'.format('test_login', - 'test_password', - 'test_host', 'test_port', - 'test_virtual_host'), - ssl=None) - self.assertEqual(mock_kombu.Connection(), - client._connection) - - def test_connect(self): - self.ssl_client.connect() - - self.ssl_client._connection.connect.assert_called_once_with() - self.ssl_client._connection.channel.assert_called_once_with() - self.assertEqual(self.ssl_client._connection.channel(), - self.ssl_client._channel) - self.assertTrue(self.ssl_client._connected) - - def test_close(self): - self.ssl_client.close() - - self.ssl_client._connection.close.assert_called_once_with() - self.assertFalse(self.ssl_client._connected) - - def test_enter_and_exit(self): - with self.ssl_client: - pass - - self.ssl_client._connection.connect.assert_called_once_with() - self.ssl_client._connection.close.assert_called_once_with() - - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_declare(self, mock_kombu): - self.ssl_client.connect() - self.ssl_client.declare(queue='test_queue', exchange='test_exchange', - enable_ha=True, ttl=1) - - queue_args = { - 'x-ha-policy': 'all', - 'x-expires': 1 - } - mock_kombu.Exchange.assert_called_once_with( - 'test_exchange', type='direct', durable=True) - mock_kombu.Queue.assert_called_once_with('test_queue', - mock_kombu.Exchange(), - 'test_queue', - durable=True, - queue_arguments=queue_args) - mock_kombu.Queue()().declare.assert_called_once_with() - mock_kombu.reset_mock() - - self.ssl_client.declare(queue='test_queue', exchange='test_exchange', - enable_ha=False, ttl=0) - - mock_kombu.Exchange.assert_called_once_with( - 'test_exchange', type='direct', durable=True) - mock_kombu.Queue.assert_called_once_with('test_queue', - mock_kombu.Exchange(), - 'test_queue', - durable=True, - queue_arguments={}) - - def test_declare_except_runtime_error(self): - with self.assertRaisesRegex(RuntimeError, - 'Not connected to RabbitMQ'): - self.ssl_client.declare(None) - - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_send(self, mock_kombu): - mock_message = mock.MagicMock(body='test_message', id=3) - - self.ssl_client.connect() - self.ssl_client.send(mock_message, 'test_key', 'test_exchange') - - mock_kombu.Producer.assert_called_once_with( - self.ssl_client._connection) - mock_kombu.Producer().publish.assert_called_once_with( - exchange='test_exchange', routing_key='test_key', - body=jsonutils.dumps('test_message'), message_id='3', - headers=None) - - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_send_signed(self, mock_kombu): - mock_message = mock.MagicMock(body='test_message', id=3) - - signer = mock.MagicMock() - signer.return_value = "SIGNATURE" - self.ssl_client.connect() - self.ssl_client.send(mock_message, 'test_key', 'test_exchange', signer) - - mock_kombu.Producer.assert_called_once_with( - self.ssl_client._connection) - mock_kombu.Producer().publish.assert_called_once_with( - exchange='test_exchange', routing_key='test_key', - body=jsonutils.dumps('test_message'), message_id='3', - headers={'signature': 'SIGNATURE'}) - - def test_send_except_runtime_error(self): - with self.assertRaisesRegex(RuntimeError, - 'Not connected to RabbitMQ'): - self.ssl_client.send(None, None) - - @mock.patch('murano.common.messaging.mqclient.subscription') - def test_open(self, mock_subscription): - self.ssl_client.connect() - self.ssl_client.open('test_queue', prefetch_count=2) - mock_subscription.Subscription.assert_called_once_with( - self.ssl_client._connection, 'test_queue', 2) - - def test_open_except_runtime_error(self): - with self.assertRaisesRegex(RuntimeError, - 'Not connected to RabbitMQ'): - self.ssl_client.open(None) diff --git a/murano/tests/unit/common/test_app_loader.py b/murano/tests/unit/common/test_app_loader.py deleted file mode 100644 index 0e0f348c1..000000000 --- a/murano/tests/unit/common/test_app_loader.py +++ /dev/null @@ -1,80 +0,0 @@ -# Licensed 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 os -from unittest import mock - -from oslo_config import cfg -from paste import deploy - -from murano.common import app_loader -from murano.common import config # noqa -from murano.tests.unit import base - -CONF = cfg.CONF - - -class AppLoaderTest(base.MuranoTestCase): - def setUp(self): - super(AppLoaderTest, self).setUp() - self.override_config('flavor', 'myflavor', 'paste_deploy') - self.override_config('config_file', 'path/to/myapp-paste.ini', - 'paste_deploy') - CONF.config_file = ['myapp.conf'] - CONF.prog = 'myapp' - CONF.find_file = mock.MagicMock(return_value='path/to/myapp-paste.ini') - - @mock.patch.object(deploy, 'loadapp', return_value=mock.sentinel.myapp) - def _test_load_paste_app(self, mock_loadapp, - appname='myapp', - fullname='myapp-myflavor', - path='path/to/myapp-paste.ini'): - expected_config_path = 'config:%s/%s' % (os.path.abspath('.'), path,) - - app = app_loader.load_paste_app(appname) - - mock_loadapp.assert_called_with(expected_config_path, name=fullname) - self.assertEqual(mock.sentinel.myapp, app) - - def test_load_paste_app(self): - self._test_load_paste_app() - - def test_load_paste_app_no_name(self): - self._test_load_paste_app(appname=None) - - def test_load_paste_app_no_flavor(self): - self.override_config('flavor', None, 'paste_deploy') - self._test_load_paste_app(fullname='myapp') - - def test_load_paste_app_no_pastedep_cfg_opt(self): - self.override_config('config_file', None, 'paste_deploy') - - self._test_load_paste_app() - - def test_load_paste_app_no_pastedep_cfg_opt_and_cfg_opt(self): - self.override_config('config_file', None, 'paste_deploy') - CONF.config_file = [] - - self._test_load_paste_app() - CONF.find_file.assert_called_with('myapp-paste.ini') - - def test_load_paste_app_no_cfg_at_all(self): - self.override_config('config_file', None, 'paste_deploy') - CONF.find_file.return_value = None - - self.assertRaises(RuntimeError, app_loader.load_paste_app, 'myapp') - - def test_load_paste_app_deploy_error(self): - deploy.loadapp = mock.MagicMock() - deploy.loadapp.side_effect = LookupError('Oops') - - self.assertRaises(RuntimeError, app_loader.load_paste_app, 'myapp') diff --git a/murano/tests/unit/common/test_auth_utils.py b/murano/tests/unit/common/test_auth_utils.py deleted file mode 100644 index 254f4e1f2..000000000 --- a/murano/tests/unit/common/test_auth_utils.py +++ /dev/null @@ -1,338 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 unittest import mock - -from keystoneauth1 import loading as ka_loading - -from murano.common import auth_utils -from murano.tests.unit import base - -from oslo_config import cfg - - -class TestAuthUtils(base.MuranoTestCase): - - def setUp(self): - super(TestAuthUtils, self).setUp() - # Register the Password auth plugin options, - # so we can use CONF.set_override - password_option = ka_loading.get_auth_plugin_conf_options('password') - cfg.CONF.register_opts(password_option, - group=auth_utils.CFG_MURANO_AUTH_GROUP) - self.addCleanup(mock.patch.stopall) - - def _init_mock_cfg(self): - mock_auth_obj = mock.patch.object(auth_utils, 'ka_loading', - spec_set=ka_loading).start() - mock_auth_obj.load_auth_from_conf_options.return_value = \ - mock.sentinel.auth - mock_auth_obj.load_session_from_conf_options.\ - return_value = mock.sentinel.session - cfg.CONF.set_override('auth_type', - 'password', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('www_authenticate_uri', - 'foo_www_authenticate_uri', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('auth_url', - 'foo_auth_url', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('username', - 'fakeuser', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('password', - 'fakepass', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('user_domain_name', - 'Default', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('project_domain_name', - 'Default', - auth_utils.CFG_MURANO_AUTH_GROUP) - cfg.CONF.set_override('project_name', - 'fakeproj', - auth_utils.CFG_MURANO_AUTH_GROUP) - return mock_auth_obj - - def test_get_keystone_auth(self): - mock_identity = self._init_mock_cfg() - - expected_auth = mock.sentinel.auth - actual_auth = auth_utils._get_keystone_auth() - - self.assertEqual(expected_auth, actual_auth) - mock_identity.load_auth_from_conf_options.assert_called_once_with( - cfg.CONF, auth_utils.CFG_MURANO_AUTH_GROUP) - - def test_get_keystone_with_trust_id(self): - mock_ka_loading = self._init_mock_cfg() - - expected_kwargs = { - 'project_name': None, - 'project_domain_name': None, - 'project_id': None, - 'trust_id': mock.sentinel.trust_id - } - expected_auth = mock.sentinel.auth - actual_auth = auth_utils._get_keystone_auth(mock.sentinel.trust_id) - - self.assertEqual(expected_auth, actual_auth) - mock_ka_loading.load_auth_from_conf_options.assert_called_once_with( - cfg.CONF, - auth_utils.CFG_MURANO_AUTH_GROUP, - **expected_kwargs) - - @mock.patch.object(auth_utils, 'ks_client', autospec=True) - @mock.patch.object(auth_utils, '_get_session', autospec=True) - def test_create_keystone_admin_client(self, mock_get_sess, mock_ks_client): - self._init_mock_cfg() - mock_get_sess.return_value = mock.sentinel.session - mock_ks_client.Client.return_value = mock.sentinel.ks_admin_client - - result = auth_utils._create_keystone_admin_client() - - self.assertEqual(result, mock.sentinel.ks_admin_client) - self.assertTrue(mock_get_sess.called) - mock_ks_client.Client.assert_called_once_with( - session=mock.sentinel.session) - - @mock.patch.object(auth_utils, '_get_session', autospec=True) - @mock.patch.object(auth_utils, '_get_keystone_auth', autospec=True) - @mock.patch.object( - auth_utils.helpers, 'get_execution_session', autospec=True) - def test_get_client_session(self, mock_get_execution_session, - mock_get_keystone_auth, mock_get_session): - mock_exec_session = mock.Mock(trust_id=mock.sentinel.trust_id) - mock_get_execution_session.return_value = mock_exec_session - mock_get_keystone_auth.return_value = mock.sentinel.auth - mock_get_session.return_value = mock.sentinel.session - - session = auth_utils.get_client_session(conf=mock.sentinel.conf) - - self.assertEqual(mock.sentinel.session, session) - mock_get_execution_session.assert_called_once_with() - mock_get_keystone_auth.assert_called_once_with(mock.sentinel.trust_id) - mock_get_session.assert_called_once_with( - auth=mock.sentinel.auth, - conf_section=mock.sentinel.conf) - - @mock.patch.object(auth_utils, 'get_token_client_session', autospec=True) - @mock.patch.object( - auth_utils.helpers, 'get_execution_session', autospec=True) - def test_get_client_session_without_trust_id( - self, mock_get_execution_session, mock_get_token_client_session): - mock_get_token_client_session.return_value = mock.sentinel.session - mock_exec_session = mock.Mock(trust_id=None, - token=mock.sentinel.token, - project_id=mock.sentinel.project_id) - - session = auth_utils.get_client_session( - execution_session=mock_exec_session, conf=mock.sentinel.conf) - - self.assertEqual(mock.sentinel.session, session) - self.assertFalse(mock_get_execution_session.called) - mock_get_token_client_session.assert_called_once_with( - token=mock.sentinel.token, project_id=mock.sentinel.project_id) - - @mock.patch.object(auth_utils, '_get_session', autospec=True) - @mock.patch.object(auth_utils, 'identity', autospec=True) - @mock.patch.object( - auth_utils.helpers, 'get_execution_session', autospec=True) - def test_get_token_client_session( - self, mock_get_execution_session, mock_identity, - mock_get_session): - cfg.CONF.set_override('www_authenticate_uri', - 'foo_www_authenticate_uri/v2.0', - auth_utils.CFG_MURANO_AUTH_GROUP) - - mock_get_execution_session.return_value = \ - mock.Mock(token=mock.sentinel.token, - project_id=mock.sentinel.project_id) - mock_identity.Token.return_value = mock.sentinel.auth - mock_get_session.return_value = mock.sentinel.session - - session = auth_utils.get_token_client_session() - self.assertEqual(mock.sentinel.session, session) - - mock_get_execution_session.assert_called_once_with() - mock_identity.Token.assert_called_once_with( - 'foo_www_authenticate_uri/v3', token=mock.sentinel.token, - project_id=mock.sentinel.project_id) - mock_get_session.assert_called_once_with( - auth=mock.sentinel.auth, - conf_section=None) - - @mock.patch.object(auth_utils, 'get_token_client_session', autospec=True) - @mock.patch.object(auth_utils, 'ks_client', autospec=True) - def test_create_keystone_client(self, mock_ks_client, - mock_get_token_client_session): - mock_ks_client.Client.return_value = mock.sentinel.ks_client - mock_session = mock.Mock( - token=mock.sentinel.token, project_id=mock.sentinel.project_id, - conf=mock.sentinel.conf) - mock_get_token_client_session.return_value = mock_session - - ks_client = auth_utils.create_keystone_client( - mock.sentinel.token, mock.sentinel.project_id, mock.sentinel.conf) - - self.assertEqual(mock.sentinel.ks_client, ks_client) - mock_ks_client.Client.assert_called_once_with(session=mock_session) - - @mock.patch.object(auth_utils, 'create_keystone_client', autospec=True) - @mock.patch.object( - auth_utils, '_create_keystone_admin_client', autospec=True) - def test_create_trust(self, mock_create_ks_admin_client, - mock_create_ks_client): - mock_auth_ref = mock.Mock(user_id=mock.sentinel.trustor_user, - project_id=mock.sentinel.project_id, - role_names=mock.sentinel.role_names) - mock_admin_session = mock.Mock(**{ - 'auth.get_user_id.return_value': mock.sentinel.trustee_user - }) - mock_user_session = mock.Mock(**{ - 'auth.get_access.return_value': mock_auth_ref - }) - mock_trust = mock.Mock(id=mock.sentinel.trust_id) - mock_admin_client = mock.Mock(session=mock_admin_session) - mock_user_client = mock.Mock( - session=mock_user_session, - **{'trusts.create.return_value': mock_trust}) - - mock_create_ks_admin_client.return_value = mock_admin_client - mock_create_ks_client.return_value = mock_user_client - - trust_id = auth_utils.create_trust( - trustee_token=mock.sentinel.trustee_token, - trustee_project_id=mock.sentinel.trustee_project_id) - - self.assertEqual(mock.sentinel.trust_id, trust_id) - mock_create_ks_admin_client.assert_called_once_with() - mock_create_ks_client.assert_called_once_with( - token=mock.sentinel.trustee_token, - project_id=mock.sentinel.trustee_project_id) - mock_admin_client.session.auth.get_user_id.assert_called_once_with( - mock_admin_session) - mock_user_client.session.auth.get_access.assert_called_once_with( - mock_user_session) - mock_user_client.trusts.create.assert_called_once_with( - trustor_user=mock.sentinel.trustor_user, - trustee_user=mock.sentinel.trustee_user, - impersonation=True, - role_names=mock.sentinel.role_names, - project=mock.sentinel.project_id) - - @mock.patch.object(auth_utils, 'create_keystone_client', autospec=True) - def test_delete_trust(self, mock_ks_client): - mock_auth_ref = mock.Mock(trust_id=mock.sentinel.trust_id, - token=mock.sentinel.token, - project_id=mock.sentinel.project_id) - mock_user_session = mock.Mock(**{ - 'auth.get_access.return_value': mock_auth_ref - }) - mock_user_client = mock.Mock( - session=mock_user_session) - - mock_ks_client.return_value = mock_user_client - - auth_utils.delete_trust(mock_auth_ref) - - mock_user_client.trusts.delete.assert_called_once_with( - mock_auth_ref.trust_id) - - def test_get_config_option(self): - cfg.CONF.set_override('url', 'foourl', 'murano') - self.assertEqual('foourl', auth_utils._get_config_option( - 'murano', 'url')) - - def test_get_config_option_return_default(self): - self.assertIsNone(auth_utils._get_config_option(None, 'url')) - - def test_get_session(self): - mock_ka_loading = self._init_mock_cfg() - - session = auth_utils._get_session(mock.sentinel.auth) - - self.assertEqual(mock.sentinel.session, session) - mock_ka_loading.load_session_from_conf_options.\ - assert_called_once_with(auth=mock.sentinel.auth, - conf=cfg.CONF, - group=auth_utils.CFG_MURANO_AUTH_GROUP) - - def test_get_session_client_parameters(self): - - cfg.CONF.set_override('url', 'foourl', 'murano') - - expected_result = { - 'session': mock.sentinel.session, - 'endpoint_override': 'foourl' - } - - result = auth_utils.get_session_client_parameters( - conf='murano', - service_type=mock.sentinel.service_type, - service_name=mock.sentinel.service_name, - session=mock.sentinel.session) - - for key, val in expected_result.items(): - self.assertEqual(val, result[key]) - - def test_get_session_client_parameters_without_url(self): - cfg.CONF.set_override('home_region', 'fooregion') - - expected_result = { - 'session': mock.sentinel.session, - 'service_type': mock.sentinel.service_type, - 'service_name': mock.sentinel.service_name, - 'interface': mock.sentinel.endpoint_type, - 'region_name': 'fooregion' - } - - result = auth_utils.get_session_client_parameters( - service_type=mock.sentinel.service_type, - service_name=mock.sentinel.service_name, - interface=mock.sentinel.endpoint_type, - session=mock.sentinel.session) - - for key, val in expected_result.items(): - self.assertEqual(val, result[key]) - - @mock.patch.object( - auth_utils, '_create_keystone_admin_client', autospec=True) - def test_get_user(self, mock_create_ks_admin_client): - mock_client = mock.Mock( - **{'users.get.return_value.to_dict.return_value': - mock.sentinel.user}) - mock_create_ks_admin_client.return_value = mock_client - - user = auth_utils.get_user(mock.sentinel.uid) - - self.assertEqual(mock.sentinel.user, user) - mock_client.users.get.assert_called_once_with(mock.sentinel.uid) - mock_client.users.get.return_value.to_dict.assert_called_once_with() - - @mock.patch.object( - auth_utils, '_create_keystone_admin_client', autospec=True) - def test_get_project(self, mock_create_ks_admin_client): - mock_client = mock.Mock( - **{'projects.get.return_value.to_dict.return_value': - mock.sentinel.project}) - mock_create_ks_admin_client.return_value = mock_client - - project = auth_utils.get_project(mock.sentinel.pid) - - self.assertEqual(mock.sentinel.project, project) - mock_client.projects.get.assert_called_once_with(mock.sentinel.pid) - mock_client.projects.get.return_value.to_dict.assert_called_once_with() diff --git a/murano/tests/unit/common/test_engine.py b/murano/tests/unit/common/test_engine.py deleted file mode 100644 index 3f183c7cb..000000000 --- a/murano/tests/unit/common/test_engine.py +++ /dev/null @@ -1,357 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from oslo_service import service - -from murano.common import engine -from murano.dsl import constants -from murano.dsl import helpers -from murano.dsl import murano_package -from murano.engine import mock_context_manager -from murano.engine import package_loader -from murano.tests.unit import base - - -class TestEngineService(base.MuranoTestCase): - def setUp(self): - super(TestEngineService, self).setUp() - self.engine = engine.EngineService() - self.addCleanup(mock.patch.stopall) - - @mock.patch.object(service.Service, 'reset') - @mock.patch.object(service.Service, 'stop') - @mock.patch.object(service.Service, 'start') - @mock.patch('murano.common.rpc.get_server') - def test_start_stop_reset(self, mock_get_server, mock_start, - mock_stop, mock_reset): - self.engine.start() - self.assertTrue(mock_get_server.called) - self.assertTrue(mock_start.called) - self.engine.stop() - self.assertTrue(mock_stop.called) - self.engine.reset() - self.assertTrue(mock_reset.called) - - @mock.patch.object(service.Service, 'stop') - @mock.patch.object(service.Service, 'start') - @mock.patch('murano.common.rpc.get_server') - def test_stop_graceful(self, mock_get_server, mock_start, mock_stop): - self.engine.start() - self.assertTrue(mock_get_server.called) - self.assertTrue(mock_start.called) - self.engine.stop(graceful=True) - self.assertTrue(mock_stop.called) - - -class TestTaskExecutor(base.MuranoTestCase): - def setUp(self): - super(TestTaskExecutor, self).setUp() - self.task = { - 'action': { - 'args': None, - 'object_id': 'my_obj_id', - 'method': 'my_method' - }, - 'model': { - 'SystemData': { - 'Packages': 'my_packages' - }, - 'project_id': 'my_tenant_id', - 'user_id': 'my_user_id' - }, - 'token': 'my_token', - 'project_id': 'my_tenant_id', - 'user_id': 'my_user_id', - 'id': 'my_env_id' - } - self.task_executor = engine.TaskExecutor(self.task) - self.task_executor._model = self.task['model'] - self.task_executor._session.token = self.task['token'] - self.task_executor._session.project_id = self.task['project_id'] - self.task_executor._session.user_id = self.task['user_id'] - self.task_executor._session.environment_owner_project_id_ = \ - self.task['model']['project_id'] - self.task_executor._session.environment_owner_user_id = \ - self.task['model']['user_id'] - (self.task_executor._session - .system_attributes) = (self.task_executor._model. - get('SystemData', {})) - self.addCleanup(mock.patch.stopall) - - def test_properties(self): - self.assertEqual(self.task['action'], self.task_executor.action) - self.assertEqual(self.task_executor._session, - self.task_executor.session) - self.assertEqual(self.task['model'], self.task_executor.model) - - @mock.patch('murano.common.engine.auth_utils.delete_trust') - @mock.patch('murano.common.engine.auth_utils.create_trust') - @mock.patch('murano.common.engine.package_loader.' - 'CombinedPackageLoader.import_fixation_table') - @mock.patch('murano.common.engine.TaskExecutor._execute') - def test_execute(self, mock_execute, mock_loader, - mock_create, mock_delete): - mock_loader.return_value = {'SystemData': 'my_sys_data'} - mock_create.return_value = 'trust_id' - expected = { - 'action': { - 'result': '?', - 'isException': False - } - } - mock_execute.return_value = expected - result = self.task_executor.execute() - - self.assertEqual(expected, result) - self.assertTrue(mock_execute.called) - self.assertTrue(mock_loader.called) - self.assertTrue(mock_create.called) - self.assertTrue(mock_delete.called) - - def test_private_execute(self): - mock_loader = mock.Mock() - result = self.task_executor._execute(mock_loader) - expected = { - 'action': { - 'result': None, - 'isException': False - } - } - self.assertEqual(expected, result) - - @mock.patch('murano.common.engine.auth_utils.delete_trust') - @mock.patch('murano.common.engine.auth_utils.create_trust') - def test_trust(self, mock_create, mock_delete): - mock_create.return_value = 'trust_id' - self.task_executor._create_trust() - self.assertEqual('trust_id', self.task_executor._session.trust_id) - self.assertTrue(mock_create.called) - - self.task_executor._delete_trust() - self.assertIsNone(self.task_executor._session.trust_id) - self.assertTrue(mock_delete.called) - - -class TestStaticActionExecutor(base.MuranoTestCase): - - def setUp(self): - super(TestStaticActionExecutor, self).setUp() - - self.action = { - 'method': 'TestAction', - 'args': {'name': 'foo'}, - 'class_name': 'TestClass', - 'pkg_name': 'TestPackage', - 'class_version': '=0' - } - self.task = { - 'action': self.action, - 'token': 'test_token', - 'project_id': 'test_tenant', - 'user_id': 'test_user', - 'id': 'test_task_id' - } - self.task_executor = engine.StaticActionExecutor(self.task) - self.assertIsInstance(self.task_executor._reporter, - engine.status_reporter.StatusReporter) - self.assertEqual(self.task['id'], - self.task_executor._reporter._environment_id) - - def test_action_property(self): - self.assertEqual(self.action, self.task_executor.action) - - def test_session_property(self): - self.assertIsInstance(self.task_executor.session, - engine.execution_session.ExecutionSession) - self.assertEqual('test_token', self.task_executor.session.token) - self.assertEqual('test_tenant', self.task_executor.session.project_id) - self.assertIsInstance(self.task_executor._model_policy_enforcer, - engine.enforcer.ModelPolicyEnforcer) - self.assertEqual( - self.task_executor.session, - self.task_executor._model_policy_enforcer._execution_session) - - @mock.patch.object(engine, 'serializer') - @mock.patch.object(engine.dsl_executor.MuranoDslExecutor, 'package_loader') - def test_execute(self, mock_package_loader, mock_serializer): - mock_class = mock.Mock() - mock_package = mock.Mock(spec=murano_package.MuranoPackage) - mock_package.find_class.return_value = mock_class - mock_package_loader.load_package.return_value = mock_package - version_spec = helpers.parse_version_spec(self.action['class_version']) - - self.task_executor.execute() - mock_package_loader.load_package.assert_called_once_with( - 'TestPackage', version_spec) - mock_package.find_class.assert_called_once_with( - self.action['class_name'], search_requirements=False) - mock_class.invoke.assert_called_once_with('TestAction', None, (), - {'name': 'foo'}) - self.assertTrue(mock_serializer.serialize.called) - - @mock.patch.object(engine, 'serializer') - @mock.patch.object(engine.dsl_executor.MuranoDslExecutor, 'package_loader') - def test_execute_without_package_name(self, mock_package_loader, - mock_serializer): - mock_class = mock.Mock() - mock_package = mock.Mock(spec=murano_package.MuranoPackage) - mock_package.find_class.return_value = mock_class - mock_package_loader.load_class_package.return_value = mock_package - version_spec = helpers.parse_version_spec(self.action['class_version']) - self.task_executor.action['pkg_name'] = None - - self.task_executor.execute() - mock_package_loader.load_class_package.assert_called_once_with( - 'TestClass', version_spec) - mock_package.find_class.assert_called_once_with( - self.action['class_name'], search_requirements=False) - mock_class.invoke.assert_called_once_with('TestAction', None, (), - {'name': 'foo'}) - self.assertTrue(mock_serializer.serialize.called) - - -class TestSchemaEndpoint(base.MuranoTestCase): - - def setUp(self): - super(TestSchemaEndpoint, self).setUp() - context_manager = mock_context_manager.MockContextManager() - self.context = context_manager.create_root_context( - constants.RUNTIME_VERSION_1_5) - - @mock.patch('murano.common.engine.schema_generator') - @mock.patch('murano.common.engine.package_loader') - def test_generate_schema(self, mock_package_loader, - mock_schema_generator): - mock_pkg_loader = mock.Mock( - spec=package_loader.CombinedPackageLoader) - mock_package_loader.CombinedPackageLoader().__enter__.return_value =\ - mock_pkg_loader - mock_schema_generator.generate_schema.return_value = 'test_schema' - - arg1, arg2, arg3 = mock.Mock(), mock.Mock(), mock.Mock() - test_args = (arg1, arg2, arg3) - test_kwargs = {'foo': 'bar', 'class_name': 'test_class_name'} - - result = engine.SchemaEndpoint.generate_schema( - self.context, *test_args, **test_kwargs) - - self.assertEqual('test_schema', result) - mock_schema_generator.generate_schema.assert_called_once_with( - mock_pkg_loader, mock.ANY, *test_args, **test_kwargs) - - -class TestTaskProcessingEndpoint(base.MuranoTestCase): - - def setUp(self): - super(TestTaskProcessingEndpoint, self).setUp() - - self.action = { - 'method': 'TestAction', - 'args': {'name': 'foo'}, - 'class_name': 'TestClass', - 'pkg_name': 'TestPackage', - 'class_version': '=0', - 'object_id': 'test_object_id' - } - self.task = { - 'action': self.action, - 'model': { - 'SystemData': {'TrustId': 'test_trust_id'}, - 'project_id': 'test_tenant', - 'user_id': 'test_user' - }, - 'token': 'test_token', - 'project_id': 'test_tenant', - 'user_id': 'test_user', - 'id': 'test_task_id' - } - context_manager = mock_context_manager.MockContextManager() - self.context = context_manager.create_root_context( - constants.RUNTIME_VERSION_1_5) - - @mock.patch.object(engine.TaskExecutor, '_delete_trust') - @mock.patch.object(engine, 'rpc') - @mock.patch.object(engine.dsl_executor.MuranoDslExecutor, 'finalize') - @mock.patch.object(engine, 'LOG') - def test_handle_task(self, mock_log, mock_finalize, mock_rpc, - mock_delete_trust): - mock_finalize.return_value = self.task['model'] - - handle_task = engine.TaskProcessingEndpoint.handle_task - handle_task(self.context, self.task) - - mock_delete_trust.assert_called_once_with() - mock_rpc.api().process_result.assert_called_once_with( - mock.ANY, 'test_task_id') - self.assertEqual(2, mock_log.info.call_count) - self.assertIn('Starting processing task:', - str(mock_log.info.mock_calls[0])) - self.assertIn('Finished processing task:', - str(mock_log.info.mock_calls[1])) - - -class TestStaticActionEndpoint(base.MuranoTestCase): - - def setUp(self): - super(TestStaticActionEndpoint, self).setUp() - - self.action = { - 'method': 'TestAction', - 'args': {'name': 'foo'}, - 'class_name': 'TestClass', - 'pkg_name': 'TestPackage', - 'class_version': '=0', - 'object_id': 'test_object_id' - } - self.task = { - 'action': self.action, - 'model': {'SystemData': {'TrustId': 'test_trust_id'}}, - 'token': 'test_token', - 'project_id': 'test_tenant', - 'user_id': 'test_user', - 'id': 'test_task_id' - } - context_manager = mock_context_manager.MockContextManager() - self.context = context_manager.create_root_context( - constants.RUNTIME_VERSION_1_5) - - @mock.patch('murano.dsl.serializer.serialize') - @mock.patch('murano.common.engine.package_loader') - @mock.patch.object(engine, 'LOG') - def test_call_static_action(self, mock_log, mock_package_loader, - mock_serialize): - mock_pkg_loader = mock.Mock( - spec=package_loader.CombinedPackageLoader) - mock_package_loader.CombinedPackageLoader().__enter__.return_value =\ - mock_pkg_loader - - expected = { - 'Objects': ['foo'], - 'ObjectsCopy': ['bar'], - 'Attributes': ['baz'] - } - mock_serialize.return_value = expected - - call_static_action = engine.StaticActionEndpoint.call_static_action - result = call_static_action(self.context, self.task) - - self.assertEqual(expected, result) - self.assertEqual(2, mock_log.info.call_count) - self.assertIn('Starting execution of static action:', - str(mock_log.info.mock_calls[0])) - self.assertIn('Finished execution of static action:', - str(mock_log.info.mock_calls[1])) diff --git a/murano/tests/unit/common/test_plugin_loader.py b/murano/tests/unit/common/test_plugin_loader.py deleted file mode 100644 index 61f4825bb..000000000 --- a/murano/tests/unit/common/test_plugin_loader.py +++ /dev/null @@ -1,102 +0,0 @@ -# Licensed 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 unittest import mock - -from oslo_config import cfg - -from murano.common.plugins import extensions_loader -from murano.tests.unit import base - -CONF = cfg.CONF - - -class PluginLoaderTest(base.MuranoTestCase): - - @mock.patch('stevedore.extension.Extension') - def test_load_extension(self, ext): - """Test PluginLoader.load_extension. - - Check that stevedore plugin loading creates instance - of PackageDefinition class, new class are added to that package - and name mapping between class and plugin are updated. - """ - ext.entry_point.dist.project_name = 'plugin1' - ext.entry_point.name = 'Test' - - name_map = {} - test_obj = extensions_loader.PluginLoader('test.namespace') - test_obj.load_extension(ext, name_map) - self.assertEqual(1, len(test_obj.packages)) - loaded_pkg = list(test_obj.packages.values())[0] - self.assertIsInstance(loaded_pkg, - extensions_loader.PackageDefinition) - self.assertEqual('Test', - list(loaded_pkg.classes.keys())[0]) - result = {'Test': list(test_obj.packages.keys())} - self.assertEqual(result, - name_map) - - def test_cleanup_duplicates(self): - """Test PluginLoader.cleanup_duplicates. - - Check loading two plugins with same 'Test1' classes - inside initiates removing of all duplicated classes. - """ - name_map = {} - ext1 = mock.MagicMock(name='ext1') - ext1.entry_point.name = 'Test1' - ext2 = mock.MagicMock(name='ext2') - ext2.entry_point.name = 'Test1' - - test_obj = extensions_loader.PluginLoader() - test_obj.load_extension(ext1, name_map) - test_obj.load_extension(ext2, name_map) - - dist1 = ext1.entry_point.dist - dist2 = ext2.entry_point.dist - self.assertEqual(1, len(test_obj.packages[str(dist1)].classes)) - self.assertEqual(1, len(test_obj.packages[str(dist2)].classes)) - test_obj.cleanup_duplicates(name_map) - - self.assertEqual(0, len(test_obj.packages[str(dist1)].classes)) - self.assertEqual(0, len(test_obj.packages[str(dist2)].classes)) - - def test_load_plugin_with_inappropriate_class_name(self): - """Negative test load_extension. - - Check plugin that contains incorrect MuranoPL class name - won't be loaded. - """ - name_map = {} - ext = mock.MagicMock(name='ext') - ext.entry_point.name = 'murano-pl-class' - - test_obj = extensions_loader.PluginLoader() - test_obj.load_extension(ext, name_map) - # No packages are loaded - self.assertEqual(0, len(test_obj.packages)) - - @mock.patch('stevedore.extension.Extension') - def test_is_plugin_enabled(self, ext): - """Test is_plugin_enabled. - - Check that only plugins specified in config file can be loaded. - """ - self.override_config('enabled_plugins', - 'plugin1, plugin2', - group='murano') - ext.entry_point.dist.project_name = 'test' - test_method = extensions_loader.PluginLoader.is_plugin_enabled - self.assertFalse(test_method(ext)) - ext.entry_point.dist.project_name = 'plugin1' - self.assertTrue(test_method(ext)) diff --git a/murano/tests/unit/common/test_server.py b/murano/tests/unit/common/test_server.py deleted file mode 100644 index b56f4cc6d..000000000 --- a/murano/tests/unit/common/test_server.py +++ /dev/null @@ -1,304 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 datetime import datetime -from unittest import mock - -from murano.common import server -from murano.services import states -from murano.tests.unit import base -from murano.tests.unit import utils as test_utils - - -class ServerTest(base.MuranoTestCase): - - @classmethod - def setUpClass(cls): - super(ServerTest, cls).setUpClass() - cls.result_endpoint = server.ResultEndpoint() - cls.dummy_context = test_utils.dummy_context() - - @mock.patch('murano.common.server.status_reporter.get_notifier') - @mock.patch('murano.common.server.LOG') - @mock.patch('murano.common.server.get_last_deployment') - @mock.patch('murano.common.server.models') - @mock.patch('murano.common.server.session') - def test_process_result(self, mock_db_session, mock_models, - mock_last_deployment, mock_log, mock_notifier): - test_result = { - 'model': { - 'Objects': { - 'applications': ['app1', 'app2'], - 'services': ['service1', 'service2'] - } - }, - 'action': { - 'isException': False - } - } - mock_env = mock.MagicMock(id='test_env_id', - tenant_id='test_tenant_id', - description=None, - version=1) - mock_db_session.get_session().query().get.return_value = mock_env - mock_db_session.get_session().query().filter_by().count.\ - return_value = 0 - - self.result_endpoint.process_result(self.dummy_context, test_result, - 'test_env_id') - - self.assertEqual(mock_env.description, test_result['model']) - self.assertEqual(2, mock_env.version) - self.assertEqual(test_result['action'], - mock_last_deployment().result) - self.assertEqual('Deployment finished', - mock_models.Status().text) - self.assertEqual('info', mock_models.Status().level) - mock_last_deployment().statuses.append.assert_called_once_with( - mock_models.Status()) - mock_db_session.get_session().query().filter_by.assert_any_call( - **{'environment_id': mock_env.id, - 'state': states.SessionState.DEPLOYING}) - self.assertEqual( - states.SessionState.DEPLOYED, - mock_db_session.get_session().query().filter_by().first().state) - mock_log.info.assert_called_once_with( - 'EnvId: {env_id} TenantId: {tenant_id} Status: ' - 'Successful Apps: {services}' - .format(env_id=mock_env.id, - tenant_id=mock_env.tenant_id, - services=test_result['model']['Objects']['services'])) - mock_notifier.return_value.report.assert_called_once_with( - 'environment.deploy.end', - mock_db_session.get_session().query().get(mock_env.id).to_dict()) - - @mock.patch('murano.common.server.LOG') - @mock.patch('murano.common.server.get_last_deployment') - @mock.patch('murano.common.server.models') - @mock.patch('murano.common.server.session') - def test_process_result_with_errors(self, mock_db_session, mock_models, - mock_last_deployment, mock_log): - test_result = { - 'model': { - 'Objects': { - 'applications': ['app1', 'app2'], - 'services': ['service1', 'service2'] - } - }, - 'action': { - 'isException': True - } - } - mock_env = mock.MagicMock(id='test_env_id', - tenant_id='test_tenant_id', - description=None, - version=1) - mock_db_session.get_session().query().get.return_value = mock_env - mock_db_session.get_session().query().filter_by().count.\ - return_value = 1 - - self.result_endpoint.process_result(self.dummy_context, test_result, - 'test_env_id') - - self.assertEqual(mock_env.description, test_result['model']) - self.assertEqual(test_result['action'], - mock_last_deployment().result) - self.assertEqual('Deployment finished with errors', - mock_models.Status().text) - mock_last_deployment().statuses.append.assert_called_once_with( - mock_models.Status()) - mock_db_session.get_session().query().filter_by.assert_any_call( - **{'environment_id': mock_env.id, - 'state': states.SessionState.DEPLOYING}) - self.assertEqual( - states.SessionState.DEPLOY_FAILURE, - mock_db_session.get_session().query().filter_by().first().state) - mock_log.warning.assert_called_once_with( - 'EnvId: {env_id} TenantId: {tenant_id} Status: ' - 'Failed Apps: {services}' - .format(env_id=mock_env.id, - tenant_id=mock_env.tenant_id, - services=test_result['model']['Objects']['services'])) - - @mock.patch('murano.common.server.LOG') - @mock.patch('murano.common.server.get_last_deployment') - @mock.patch('murano.common.server.models') - @mock.patch('murano.common.server.session') - def test_process_result_with_warnings(self, mock_db_session, mock_models, - mock_last_deployment, mock_log): - test_result = { - 'model': { - 'Objects': None, - 'ObjectsCopy': ['object1', 'object2'] - }, - 'action': { - 'isException': True - } - } - mock_env = mock.MagicMock(id='test_env_id', - tenant_id='test_tenant_id', - description=None, - version=1) - mock_db_session.get_session().query().get.return_value = mock_env - # num_errors will be initialized to 0, num_warnings to 1 - mock_db_session.get_session().query().filter_by().count.\ - side_effect = [0, 1] - - self.result_endpoint.process_result(self.dummy_context, test_result, - 'test_env_id') - - self.assertEqual(mock_env.description, test_result['model']) - self.assertEqual(test_result['action'], - mock_last_deployment().result) - self.assertEqual('Deletion finished with warnings', - mock_models.Status().text) - mock_last_deployment().statuses.append.assert_called_once_with( - mock_models.Status()) - mock_db_session.get_session().query().filter_by.assert_any_call( - **{'environment_id': mock_env.id, - 'state': states.SessionState.DELETING}) - self.assertEqual( - states.SessionState.DELETE_FAILURE, - mock_db_session.get_session().query().filter_by().first().state) - mock_log.warning.assert_called_once_with( - 'EnvId: {env_id} TenantId: {tenant_id} Status: ' - 'Failed Apps: {services}' - .format(env_id=mock_env.id, - tenant_id=mock_env.tenant_id, - services=[])) - - @mock.patch('murano.common.server.LOG') - @mock.patch('murano.common.server.session') - def test_process_result_with_no_environment(self, mock_db_session, - mock_log): - test_result = {'model': None} - mock_db_session.get_session().query().get.return_value = None - - result = self.result_endpoint.process_result(self.dummy_context, - test_result, - 'test_env_id') - self.assertIsNone(result) - mock_log.warning.assert_called_once_with( - 'Environment result could not be handled, ' - 'specified environment not found in database') - - @mock.patch('murano.common.server.environments') - @mock.patch('murano.common.server.session') - def test_process_result_with_no_objects(self, mock_db_session, - mock_environments): - test_result = {'model': {'Objects': None, 'ObjectsCopy': None}} - - result = self.result_endpoint.process_result(self.dummy_context, - test_result, - 'test_env_id') - self.assertIsNone(result) - mock_environments.EnvironmentServices.remove.assert_called_once_with( - 'test_env_id') - - @mock.patch('murano.common.server.instances') - def test_track_instance(self, mock_instances): - test_payload = { - 'instance': 'test_instance', - 'instance_type': 'test_instance_type', - 'environment': 'test_environment', - 'unit_count': 'test_unit_count', - 'type_name': 'test_type_name', - 'type_title': 'test_type_title' - } - server.track_instance(test_payload) - mock_instances.InstanceStatsServices.track_instance.\ - assert_called_once_with(test_payload['instance'], - test_payload['environment'], - test_payload['instance_type'], - test_payload['type_name'], - test_payload['type_title'], - test_payload['unit_count']) - - @mock.patch('murano.common.server.instances') - def test_untrack_instance(self, mock_instances): - test_payload = { - 'instance': 'test_instance', - 'environment': 'test_environment' - } - server.untrack_instance(test_payload) - mock_instances.InstanceStatsServices.destroy_instance.\ - assert_called_once_with(test_payload['instance'], - test_payload['environment']) - - @mock.patch('murano.common.server.get_last_deployment') - @mock.patch('murano.common.server.session') - @mock.patch('murano.common.server.models') - def test_report_notification(self, mock_models, mock_db_session, - mock_last_deployment): - mock_last_deployment.return_value = mock.MagicMock( - id='test_deployment_id') - - test_report = { - 'id': 'test_report_id', - 'timestamp': datetime.now().isoformat(), - 'created': None - } - - server.report_notification(test_report) - self.assertIsNotNone(test_report['created']) - mock_models.Status().update.assert_called_once_with(test_report) - self.assertEqual('test_deployment_id', mock_models.Status().task_id) - mock_db_session.get_session().add.assert_called_once_with( - mock_models.Status()) - - def test_get_last_deployment(self): - mock_unit = mock.MagicMock() - result = server.get_last_deployment(mock_unit, 'test_env_id') - self.assertEqual(mock_unit.query().filter_by().order_by().first(), - result) - mock_unit.query().filter_by.assert_any_call( - environment_id='test_env_id') - - def test_service_class(self): - service = server.Service() - self.assertIsNone(service.server) - - # Test stop server. - service.server = mock.MagicMock() - service.stop(graceful=True) - service.server.stop.assert_called_once_with() - service.server.wait.assert_called_once_with() - - # Test reset server. - service.reset() - service.server.reset.assert_called_once_with() - - @mock.patch('murano.common.rpc.messaging') - def test_notification_service_class(self, mock_messaging): - mock_server = mock.MagicMock() - mock_messaging.get_notification_listener.return_value = mock_server - notification_service = server.NotificationService() - self.assertIsNone(notification_service.server) - - notification_service.start() - self.assertEqual(1, - mock_messaging.get_notification_listener.call_count) - mock_server.start.assert_called_once_with() - - @mock.patch('murano.common.rpc.messaging') - def test_api_service_class(self, mock_messaging): - mock_server = mock.MagicMock() - mock_messaging.get_rpc_server.return_value = mock_server - api_service = server.ApiService() - - api_service.start() - self.assertEqual(1, - mock_messaging.get_rpc_server.call_count) - mock_server.start.assert_called_once_with() diff --git a/murano/tests/unit/common/test_statservice.py b/murano/tests/unit/common/test_statservice.py deleted file mode 100644 index 6df9e495c..000000000 --- a/murano/tests/unit/common/test_statservice.py +++ /dev/null @@ -1,254 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 datetime as dt -import time -from unittest import mock - -from oslo_utils import timeutils - -from murano.common import statservice -from murano.db import models -from murano.db import session as db_session -from murano.services import states -from murano.tests.unit import base - - -@mock.patch('murano.common.statservice.v1.stats', - request_count=11, error_count=22, average_time=33, - requests_per_tenant=44) -class StatsCollectingServiceTest(base.MuranoTestCase): - - def setUp(self): - super(StatsCollectingServiceTest, self).setUp() - self.service = statservice.StatsCollectingService() - self.service._prev_time = 0 - self.mock_new_stats = mock.MagicMock(request_count=1, error_count=2) - self.service._stats_db = mock.MagicMock( - get_stats_by_host=mock.MagicMock(return_value=self.mock_new_stats)) - statservice.LOG = mock.MagicMock() - - def test_service_start_and_stop(self, _): - self.assertEqual(0, len(self.service.tg.threads)) - self.service.start() - self.assertEqual(2, len(self.service.tg.threads)) - self.service.stop() - self.assertEqual(0, len(self.service.tg.threads)) - - @mock.patch('murano.common.statservice.time') - def test_update_stats(self, mock_time, mock_stats): - now = time.time() - mock_time.time.return_value = now - - self.service.update_stats() - - statservice.LOG.debug.assert_any_call( - 'Stats: (Requests: 11 Errors: 22 Ave.Res.Time 33.0000\n Per ' - 'tenant: 44)') - self.assertEqual(now, self.service._prev_time) - self.assertEqual(mock_stats.request_count, - self.mock_new_stats.request_count) - self.assertEqual(mock_stats.error_count, - self.mock_new_stats.error_count) - self.assertEqual(mock_stats.average_time, - self.mock_new_stats.average_response_time) - self.assertEqual(str(mock_stats.requests_per_tenant), - self.mock_new_stats.requests_per_tenant) - self.assertEqual((11 - 1) / now, - self.mock_new_stats.requests_per_second) - self.assertEqual((22 - 2) / now, - self.mock_new_stats.errors_per_second) - self.service._stats_db.update.assert_called_once_with( - self.service._hostname, self.mock_new_stats) - - @mock.patch('murano.common.statservice.multiprocessing.cpu_count', - return_value=5) - @mock.patch('murano.common.statservice.psutil.cpu_percent', - return_value=12.3) - def test_update_stats_with_create_stats_db(self, _, __, mock_stats): - self.service._stats_db.get_stats_by_host.return_value = None - - result = self.service.update_stats() - - self.assertIsNone(result) - self.service._stats_db.create.assert_called_once_with( - self.service._hostname, mock_stats.request_count, - mock_stats.error_count, mock_stats.average_time, - mock_stats.requests_per_tenant, 5, 12.3 - ) - - @mock.patch('murano.common.statservice.LOG') - def test_update_stats_handle_exception(self, mock_log, _): - self.service._stats_db.update.side_effect =\ - Exception('test_error_code') - - self.service.update_stats() - - mock_log.exception.assert_called_once_with( - "Failed to get statistics object from a " - "database. {error_code}".format(error_code='test_error_code')) - - -class EnvReportingTest(base.MuranoNotifyWithDBTestCase): - - def setUp(self): - super(EnvReportingTest, self).setUp() - self.service = statservice.StatsCollectingService() - - @mock.patch('murano.common.statservice.status_reporter.' - 'Notification.report') - def test_report_env_stats(self, mock_notifier): - now = timeutils.utcnow() - later = now + dt.timedelta(minutes=1) - - session = db_session.get_session() - - environment1 = models.Environment( - name='test_environment1', tenant_id='test_tenant_id1', - version=2, id='test_env_id_1', - created=now, - updated=later, - description={ - 'Objects': { - 'applications': ['app1'], - 'services': ['service1'] - } - } - ) - environment2 = models.Environment( - name='test_environment2', tenant_id='test_tenant_id2', - version=1, id='test_env_id_2', - created=now, - updated=later, - description={ - 'Objects': { - 'applications': ['app2'], - 'services': ['service3'] - } - } - ) - environment3 = models.Environment( - name='test_environment3', tenant_id='test_tenant_id2', - version=1, id='test_env_id_3', - created=now, - updated=later, - description={} - ) - - session_1 = models.Session( - environment=environment1, user_id='test_user_id', - description={}, - state=states.SessionState.DEPLOYED, - version=1 - ) - - session_2 = models.Session( - environment=environment2, user_id='test_user_id', - description={}, - state=states.SessionState.DEPLOYED, - version=0 - ) - - session_3 = models.Session( - environment=environment3, user_id='test_user_id', - description={}, - state=states.SessionState.DEPLOY_FAILURE, - version=1 - ) - - task_1 = models.Task( - id='task_id_1', - environment=environment1, - description={}, - created=now, - started=now, - updated=later, - finished=later - ) - - task_2 = models.Task( - id='task_id_2', - environment=environment2, - description={}, - created=now, - started=now, - updated=later, - finished=later - ) - - task_3 = models.Task( - id='task_id_3', - environment=environment3, - description={}, - created=now, - started=now, - updated=later, - finished=later - ) - - status_1 = models.Status( - id='status_id_1', - task_id='task_id_1', - text='Deployed', - level='info' - ) - - status_2 = models.Status( - id='status_id_2', - task_id='task_id_2', - text='Deployed', - level='info' - ) - - status_3 = models.Status( - id='status_id_3', - task_id='task_id_3', - text='Something was wrong', - level='error' - ) - - session.add_all([environment1, environment2, environment3]) - session.add_all([session_1, session_2, session_3]) - session.add_all([task_1, task_2, task_3]) - session.add_all([status_1, status_2, status_3]) - - session.flush() - - self.service.report_env_stats() - - self.assertEqual(mock_notifier.call_count, 2) - - dict_env_1 = {'version': 2, - 'updated': later, - 'tenant_id': u'test_tenant_id1', - 'created': now, - 'description_text': u'', - 'status': 'ready', - 'id': u'test_env_id_1', - 'name': u'test_environment1'} - - dict_env_2 = {'version': 1, - 'updated': later, - 'tenant_id': u'test_tenant_id2', - 'created': now, - 'description_text': u'', - 'status': 'ready', - 'id': u'test_env_id_2', - 'name': u'test_environment2'} - - calls = [mock.call('environment.exists', dict_env_1), - mock.call('environment.exists', dict_env_2)] - - mock_notifier.assert_has_calls(calls) diff --git a/murano/tests/unit/common/test_traverse_helper.py b/murano/tests/unit/common/test_traverse_helper.py deleted file mode 100644 index 9b22197f3..000000000 --- a/murano/tests/unit/common/test_traverse_helper.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 murano.common import utils -from murano.tests.unit import base - - -class TraverseHelperTests(base.MuranoTestCase): - def test_root_get_with_dict(self): - source = {'attr': True} - value = utils.TraverseHelper.get('/', source) - self.assertEqual(value, source) - - def test_root_get_with_list(self): - source = [{'attr': True}] - value = utils.TraverseHelper.get('/', source) - self.assertListEqual(value, source) - - def test_root_get_with_value_type(self): - source = 'source' - value = utils.TraverseHelper.get('/', source) - self.assertEqual(source, value) - - def test_attribute_get(self): - source = {'attr': True} - value = utils.TraverseHelper.get('/attr', source) - self.assertTrue(value) - - def test_nested_attribute_get(self): - source = {'obj': {'attr': True}} - value = utils.TraverseHelper.get('/obj/attr', source) - self.assertTrue(value) - - def test_list_item_attribute_get(self): - source = {'obj': [ - {'?': {'id': '1'}, 'value': 1}, - {'?': {'id': '2s'}, 'value': 2}, - ]} - value = utils.TraverseHelper.get('/obj/2s/value', source) - self.assertEqual(2, value) - - def test_list_item_attribute_get_by_index(self): - source = {'obj': [ - {'?': {'id': 'guid1'}, 'value': 1}, - {'?': {'id': 'guid2'}, 'value': 2} - ]} - value = utils.TraverseHelper.get('/obj/1/value', source) - self.assertEqual(2, value) - - def test_attribute_set(self): - source = {'attr': True} - utils.TraverseHelper.update('/newAttr', False, source) - value = utils.TraverseHelper.get('/newAttr', source) - self.assertFalse(value) - - def test_attribute_update(self): - source = {'attr': True} - utils.TraverseHelper.update('/attr', False, source) - value = utils.TraverseHelper.get('/attr', source) - self.assertFalse(value) - - def test_nested_attribute_update(self): - source = {'obj': {'attr': True}} - utils.TraverseHelper.update('/obj/attr', False, source) - value = utils.TraverseHelper.get('/obj/attr', source) - self.assertFalse(value) - - def test_adding_item_to_list(self): - source = {'attr': [1, 2, 3]} - utils.TraverseHelper.insert('/attr', 4, source) - value = utils.TraverseHelper.get('/attr', source) - self.assertListEqual(value, [1, 2, 3, 4]) - - def test_nested_adding_item_to_list(self): - source = {'obj': {'attr': [1, 2, 3]}} - utils.TraverseHelper.insert('/obj/attr', 4, source) - value = utils.TraverseHelper.get('/obj/attr', source) - self.assertListEqual(value, [1, 2, 3, 4]) - - def test_extending_list_with_list(self): - source = {'attr': [1, 2, 3]} - utils.TraverseHelper.extend('/attr', [4, 5], source) - value = utils.TraverseHelper.get('/attr', source) - self.assertListEqual(value, [1, 2, 3, 4, 5]) - - def test_nested_extending_list_with_list(self): - source = {'obj': {'attr': [1, 2, 3]}} - utils.TraverseHelper.extend('/obj/attr', [4, 5], source) - value = utils.TraverseHelper.get('/obj/attr', source) - self.assertListEqual(value, [1, 2, 3, 4, 5]) - - def test_attribute_remove_from_dict(self): - source = {'attr1': False, 'attr2': True} - utils.TraverseHelper.remove('/attr1', source) - value = utils.TraverseHelper.get('/', source) - self.assertEqual(value, {'attr2': True}) - - def test_nested_attribute_remove_from_dict(self): - source = {'obj': {'attr1': False, 'attr2': True}} - utils.TraverseHelper.remove('/obj/attr1', source) - value = utils.TraverseHelper.get('/obj', source) - self.assertEqual(value, {'attr2': True}) - - def test_nested_attribute_remove_from_list_by_id(self): - source = {'obj': [{'?': {'id': 'id1'}}, {'?': {'id': 'id2'}}]} - utils.TraverseHelper.remove('/obj/id1', source) - value = utils.TraverseHelper.get('/obj', source) - self.assertListEqual(value, [{'?': {'id': 'id2'}}]) - - def test_nested_attribute_remove_from_list_by_index(self): - source = {'obj': [{'?': {'id': 'id1'}}, {'?': {'id': 'id2'}}]} - utils.TraverseHelper.remove('/obj/0', source) - value = utils.TraverseHelper.get('/obj', source) - self.assertListEqual(value, [{'?': {'id': 'id2'}}]) diff --git a/murano/tests/unit/common/test_utils.py b/murano/tests/unit/common/test_utils.py deleted file mode 100644 index b83ff82e8..000000000 --- a/murano/tests/unit/common/test_utils.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 datetime -import json - -from murano.common import utils - -from murano.tests.unit import base - - -class UtilsTests(base.MuranoTestCase): - - def test_validate_quotes(self): - self.assertTrue(utils.validate_quotes('"ab"')) - - def test_validate_quotes_not_closed_quotes(self): - self.assertRaises(ValueError, utils.validate_quotes, '"ab","b""') - - def test_validate_quotes_not_opened_quotes(self): - self.assertRaises(ValueError, utils.validate_quotes, '""ab","b"') - - def test_validate_quotes_no_coma_before_opening_quotes(self): - self.assertRaises(ValueError, utils.validate_quotes, '"ab""b"') - - def test_split_for_quotes(self): - self.assertEqual(["a,b", "ac"], utils.split_for_quotes('"a,b","ac"')) - - def test_split_for_quotes_with_backslash(self): - self.assertEqual(['a"bc', 'de', 'fg,h', r'klm\\', '"nop'], - utils.split_for_quotes(r'"a\"bc","de",' - r'"fg,h","klm\\","\"nop"')) - - def test_validate_body(self): - json_schema = json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) - self.assertIsNotNone(utils.validate_body(json_schema)) - - json_schema = json.dumps(['body', {'body': ('baz', None, 1.0, 2)}]) - self.assertIsNotNone(utils.validate_body(json_schema)) - - def test_build_entity_map(self): - entity = {"?": {"fun": "id"}} - self.assertEqual({}, utils.build_entity_map(entity)) - - entity = {"?": {"id": "id"}} - self.assertEqual({'id': {'?': {'id': 'id'}}}, - utils.build_entity_map(entity)) - - entity = [{"?": {"id": "id1"}}, {"?": {"id": "id2"}}] - self.assertEqual({'id1': {'?': {'id': 'id1'}}, - 'id2': {'?': {'id': 'id2'}}}, - utils.build_entity_map(entity)) - - def test_is_different(self): - t1 = "Hello" - t2 = "World" - self.assertTrue(utils.is_different(t1, t2)) - - t1 = "Hello" - t2 = "Hello" - self.assertFalse(utils.is_different(t1, t2)) - - t1 = {1, 2, 3, 4} - t2 = t1 - self.assertFalse(utils.is_different(t1, t2)) - - t2 = {1, 2, 3} - self.assertTrue(utils.is_different(t1, t2)) - - t1 = [1, 2, {1, 2, 3, 4}] - t1[0] = t1 - self.assertTrue(utils.is_different(t1, t2)) - - t1 = [t2] - t2 = [t1] - self.assertTrue(utils.is_different(t1, t2)) - - t1 = [{1, 2, 3}, {1, 2, 3}] - t2 = [{1, 2, 3}, {1, 2}] - self.assertTrue(utils.is_different(t1, t2)) - - t1 = datetime.date(2016, 8, 8) - t2 = datetime.date(2016, 8, 7) - self.assertTrue(utils.is_different(t1, t2)) - - t1 = {1: 1, 2: 2, 3: 3} - t2 = {1: 1, 2: 4, 3: 3} - self.assertTrue(utils.is_different(t1, t2)) - - t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2, 3]}} - t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": "world\n\n\nEnd"}} - self.assertTrue(utils.is_different(t1, t2)) - - t1 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 2, 5]}} - t2 = {1: 1, 2: 2, 3: 3, 4: {"a": "hello", "b": [1, 3, 2, 5]}} - self.assertTrue(utils.is_different(t1, t2)) - - class ClassA(object): - __slots__ = ['x', 'y'] - - def __init__(self, x, y): - self.x = x - self.y = y - - t1 = ClassA(1, 1) - t2 = ClassA(1, 2) - self.assertTrue(utils.is_different(t1, t2)) - - t1 = [1, 2, 3] - t1.append(t1) - - t2 = [1, 2, 4] - t2.append(t2) - self.assertTrue(utils.is_different(t1, t2)) - - t1 = [1, 2, 3] - t2 = [1, 2, 4] - t2.append(t1) - t1.append(t2) - self.assertTrue(utils.is_different(t1, t2)) - - t1 = utils - t2 = datetime - self.assertTrue(utils.is_different(t1, t2)) - - t2 = "Not a module" - self.assertTrue(utils.is_different(t1, t2)) diff --git a/murano/tests/unit/common/test_wsgi.py b/murano/tests/unit/common/test_wsgi.py deleted file mode 100644 index 7dddb1930..000000000 --- a/murano/tests/unit/common/test_wsgi.py +++ /dev/null @@ -1,609 +0,0 @@ -# Copyright (c) 2016 AT&T inc. -# All Rights Reserved. -# -# Licensed 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 errno -import eventlet -import socket -from unittest import mock - -import webob - -from oslo_config import cfg -from xml.dom import minidom - -from murano.common import exceptions -from murano.common.i18n import _ -from murano.common import wsgi -from murano.tests.unit import base - -CONF = cfg.CONF - - -class TestServiceBrokerResponseSerializer(base.MuranoTestCase): - - def test_service_broker_response_serializer(self): - self.sbrs = wsgi.ServiceBrokerResponseSerializer() - result = self.sbrs.serialize("test_response", "application/json") - self.assertEqual(200, result.status_code) - - response = webob.Response("test_body") - response.data = "test_data" - result = self.sbrs.serialize(response, "application/json") - self.assertEqual(200, result.status_code) - - -class TestResponseSerializer(base.MuranoTestCase): - - def test_response_serializer(self): - self.response_serializer = wsgi.ResponseSerializer(None) - self.assertRaises(exceptions.UnsupportedContentType, - self.response_serializer.get_body_serializer, - "not_valid") - - -class TestRequestDeserializer(base.MuranoTestCase): - - def test_request_deserializer_deserialize_body(self): - self.request_deserializer = wsgi.RequestDeserializer() - request = mock.Mock() - request.get_content_type.side_effect = ( - exceptions.UnsupportedContentType - ) - request.body = "body" - self.assertRaises(exceptions.UnsupportedContentType, - self.request_deserializer.deserialize_body, - request, "act") - - request.get_content_type.side_effect = None - request.get_content_type.return_value = None - result = self.request_deserializer.deserialize_body(request, "act") - self.assertEqual({}, result) - - request.get_content_type.return_value = "" - self.assertRaises(exceptions.UnsupportedContentType, - self.request_deserializer.deserialize_body, - request, "act") - - def test_request_deserializer_get_action_args(self): - self.request_deserializer = wsgi.RequestDeserializer() - request_environment = [] - result = self.request_deserializer.get_action_args(request_environment) - self.assertEqual({}, result) - - request_environment = {'wsgiorg.routing_args': ["", {}]} - result = self.request_deserializer.get_action_args(request_environment) - self.assertEqual({}, result) - - -class TestService(base.MuranoTestCase): - - def setUp(self): - super(TestService, self).setUp() - # Greatly speed up the time it takes to run the tests that call - # wsgi._get_socket below: retry_until will be set to 31, the 1st - # iteration of the while loop will execute because it will evaluate to - # 30 < 31, but 2nd one will not because it will evaluate to 31 < 31. - self.mock_time = mock.patch.object( - wsgi, 'time', **{'time.side_effect': [1, 30, 31]}).start() - self.addCleanup(mock.patch.stopall) - - @mock.patch("murano.common.wsgi.socket") - def test_service_get_socket(self, socket): - self.service = wsgi.Service(None, 1) - new_socket = self.service._get_socket(None, None, None) - self.assertIsInstance(new_socket, eventlet.greenio.base.GreenSocket) - - self.mock_time.time.side_effect = [1, 30, 31] - socket.TCP_KEEPIDLE = True - new_socket_2 = self.service._get_socket(None, None, None) - self.assertIsInstance(new_socket_2, eventlet.greenio.base.GreenSocket) - - @mock.patch("murano.common.wsgi.socket") - @mock.patch("murano.common.wsgi.sslutils") - def test_service_get_socket_sslutils_enabled(self, sslutils, mock_socket): - self.service = wsgi.Service(None, 1) - new_socket = self.service._get_socket(None, None, None) - self.assertIsNotNone(new_socket) - - @mock.patch("murano.common.wsgi.socket") - @mock.patch("murano.common.wsgi.eventlet") - def test_service_get_socket_no_bind(self, eventlet, mock_socket): - self.service = wsgi.Service(None, 1) - eventlet.listen.return_value = None - self.assertRaises(RuntimeError, self.service._get_socket, - None, None, None) - - @mock.patch("murano.common.wsgi.socket") - @mock.patch("murano.common.wsgi.eventlet") - def test_service_get_socket_os_error(self, eventlet, mock_socket): - mock_socket.error = socket.error - self.service = wsgi.Service(None, 1) - sock_err = socket.error(1) - eventlet.listen.side_effect = sock_err - self.assertRaises(socket.error, self.service._get_socket, - None, None, None) - - @mock.patch("murano.common.wsgi.socket") - @mock.patch("murano.common.wsgi.eventlet") - def test_service_get_socket_socket_error_EADDRINUSE(self, eventlet, - mock_socket): - mock_socket.error = socket.error - self.service = wsgi.Service(None, 1) - sock_err = socket.error(errno.EADDRINUSE) - eventlet.listen.side_effect = sock_err - self.assertRaises(RuntimeError, self.service._get_socket, - None, None, None) - - @mock.patch("murano.common.wsgi.eventlet") - def test_service_start(self, eventlet): - self.service = wsgi.Service(None, 1) - self.service.start() - self.assertTrue(eventlet.spawn.called) - - def test_service_stop(self): - self.service = wsgi.Service(None, 1) - self.service.greenthread = mock.Mock() - self.service.stop() - self.assertTrue(self.service.greenthread.kill.called) - - def test_service_properties(self): - self.service = wsgi.Service(None, 1) - self.service._socket = mock.Mock() - self.service._socket.getsockname.return_value = ["host", "port"] - host = self.service.host - port = self.service.port - self.assertEqual("host", host) - self.assertEqual("port", port) - - @mock.patch("murano.common.wsgi.logging") - def test_service_reset(self, logging): - self.service = wsgi.Service(None, 1) - self.service.reset() - self.assertTrue(logging.setup.called) - - @mock.patch("murano.common.wsgi.eventlet") - def test_service_run(self, eventlet): - self.service = wsgi.Service(None, 1) - self.service._run(None, None) - self.assertTrue(eventlet.wsgi.server.called) - - def test_backlog_prop(self): - service = wsgi.Service(None, None) - service._backlog = mock.sentinel.backlog - self.assertEqual(mock.sentinel.backlog, service.backlog) - - -class TestMiddleware(base.MuranoTestCase): - - def test_middleware_call(self): - self.middleware = wsgi.Middleware(None) - mock_request = mock.Mock() - mock_request.get_response.return_value = "a response" - self.assertEqual("a response", self.middleware(mock_request)) - - def test_call_with_response(self): - middleware = wsgi.Middleware(None) - middleware.process_request = mock.Mock(return_value=mock.sentinel.resp) - - resp = middleware(mock.sentinel.req) - - self.assertEqual(mock.sentinel.resp, resp) - middleware.process_request.assert_called_once_with(mock.sentinel.req) - - -class TestDebug(base.MuranoTestCase): - - def test_debug_call(self): - self.debug = wsgi.Debug(None) - mock_request = mock.Mock() - mock_request.environ = {"one": 1, "two": 2} - - mock_response = mock.Mock() - mock_response.headers = {"one": 1, "two": 2} - mock_request.get_response.return_value = mock_response - - self.assertEqual(mock_response, self.debug(mock_request)) - - @mock.patch('sys.stdout') - def test_print_generator(self, mock_stdout): - for x in wsgi.Debug.print_generator(['foo', 'bar', 'baz']): - pass - mock_stdout.write.assert_has_calls([ - mock.call('**************************************** BODY'), - mock.call('\n'), - mock.call('foo'), - mock.call('bar'), - mock.call('baz'), - mock.call(''), - mock.call('\n') - ]) - - -class TestRequest(base.MuranoTestCase): - - @mock.patch.object(wsgi.webob.BaseRequest, 'path', - **{'rsplit.return_value': ['foo.bar', 'baz']}) - def test_best_match_content_type_with_multi_part_path(self, mock_path): - request = wsgi.Request({}) - supported_content_types = ['application/baz'] - result = request.best_match_content_type(None, supported_content_types) - self.assertEqual('application/baz', result) - - -class TestResource(base.MuranoTestCase): - - def test_resource_call_exceptions(self): - self.resource = wsgi.Resource(None) - self.resource.deserialize_request = mock.Mock() - self.resource.deserialize_request.side_effect = ( - exceptions.UnsupportedContentType - ) - mock_request = mock.Mock() - mock_request.headers = {} - result = self.resource(mock_request) - self.assertEqual(415, result.status_code) - - self.resource.deserialize_request.side_effect = ( - exceptions.MalformedRequestBody - ) - result = self.resource(mock_request) - self.assertEqual(400, result.status_code) - - self.resource.deserialize_request.side_effect = None - self.resource.deserialize_request.return_value = ["", {"k": "v"}, ""] - self.resource.execute_action = mock.Mock() - self.resource.serialize_response = mock.Mock() - self.resource.serialize_response.side_effect = Exception - - result = self.resource(mock_request) - self.assertIsNotNone(result) - - def test_get_action_args(self): - self.resource = wsgi.Resource(None) - result = self.resource.get_action_args(None) - self.assertEqual({}, result) - - request_environment = {'wsgiorg.routing_args': ["arg_0", {"k": "v"}]} - result = self.resource.get_action_args(request_environment) - self.assertEqual({"k": "v"}, result) - - def test_dispatch_except_attribute_error(self): - mock_obj = mock.Mock(spec=wsgi.Resource) - setattr(mock_obj, 'default', - mock.Mock(return_value=mock.sentinel.ret_value)) - - resource = wsgi.Resource(None) - result = resource.dispatch(mock_obj, 'invalid_action') - - self.assertEqual(mock.sentinel.ret_value, result) - mock_obj.default.assert_called_once_with() - - -class TestActionDispatcher(base.MuranoTestCase): - - def test_default(self): - action_dispatcher = wsgi.ActionDispatcher() - self.assertRaises(NotImplementedError, action_dispatcher.default, None) - - -class TestDictSerializer(base.MuranoTestCase): - - def test_default(self): - dict_serializer = wsgi.DictSerializer() - self.assertEqual("", dict_serializer.default(None)) - - -class TestXMLDictSerializer(base.MuranoTestCase): - - def test_router_dispatch_not_found(self): - self.router = wsgi.Router(None) - req = mock.Mock() - req.environ = {'wsgiorg.routing_args': [False, False]} - response = self.router._dispatch(req) - self.assertIsInstance(response, webob.exc.HTTPNotFound) - - def test_XML_Dict_Serializer_default_string(self): - self.serializer = wsgi.XMLDictSerializer() - actual_xml = self.serializer.default({"XML Root": "some data"}) - expected_xml = b'some data\n' - self.assertEqual(expected_xml, actual_xml) - - def test_XML_Dict_Serializer_node_dict(self): - self.serializer = wsgi.XMLDictSerializer() - doc = minidom.Document() - metadata = mock.Mock() - metadata.get.return_value = {"node": {"item_name": "name", - "item_key": "key"}} - nodename = "node" - data = {"data_key": "data_value"} - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual('data_value', - result.childNodes[0].toxml()) - - mock_get_nodename = mock.Mock() - mock_get_nodename.get.return_vale = "k" - metadata.get.return_value = {"k": "v"} - nodename = "node" - data = {"data_key": "data_value"} - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual("node", result.nodeName) - - metadata.get.return_value = {} - nodename = "node" - data = {"data_key": "data_value"} - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual('data_value', - result.childNodes[0].toxml()) - - def test_XML_Dict_Serializer_node_list(self): - self.serializer = wsgi.XMLDictSerializer() - doc = minidom.Document() - metadata = mock.Mock() - metadata.get.return_value = {"node": {"item_name": "name", - "item_key": "key"}} - nodename = "node" - data = ["data_1", "data_2", "data_3"] - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual('', result.childNodes[0].toxml()) - - nodename = "not_node" - data = ["data_1", "data_2", "data_3"] - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual(3, len(result.childNodes)) - - nodename = "nodes" - data = ["data_1", "data_2", "data_3"] - result = self.serializer._to_xml_node(doc, metadata, nodename, data) - self.assertEqual(3, len(result.childNodes)) - - def test_XML_Dict_Serializer_create_link_nodes(self): - self.serializer = wsgi.XMLDictSerializer() - xml_doc = minidom.Document() - links = [{"rel": "rel", "href": "href", "type": "type"}] - link_nodes = self.serializer._create_link_nodes(xml_doc, links) - result = link_nodes[0].toxml() - for item in ['down>up mode. - """ - - env = alembic_script.ScriptDirectory.from_config(self.ALEMBIC_CONFIG) - versions = [] - for rev in env.walk_revisions(): - versions.append((rev.revision, rev.down_revision or '-1')) - - versions.reverse() - return versions - - def walk_versions(self, engine=None, snake_walk=False, downgrade=True): - # Determine latest version script from the repo, then - # upgrade from 1 through to the latest, with no data - # in the databases. This just checks that the schema itself - # upgrades successfully. - - self._configure(engine) - up_and_down_versions = self._up_and_down_versions() - for ver_up, ver_down in up_and_down_versions: - # upgrade -> downgrade -> upgrade - self._migrate_up(engine, ver_up, with_data=True) - if snake_walk: - downgraded = self._migrate_down(engine, - ver_down, - with_data=True, - next_version=ver_up) - if downgraded: - self._migrate_up(engine, ver_up) - - if downgrade: - # Now walk it back down to 0 from the latest, testing - # the downgrade paths. - up_and_down_versions.reverse() - for ver_up, ver_down in up_and_down_versions: - # downgrade -> upgrade -> downgrade - downgraded = self._migrate_down(engine, - ver_down, next_version=ver_up) - - if snake_walk and downgraded: - self._migrate_up(engine, ver_up) - self._migrate_down(engine, ver_down, next_version=ver_up) - - def _get_version_from_db(self, engine): - """Fetches latest version of migrate repo from database - - For each type of migrate repo, the latest version from db - will be returned. - """ - conn = engine.connect() - try: - context = migration.MigrationContext.configure(conn) - version = context.get_current_revision() or '-1' - finally: - conn.close() - return version - - def _migrate(self, engine, version, cmd): - """Base method for manipulation with migrate repo - - It will upgrade or downgrade the actual database. - """ - - self._alembic_command(cmd, engine, self.ALEMBIC_CONFIG, version) - - def _migrate_down(self, engine, version, with_data=False, - next_version=None): - try: - self._migrate(engine, version, 'downgrade') - except NotImplementedError: - # NOTE(sirp): some migrations, namely release-level - # migrations, don't support a downgrade. - return False - self.assertEqual(version, self._get_version_from_db(engine)) - - # NOTE(sirp): `version` is what we're downgrading to (i.e. the 'target' - # version). So if we have any downgrade checks, they need to be run for - # the previous (higher numbered) migration. - if with_data: - post_downgrade = getattr( - self, "_post_downgrade_%s" % next_version, None) - if post_downgrade: - post_downgrade(engine) - - return True - - def _migrate_up(self, engine, version, with_data=False): - """Migrate up to a new version of the db. - - We allow for data insertion and post checks at every - migration version with special _pre_upgrade_### and - _check_### functions in the main test. - """ - # NOTE(sdague): try block is here because it's impossible to debug - # where a failed data migration happens otherwise - check_version = version - try: - if with_data: - data = None - pre_upgrade = getattr( - self, "_pre_upgrade_%s" % check_version, None) - if pre_upgrade: - data = pre_upgrade(engine) - self._migrate(engine, version, 'upgrade') - self.assertEqual(version, self._get_version_from_db(engine)) - if with_data: - check = getattr(self, "_check_%s" % check_version, None) - if check: - check(engine, data) - except Exception: - LOG.error("Failed to migrate to version {ver} on engine {eng}" - .format(ver=version, eng=engine)) - raise diff --git a/murano/tests/unit/db/services/__init__.py b/murano/tests/unit/db/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/db/services/environment_templates.py b/murano/tests/unit/db/services/environment_templates.py deleted file mode 100644 index 27d47228d..000000000 --- a/murano/tests/unit/db/services/environment_templates.py +++ /dev/null @@ -1,177 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D. - -# Licensed 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 fixtures - - -class EmptyEnvironmentFixture(fixtures.Fixture): - def setUp(self): - super(EmptyEnvironmentFixture, self).setUp() - self.env_desc = { - "tenant_id": "tenant_id", - "name": "my_environment", - "id": "template_id" - } - self.addCleanup(delattr, self, 'env_desc') - - -class EmptyEnvironmentTemplateFixture(fixtures.Fixture): - def setUp(self): - super(EmptyEnvironmentTemplateFixture, self).setUp() - self.environment_template_desc = { - "tenant_id": "tenant_id", - "name": "my_template", - "id": "template_id" - } - self.addCleanup(delattr, self, 'environment_template_desc') - - -class AppEnvTemplateFixture(fixtures.Fixture): - def setUp(self): - super(AppEnvTemplateFixture, self).setUp() - self.env_template_desc = \ - { - "services": [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "_26411a1861294160833743e45d0eaad9": { - "name": "tomcat" - }, - "type": "io.murano.apps.apache.Tomcat", - "id": "tomcat_id" - }, - "port": "8080" - }, { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "password": "XXX", "name": - "mysql", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "mysql" - }, - "type": "io.murano.apps.database.MySQL", - "id": "54aaa43d-5970" - } - } - ], - "tenant_id": "tenant_id", - "name": "template_name", - 'id': 'template_id' - } - self.addCleanup(delattr, self, 'env_template_desc') - - -class ApplicationsFixture(fixtures.Fixture): - - def setUp(self): - super(ApplicationsFixture, self).setUp() - self.applications_desc = [ - { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "_26411a1861294160833743e45d0eaad9": { - "name": "tomcat" - }, - "type": "io.murano.apps.apache.Tomcat", - "id": "tomcat_id" - }, - "port": "8080" - }, - { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "password": "XXX", "name": - "mysql", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "mysql" - }, - "type": "io.murano.apps.database.MySQL", - "id": "54aaa43d-5970" - } - } - ] - self.addCleanup(delattr, self, 'applications_desc') - - -class ApplicationTomcatFixture(fixtures.Fixture): - - def setUp(self): - super(ApplicationTomcatFixture, self).setUp() - self.application_tomcat_desc = { - "instance": { - "assignFloatingIp": "true", - "keyname": "mykeyname", - "image": "cloud-fedora-v3", - "flavor": "m1.medium", - "?": { - "type": "io.murano.resources.LinuxInstance", - "id": "ef984a74-29a4-45c0-b1dc-2ab9f075732e" - } - }, - "name": "orion", - "?": - { - "_26411a1861294160833743e45d0eaad9": { - "name": "tomcat" - }, - "type": "io.murano.apps.apache.Tomcat", - "id": "tomcat_id" - }, - "port": "8080" - } - self.addCleanup(delattr, self, 'application_tomcat_desc') - - -class ApplicationMysqlFixture(fixtures.Fixture): - - def setUp(self): - super(ApplicationMysqlFixture, self).setUp() - self.application_mysql_desc = { - "instance": "ef984a74-29a4-45c0-b1dc-2ab9f075732e", - "password": "XXX", "name": - "mysql", - "?": { - "_26411a1861294160833743e45d0eaad9": { - "name": "mysql" - }, - "type": "io.murano.apps.database.MySQL", - "id": "54aaa43d-5970" - } - } - self.addCleanup(delattr, self, 'application_mysql_desc') diff --git a/murano/tests/unit/db/services/test_cf_connections.py b/murano/tests/unit/db/services/test_cf_connections.py deleted file mode 100644 index 008d91ffd..000000000 --- a/murano/tests/unit/db/services/test_cf_connections.py +++ /dev/null @@ -1,172 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 murano.db import models -from murano.db.services import cf_connections -from murano.db.services import environments -from murano.db import session as db_session -from murano.tests.unit import base - - -class TestCFConnection(base.MuranoWithDBTestCase): - - def setUp(self): - super(TestCFConnection, self).setUp() - self.resources = [] - self.tenant_id = 'test_tenant_id' - - environment = models.Environment( - name='test_environment', - tenant_id='test_tenant_id', - version=1 - ) - alt_environment = models.Environment( - name='alt_test_environment', - tenant_id='alt_test_tenant_id', - version=1 - ) - - unit = db_session.get_session() - with unit.begin(): - unit.add_all([environment, alt_environment]) - - self.environment_id = environment.id - self.alt_environment_id = alt_environment.id - - def tearDown(self): - super(TestCFConnection, self).tearDown() - unit = db_session.get_session() - - if self.environment_id: - environments.EnvironmentServices.remove(self.environment_id) - if self.alt_environment_id: - environments.EnvironmentServices.remove(self.alt_environment_id) - - for resource in self.resources: - if resource: - with unit.begin(): - unit.delete(resource) - - def _create_resource(self, resource_name, **kwargs): - unit = db_session.get_session() - model = None - new_resource_id = 1 - - if resource_name == 'cf_org': - model = models.CFOrganization - elif resource_name == 'cf_space': - model = models.CFSpace - elif resource_name == 'cf_service': - model = models.CFServiceInstance - - resource = unit.query(model).order_by(model.id.desc()).first() - if resource: - new_resource_id = int(resource.id) + 1 - new_resource = model(id=new_resource_id, **kwargs) - - with unit.begin(): - unit.add(new_resource) - - created_resource = unit.query(model)\ - .order_by(model.id.desc()).first() - self.assertIsNotNone(created_resource) - self.assertEqual(created_resource.id, str(new_resource.id)) - self.resources.append(created_resource) - - return created_resource - - def test_set_tenant_for_org(self): - cf_org = self._create_resource('cf_org', tenant=self.tenant_id) - cf_connections.set_tenant_for_org(cf_org.id, 'another_test_tenant_id') - - duplicate_cf_org = None - unit = db_session.get_session() - with unit.begin(): - duplicate_cf_org = unit.query(models.CFOrganization).get(cf_org.id) - self.assertIsNotNone(duplicate_cf_org) - self.assertEqual('another_test_tenant_id', - duplicate_cf_org.tenant) - - def test_set_environment_for_space(self): - cf_space = self._create_resource('cf_space', - environment_id=self.environment_id) - cf_connections.set_environment_for_space(cf_space.id, - self.alt_environment_id) - - duplicate_cf_space = None - unit = db_session.get_session() - with unit.begin(): - duplicate_cf_space = unit.query(models.CFSpace).get(cf_space.id) - self.assertIsNotNone(duplicate_cf_space) - self.assertEqual(self.alt_environment_id, - duplicate_cf_space.environment_id) - - def test_set_instance_for_service(self): - cf_service = self._create_resource('cf_service', - service_id='123', - environment_id=self.environment_id, - tenant=self.tenant_id) - cf_connections.set_instance_for_service( - instance_id=cf_service.id, service_id='123', - environment_id=self.alt_environment_id, tenant=self.tenant_id) - - duplicate_cf_service = None - unit = db_session.get_session() - with unit.begin(): - duplicate_cf_service = unit.query(models.CFServiceInstance).\ - get(cf_service.id) - self.assertIsNotNone(duplicate_cf_service) - self.assertEqual(self.alt_environment_id, - duplicate_cf_service.environment_id) - - def test_get_environment_for_space(self): - cf_space = self._create_resource('cf_space', - environment_id=self.environment_id) - retrieved_env_id =\ - cf_connections.get_environment_for_space(cf_space.id) - self.assertEqual(cf_space.environment_id, - retrieved_env_id) - - def test_get_tenant_for_org(self): - cf_org = self._create_resource('cf_org', tenant=self.tenant_id) - retrieved_tenant =\ - cf_connections.get_tenant_for_org(cf_org.id) - self.assertEqual(self.tenant_id, retrieved_tenant) - - def test_get_service_for_instance(self): - cf_service = self._create_resource('cf_service', - service_id='123', - environment_id=self.environment_id, - tenant=self.tenant_id) - retrieved_service =\ - cf_connections.get_service_for_instance(cf_service.id) - for attr in ['id', 'environment_id', 'service_id', 'tenant']: - self.assertEqual(getattr(cf_service, attr), - getattr(retrieved_service, attr)) - - def test_delete_environment_from_space(self): - environment = models.Environment( - name='test_environment_1', - tenant_id='test_tenant_id_1', - version=1 - ) - unit = db_session.get_session() - with unit.begin(): - unit.add(environment) - - cf_connections.delete_environment_from_space(environment.id) - with unit.begin(): - retrieved_env = unit.query(models.Environment).get(environment.id) - self.assertIsNotNone(retrieved_env) diff --git a/murano/tests/unit/db/services/test_core_service.py b/murano/tests/unit/db/services/test_core_service.py deleted file mode 100644 index f7e245593..000000000 --- a/murano/tests/unit/db/services/test_core_service.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D. - -# Licensed 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 unittest import mock - -from webob import exc - -from murano.db.services import core_services -from murano.tests.unit import base -from murano.tests.unit.db.services import environment_templates as et - - -class TestCoreServices(base.MuranoTestCase): - def setUp(self): - super(TestCoreServices, self).setUp() - self.core_services = core_services.CoreServices - self.addCleanup(mock.patch.stopall) - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_empty_template(self, template_services_mock): - """Check obtaining the template description without services.""" - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - template_services_mock.get_description.return_value = \ - fixture.environment_template_desc - template_des = self.core_services.get_template_data('any', '/services') - self.assertEqual([], template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_template_services(self, template_services_mock): - """Check obtaining the template description with services.""" - fixture_apps = self.useFixture(et.ApplicationsFixture()) - fixture_env_apps = self.useFixture(et.AppEnvTemplateFixture()) - template_services_mock.get_description.return_value = \ - fixture_env_apps.env_template_desc - template_des = self.core_services.get_template_data('any', '/services') - self.assertEqual(fixture_apps.applications_desc, template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_template_get_service(self, template_services_mock): - """Check obtaining the service description.""" - fixture = self.useFixture(et.AppEnvTemplateFixture()) - fixture2 = self.useFixture(et.ApplicationTomcatFixture()) - template_services_mock.get_description.return_value = \ - fixture.env_template_desc - template_des = \ - self.core_services.get_template_data('any', - '/services/tomcat_id') - self.assertEqual(fixture2.application_tomcat_desc, template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_template_post_services(self, template_services_mock): - """Check adding a service to a template.""" - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - fixture2 = self.useFixture(et.AppEnvTemplateFixture()) - template_services_mock.get_description.return_value = \ - fixture.environment_template_desc - template_des = self.core_services.\ - post_env_template_data('any', - fixture2.env_template_desc, - '/services') - self.assertEqual(fixture2.env_template_desc, template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_template_delete_services(self, template_services_mock): - """Check deleting a service in a template.""" - fixture2 = self.useFixture(et.AppEnvTemplateFixture()) - fixture = self.useFixture(et.ApplicationTomcatFixture()) - template_services_mock.get_description.return_value = \ - fixture2.env_template_desc - self.core_services.\ - delete_env_template_data('any', - '/services/54aaa43d-5970') - template_des = self.core_services.get_template_data('any', '/services') - self.assertEqual([fixture.application_tomcat_desc], template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_get_template_no_exists(self, template_services_mock): - """Check obtaining a non-existing service.""" - fixture2 = self.useFixture(et.AppEnvTemplateFixture()) - - template_services_mock.get_description.return_value = \ - fixture2.env_template_desc - self.assertRaises(exc.HTTPNotFound, - self.core_services.get_template_data, - 'any', '/services/noexists') - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_adding_services(self, template_services_mock): - """Check adding services to a template.""" - ftomcat = self.useFixture(et.ApplicationTomcatFixture()) - fmysql = self.useFixture(et.ApplicationMysqlFixture()) - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - fservices = self.useFixture(et.ApplicationsFixture()) - template_services_mock.get_description.return_value =\ - fixture.environment_template_desc - self.core_services.\ - post_env_template_data('any', - ftomcat.application_tomcat_desc, - '/services') - self.core_services.\ - post_env_template_data('any', - fmysql.application_mysql_desc, - '/services') - template_des = \ - self.core_services.get_template_data('any', '/services') - self.assertEqual(fservices.applications_desc, template_des) - template_services_mock.get_description.assert_called_with('any') - - @mock.patch('murano.db.services.environment_templates.EnvTemplateServices') - def test_none_temp_description(self, template_services_mock): - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - template_services_mock.get_description.return_value = None - self.assertIsNone(self.core_services. - get_template_data('any', '/services')) - self.assertRaises(exc.HTTPNotFound, - self.core_services.post_env_template_data, - 'any', fixture.environment_template_desc, - '/services') - self.assertRaises(exc.HTTPNotFound, - self.core_services.post_application_data, - 'any', fixture.environment_template_desc, - '/services') - self.assertRaises(exc.HTTPNotFound, - self.core_services.delete_env_template_data, - 'any', '/services') - self.assertRaises(exc.HTTPNotFound, - self.core_services.put_application_data, - 'any', fixture.environment_template_desc, - '/services') - - @mock.patch('murano.common.utils.TraverseHelper') - @mock.patch('murano.db.services.environments.EnvironmentServices') - def test_post_data(self, env_services_mock, source_mock): - fixture = self.useFixture(et.EmptyEnvironmentFixture()) - env_services_mock.get_description.return_value = fixture.env_desc - session_id = None - ftomcat = self.useFixture(et.ApplicationTomcatFixture()) - self.assertEqual(self.core_services.post_data('any', session_id, - ftomcat.application_tomcat_desc, - '/services'), ftomcat.application_tomcat_desc) - self.assertTrue(source_mock.insert.called) - - self.assertEqual(self.core_services.put_data('any', session_id, - ftomcat.application_tomcat_desc, - '/services'), ftomcat.application_tomcat_desc) - self.assertTrue(source_mock.update.called) - - self.core_services.delete_data('any', session_id, '/services') - self.assertTrue(source_mock.remove.called) - - @mock.patch('murano.db.services.environments.EnvironmentServices') - def test_get_service_status(self, env_services_mock): - service_id = 12 - fixture = self.useFixture(et.EmptyEnvironmentFixture()) - env_services_mock.get_description.return_value = fixture.env_desc - self.core_services.get_service_status('any', service_id) - self.assertTrue(env_services_mock.get_status.called) diff --git a/murano/tests/unit/db/services/test_environments.py b/murano/tests/unit/db/services/test_environments.py deleted file mode 100644 index aa383a9b6..000000000 --- a/murano/tests/unit/db/services/test_environments.py +++ /dev/null @@ -1,133 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 datetime as dt -from unittest import mock -import uuid - -from oslo_utils import timeutils - -from murano.db import models -from murano.db.services import environments -from murano.db import session as db_session -from murano.services import states -from murano.tests.unit import base -from murano.tests.unit import utils - -OLD_VERSION = 0 -LATEST_VERSION = 1 - - -class TestEnvironmentServices(base.MuranoWithDBTestCase): - def setUp(self): - super(TestEnvironmentServices, self).setUp() - self.environment = models.Environment( - name='test_environment', tenant_id='test_tenant_id', - version=LATEST_VERSION - ) - - self.env_services = environments.EnvironmentServices() - - def test_environment_ready_if_last_session_deployed_after_failed(self): - """Test environment ready status - - If last session was deployed successfully and other session - was failed - environment must have status "ready". - - Bug: #1413260 - """ - - session = db_session.get_session() - - session.add(self.environment) - - now = timeutils.utcnow() - - session_1 = models.Session( - environment=self.environment, user_id='test_user_id_1', - version=OLD_VERSION, - state=states.SessionState.DEPLOY_FAILURE, - updated=now, description={} - ) - session_2 = models.Session( - environment=self.environment, user_id='test_user_id_2', - version=LATEST_VERSION, - state=states.SessionState.DEPLOYED, - updated=now + dt.timedelta(minutes=1), description={} - ) - session.add_all([session_1, session_2]) - session.flush() - - expected_status = states.EnvironmentStatus.READY - actual_status = environments.EnvironmentServices.get_status( - self.environment.id - ) - - self.assertEqual(expected_status, actual_status) - - @mock.patch("murano.db.services.environments.auth_utils") - def test_get_network_driver(self, mock_authentication): - self.tenant_id = str(uuid.uuid4()) - self.context = utils.dummy_context(tenant_id=self.tenant_id) - - driver_context = self.env_services.get_network_driver(self.context) - self.assertEqual(driver_context, "neutron") - - def test_get_status(self): - session = db_session.get_session() - - session.add(self.environment) - - now = timeutils.utcnow() - - session_1 = models.Session( - environment=self.environment, user_id='test_user_id_1', - version=OLD_VERSION, - state=states.SessionState.DEPLOY_FAILURE, - updated=now, description={} - ) - - session.add(session_1) - session.flush() - - expected_status = states.EnvironmentStatus.DEPLOY_FAILURE - self.assertEqual(expected_status, self.env_services.get_status( - self.environment.id)) - - def test_delete_failure_get_description(self): - session = db_session.get_session() - - session.add(self.environment) - - now = timeutils.utcnow() - - session_1 = models.Session( - environment=self.environment, user_id='test_user_id_1', - version=OLD_VERSION, - state=states.SessionState.DELETE_FAILURE, - updated=now, description={} - ) - - session.add(session_1) - session.flush() - - expected_status = states.EnvironmentStatus.DELETE_FAILURE - self.assertEqual(expected_status, self.env_services.get_status( - self.environment.id)) - - env_id = self.environment.id - description = (self.env_services. - get_environment_description(env_id, - session_id=None, - inner=False)) - self.assertEqual({}, description) diff --git a/murano/tests/unit/db/services/test_instances.py b/murano/tests/unit/db/services/test_instances.py deleted file mode 100644 index 1144133ff..000000000 --- a/murano/tests/unit/db/services/test_instances.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from oslo_db import exception -from oslo_utils import timeutils - -from murano.db.services import instances -from murano.tests.unit import base as test_base - - -@mock.patch('murano.db.services.instances.db_session') -class TestInstances(test_base.MuranoTestCase): - - @mock.patch('murano.db.services.instances.timeutils') - @mock.patch('murano.db.services.instances.models') - def test_track_instance(self, mock_models, mock_timeutils, - mock_db_session): - mock_env = mock.MagicMock(tenant_id='test_tenant_id') - mock_db_session.get_session().query().get.return_value = mock_env - now = timeutils.utcnow_ts() - mock_timeutils.utcnow_ts.return_value = now - - track_instance = instances.InstanceStatsServices.track_instance - track_instance('test_instance_id', 'test_env_id', 'test_type', - 'test_type_name', 'test_type_title', unit_count=1) - - mock_models.Instance.assert_called_once_with() - self.assertEqual('test_instance_id', - mock_models.Instance().instance_id) - self.assertEqual('test_env_id', mock_models.Instance().environment_id) - self.assertEqual('test_instance_id', - mock_models.Instance().instance_id) - self.assertEqual('test_tenant_id', mock_models.Instance().tenant_id) - self.assertEqual('test_type', mock_models.Instance().instance_type) - self.assertEqual(now, mock_models.Instance().created) - self.assertIsNone(mock_models.Instance().destroyed) - self.assertEqual('test_type_name', mock_models.Instance().type_name) - self.assertEqual('test_type_title', mock_models.Instance().type_title) - self.assertEqual(1, mock_models.Instance().unit_count) - mock_db_session.get_session().add.assert_called_once_with( - mock_models.Instance()) - - @mock.patch('murano.db.services.instances.sqlalchemy') - @mock.patch('murano.db.services.instances.models') - def test_track_instance_except_duplicate_entry(self, _, mock_sqlalchemy, - mock_db_session): - mock_db_session.get_session().add.side_effect =\ - exception.DBDuplicateEntry - - track_instance = instances.InstanceStatsServices.track_instance - track_instance('test_instance_id', 'test_env_id', 'test_type', - 'test_type_name', 'test_type_title') - - self.assertEqual(1, mock_db_session.get_session().execute.call_count) - self.assertEqual(1, mock_sqlalchemy.update().where.call_count) - - @mock.patch('murano.db.services.instances.timeutils') - def test_destroy_instance(self, mock_timeutils, mock_db_session): - mock_instance = mock.MagicMock(destroyed=False) - mock_db_session.get_session().query().get.return_value = mock_instance - now = timeutils.utcnow_ts() - mock_timeutils.utcnow_ts.return_value = now - - destroy_instance = instances.InstanceStatsServices.destroy_instance - destroy_instance('test_instance_id', 'test_environment_id') - - self.assertEqual(now, mock_instance.destroyed) - mock_instance.save.assert_called_once_with( - mock_db_session.get_session()) - - def test_get_aggregated_stats(self, mock_db_session): - mock_db_session.get_session().query().filter().group_by().all\ - .return_value = [['1', '2', '3'], ['4', '5', '6']] - - get_stats = instances.InstanceStatsServices.get_aggregated_stats - result = get_stats('test_env_id') - - expected_result = [ - {'type': 1, 'duration': 2, 'count': 3}, - {'type': 4, 'duration': 5, 'count': 6}, - ] - - self.assertEqual(expected_result, result) - - @mock.patch('murano.db.services.instances.timeutils') - def test_get_raw_environment_stats(self, mock_timeutils, mock_db_session): - now = timeutils.utcnow_ts() - mock_timeutils.utcnow_ts.return_value = now - mock_db_session.get_session().query().filter().filter().all.\ - return_value = [ - mock.MagicMock(instance_type='test_instance_type', - created=now - 1000, - destroyed=now, - type_name='test_type_name', - unit_count=1, - instance_id='test_instance_id', - type_title='test_type_title'), - mock.MagicMock(instance_type='test_instance_type_2', - created=now - 2000, - destroyed=None, - type_name='test_type_name_2', - unit_count=2, - instance_id='test_instance_id_2', - type_title='test_type_title_2') - ] - mock_db_session.reset_mock() - - get_env_stats = instances.InstanceStatsServices.\ - get_raw_environment_stats - result = get_env_stats('test_env_id', 'test_instance_id') - - expected_result = [ - { - 'type': 'test_instance_type', - 'duration': 1000, # now - (now - 1000) = 1000 - 'type_name': 'test_type_name', - 'unit_count': 1, - 'instance_id': 'test_instance_id', - 'type_title': 'test_type_title', - 'active': False - }, - { - 'type': 'test_instance_type_2', - 'duration': 2000, # now - (now - 2000) = 2000 - 'type_name': 'test_type_name_2', - 'unit_count': 2, - 'instance_id': 'test_instance_id_2', - 'type_title': 'test_type_title_2', - 'active': True - }, - ] - - self.assertEqual(expected_result, result) - # Assert that two filters were performed, the second one for - # the instance_id argument. - self.assertEqual(1, - mock_db_session.get_session().query().filter. - call_count) - self.assertEqual(1, - mock_db_session.get_session().query().filter(). - filter.call_count) diff --git a/murano/tests/unit/db/services/test_stats.py b/murano/tests/unit/db/services/test_stats.py deleted file mode 100644 index 05935834a..000000000 --- a/murano/tests/unit/db/services/test_stats.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 murano.db import models -from murano.db.services import stats as statistics -from murano.db import session as db_session -from murano.tests.unit import base - - -class TestStats(base.MuranoWithDBTestCase): - - def setUp(self): - super(TestStats, self).setUp() - - self.stats_list = [] - self.stats_kwargs = { - 'host': 'test_host', - 'request_count': 5, - 'error_count': 2, - 'average_response_time': 0.5, - 'requests_per_tenant': 1, - 'cpu_count': 4, - 'cpu_percent': 0.3 - } - self.stats_kwargs_2 = { - 'host': 'test_host_2', - 'request_count': 6, - 'error_count': 3, - 'average_response_time': 0.6, - 'requests_per_tenant': 2, - 'cpu_count': 5, - 'cpu_percent': 0.4 - } - - def tearDown(self): - super(TestStats, self).tearDown() - - unit = db_session.get_session() - for stat in self.stats_list: - with unit.begin(): - unit.delete(stat) - - def _create_stats(self, which_kwargs): - stats = models.ApiStats() - for key, val in which_kwargs.items(): - setattr(stats, key, val) - stats.requests_per_second = 1.2 - stats.errors_per_second = 2.3 - - unit = db_session.get_session() - with unit.begin(): - unit.add(stats) - - self.assertIsNotNone(stats) - self.stats_list.append(stats) - - return stats - - def _are_stats_equal(self, x, y, check_type=False): - comparison_attributes = ['host', 'request_count', 'error_count', - 'average_response_time', - 'requests_per_tenant', - 'requests_per_second', - 'errors_per_second', - 'cpu_count', 'cpu_percent'] - if check_type: - if type(x) != type(y): - return False - for attr in comparison_attributes: - try: - if str(getattr(x, attr)) != str(getattr(y, attr)): - return False - except AttributeError: - if str(x[attr]) != str(getattr(y, attr)): - return False - return True - - def test_create(self): - statistics.Statistics.create(**self.stats_kwargs) - - unit = db_session.get_session() - retrieved_stats = None - with unit.begin(): - retrieved_stats = unit.query(models.ApiStats)\ - .order_by(models.ApiStats.id.desc()).first() - - self.stats_kwargs['requests_per_second'] = 0.0 - self.stats_kwargs['errors_per_second'] = 0.0 - self.assertIsNotNone(retrieved_stats) - self.assertTrue( - self._are_stats_equal(self.stats_kwargs, retrieved_stats)) - - def test_update(self): - """Note: this test expects to test update functionality. - - However, the current implementation of Statistics.update() does not - actually update the host of the statistics object passed in. - It just saves the object that is passed in, which appears to contradict - its intended use case. - """ - stats = models.ApiStats() - for key, val in self.stats_kwargs.items(): - setattr(stats, key, val) - statistics.Statistics.update('test_host', stats) - - unit = db_session.get_session() - retrieved_stats = None - with unit.begin(): - retrieved_stats = unit.query(models.ApiStats)\ - .order_by(models.ApiStats.id.desc()).first() - self.assertIsNotNone(retrieved_stats) - self.assertTrue( - self._are_stats_equal(stats, retrieved_stats, check_type=True)) - - def test_get_stats_by_id(self): - stats = self._create_stats(self.stats_kwargs) - retrieved_stats = statistics.Statistics.get_stats_by_id(stats.id) - self.assertIsNotNone(retrieved_stats) - self.assertTrue( - self._are_stats_equal(stats, retrieved_stats, check_type=True)) - - def test_get_stats_by_host(self): - stats = self._create_stats(self.stats_kwargs) - retrieved_stats = statistics.Statistics.get_stats_by_host(stats.host) - self.assertIsNotNone(retrieved_stats) - self.assertTrue( - self._are_stats_equal(stats, retrieved_stats, check_type=True)) - - def test_get_all(self): - stats = self._create_stats(self.stats_kwargs) - retrieved_stats = statistics.Statistics.get_all() - self.assertIsInstance(retrieved_stats, list) - self.assertEqual(1, len(retrieved_stats)) - self.assertTrue( - self._are_stats_equal(stats, retrieved_stats[0], check_type=True)) - - stats = self._create_stats(self.stats_kwargs_2) - retrieved_stats = statistics.Statistics.get_all() - self.assertIsInstance(retrieved_stats, list) - self.assertEqual(2, len(retrieved_stats)) - self.assertTrue( - self._are_stats_equal(stats, retrieved_stats[1], check_type=True)) diff --git a/murano/tests/unit/db/services/test_templates_service.py b/murano/tests/unit/db/services/test_templates_service.py deleted file mode 100644 index 29ccaed33..000000000 --- a/murano/tests/unit/db/services/test_templates_service.py +++ /dev/null @@ -1,117 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D. - -# Licensed 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 unittest import mock - -import fixtures - -from murano.db.services import environment_templates as env_temp -from murano.tests.unit import base -from murano.tests.unit.db.services import environment_templates as et - - -class TestTemplateServices(base.MuranoWithDBTestCase, - fixtures.TestWithFixtures): - - def setUp(self): - super(TestTemplateServices, self).setUp() - self.template_services = env_temp.EnvTemplateServices - self.uuids = ['template_id', 'template_id2'] - self.mock_uuid = self._stub_uuid(self.uuids) - self.addCleanup(mock.patch.stopall) - - def test_create_template(self): - """Check creating a template without services.""" - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - """Check the creation of a template.""" - body = { - "name": "my_template" - } - template_des = self.template_services.create(body, 'tenant_id') - self.assertEqual(fixture.environment_template_desc, - template_des.description) - - def test_clone_template(self): - """Check the clonation of a template.""" - body = { - "name": "my_template" - } - template = self.template_services.create(body, 'tenant_id') - cloned_template = self.template_services.clone(template['id'], - 'id2', - "my_template2", False) - self.assertEqual(cloned_template.description['name'], 'my_template2') - self.assertEqual(cloned_template.description['tenant_id'], 'id2') - - def test_get_empty_template(self): - """Check obtaining information about a template without services.""" - fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture()) - self.test_create_template() - template = \ - self.template_services.get_description("template_id") - self.assertEqual(fixture.environment_template_desc, template) - - def test_get_template_services(self): - """Check obtaining information about a template with services.""" - fixture = self.useFixture(et.AppEnvTemplateFixture()) - template = self.template_services.create(fixture.env_template_desc, - 'tenant_id') - self.assertEqual(fixture.env_template_desc, template.description) - template_des = \ - self.template_services.get_description("template_id") - self.assertEqual(fixture.env_template_desc, template_des) - - def test_get_template_no_exists(self): - """Check obtaining information about a non-existent template - - Check obtaining information about a template which - does not exist. - """ - self.assertRaises(ValueError, - self.template_services.get_description, - 'no_exists') - - def test_delete_template(self): - """Check deleting a template.""" - self.test_create_template() - self.template_services.delete("template_id") - - def _stub_uuid(self, values=None): - class FakeUUID(object): - def __init__(self, v): - self.hex = v - - if values is None: - values = [] - mock_uuid4 = mock.patch('uuid.uuid4').start() - mock_uuid4.side_effect = [FakeUUID(v) for v in values] - return mock_uuid4 - - def test_get_application_description(self): - fixture = self.useFixture(et.AppEnvTemplateFixture()) - self.template_services.create(fixture.env_template_desc, - 'tenant_id') - template_app_des = (self.template_services. - get_application_description("template_id")) - self.assertEqual(template_app_des, - fixture.env_template_desc['services']) - - body = { - "name": "my_template" - } - self.template_services.create(body, 'tenant_id') - template_app_des_2 = (self.template_services. - get_application_description("template_id2")) - self.assertEqual([], template_app_des_2) diff --git a/murano/tests/unit/db/test_catalog.py b/murano/tests/unit/db/test_catalog.py deleted file mode 100644 index 00df740d9..000000000 --- a/murano/tests/unit/db/test_catalog.py +++ /dev/null @@ -1,577 +0,0 @@ -# Licensed 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 uuid - -from oslo_db import exception as db_exception -from webob import exc - -from murano.db.catalog import api -from murano.tests.unit import base -from murano.tests.unit import utils - - -class CatalogDBTestCase(base.MuranoWithDBTestCase): - - def setUp(self): - super(CatalogDBTestCase, self).setUp() - self.tenant_id = str(uuid.uuid4()) - self.tenant_id_2 = str(uuid.uuid4()) - self.context = utils.dummy_context(tenant_id=self.tenant_id) - self.context_2 = utils.dummy_context(tenant_id=self.tenant_id_2) - - self.context_admin = utils.dummy_context(tenant_id=self.tenant_id) - self.context_admin.is_admin = True - - def _create_categories(self): - api.category_add('cat1') - api.category_add('cat2') - - def _stub_package(self, **kwargs): - base = { - 'archive': b"archive blob here", - 'fully_qualified_name': 'com.example.package', - 'type': 'class', - 'author': 'OpenStack', - 'name': 'package', - 'enabled': True, - 'description': 'some text', - 'is_public': False, - 'tags': ['tag1', 'tag2'], - 'logo': b"logo blob here", - 'ui_definition': '{}', - } - base.update(**kwargs) - return base - - def get_change(self, op, path, value): - return { - 'op': op, - 'path': path, - 'value': value - } - - def test_order_by(self): - pkgs = [] - for dummy in range(10): - package = api.package_upload(self._stub_package( - name=str(uuid.uuid4()), - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkgs.append(package) - - pkg_created = [pkg.id for pkg in sorted( - pkgs, key=lambda _pkg: _pkg.created)] - pkg_name = [pkg.id for pkg in sorted( - pkgs, key=lambda _pkg: _pkg.name)] - pkg_fqn = [pkg.id for pkg in sorted( - pkgs, key=lambda _pkg: _pkg.fully_qualified_name)] - - for order, pkg_ids in zip(['created', 'name', 'fqn'], - [pkg_created, pkg_name, pkg_fqn]): - res = api.package_search( - {'order_by': [order]}, self.context, limit=10) - self.assertEqual(10, len(res)) - self.assertEqual(pkg_ids, [r.id for r in res]) - - def test_order_by_compound(self): - pkgs_a, pkgs_z = [], [] - for _ in range(5): - package = api.package_upload(self._stub_package( - name='z', - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkgs_z.append(package) - for _ in range(5): - package = api.package_upload(self._stub_package( - name='a', - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkgs_a.append(package) - - # sort pkg ids by pkg created - pkg_a_id = [pkg.id for pkg in sorted( - pkgs_a, key=lambda _pkg: _pkg.created)] - pkg_z_id = [pkg.id for pkg in sorted( - pkgs_z, key=lambda _pkg: _pkg.created)] - - res = api.package_search( - {'order_by': ['name', 'created']}, self.context, limit=10) - self.assertEqual(10, len(res)) - self.assertEqual(pkg_a_id + pkg_z_id, [r.id for r in res]) - - def test_pagination_backwards(self): - """Tests backwards pagination - - Creates 10 packages with unique names and iterates backwards, - checking that package order is correct. - """ - pkgs = [] - for dummy in range(10): - package = api.package_upload(self._stub_package( - name=str(uuid.uuid4()), - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkgs.append(package) - - # sort pkg ids by pkg name - pkg_ids = [pkg.id for pkg in sorted(pkgs, key=lambda _pkg: _pkg.name)] - - res = api.package_search({}, self.context, limit=10) - self.assertEqual(10, len(res)) - self.assertEqual(pkg_ids, [r.id for r in res]) - marker = res[-1].id - - res = api.package_search( - {'marker': marker, - 'sort_dir': 'desc'}, self.context, limit=5) - self.assertEqual(5, len(res)) - self.assertEqual(list(reversed(pkg_ids[4:9])), - [r.id for r in res]) - marker = res[-1].id - - res = api.package_search( - {'marker': marker, - 'sort_dir': 'desc'}, self.context, limit=5) - self.assertEqual(4, len(res)) - self.assertEqual(list(reversed(pkg_ids[0:4])), - [r.id for r in res]) - marker = res[-1].id - - res = api.package_search( - {'marker': marker, - 'sort_dir': 'desc'}, self.context, limit=5) - self.assertEqual(0, len(res)) - - def test_pagination(self): - """Tests that package order is correct - - Creates 10 packages with unique names and iterates through them, - checking that package order is correct. - """ - - pkgs = [] - for dummy in range(10): - package = api.package_upload(self._stub_package( - name=str(uuid.uuid4()), - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkgs.append(package) - - # sort pkg ids by pkg name - pkg_ids = [pkg.id for pkg in sorted(pkgs, key=lambda _pkg: _pkg.name)] - - res = api.package_search({}, self.context, limit=4) - self.assertEqual(4, len(res)) - self.assertEqual(pkg_ids[0:4], [r.id for r in res]) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(4, len(res)) - self.assertEqual(pkg_ids[4:8], [r.id for r in res]) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(2, len(res)) - self.assertEqual(pkg_ids[8:10], [r.id for r in res]) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(0, len(res)) - - def test_pagination_loops_through_names(self): - """Tests that packages with same display name are not skipped - - Creates 10 packages with the same display name and iterates - through them, checking that package are not skipped. - """ - - for dummy in range(10): - api.package_upload( - self._stub_package(name='test', - fully_qualified_name=str(uuid.uuid4())), - self.tenant_id) - res = api.package_search({}, self.context, limit=4) - self.assertEqual(4, len(res)) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(4, len(res)) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(2, len(res)) - marker = res[-1].id - - res = api.package_search({'marker': marker}, self.context, limit=4) - self.assertEqual(0, len(res)) - - def test_package_search_search_order(self): - pkg1 = api.package_upload( - self._stub_package( - fully_qualified_name=str(uuid.uuid4()), - name='mysql', - description='awcloud'), - self.tenant_id) - pkg2 = api.package_upload( - self._stub_package( - fully_qualified_name=str(uuid.uuid4()), - name='awcloud', - description='mysql'), - self.tenant_id) - api.package_upload( - self._stub_package( - tags=[], - fully_qualified_name=str(uuid.uuid4())), - self.tenant_id) - - res = api.package_search( - {'search': 'mysql'}, self.context) - self.assertEqual(2, len(res)) - self.assertEqual(pkg1.name, res[0].name) - self.assertEqual(pkg2.description, res[1].description) - - def test_package_search_search(self): - pkg1 = api.package_upload( - self._stub_package( - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - pkg2 = api.package_upload( - self._stub_package( - tags=[], - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - res = api.package_search( - {'search': 'tag1'}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'search': pkg1.fully_qualified_name}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'search': pkg2.fully_qualified_name}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'search': 'not_a_valid_uuid'}, self.context) - self.assertEqual(0, len(res)) - - res = api.package_search( - {'search': 'some text'}, self.context) - self.assertEqual(2, len(res)) - - def test_package_search_tags(self): - api.package_upload( - self._stub_package( - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - tags=[], - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - res = api.package_search( - {'tag': ['tag1']}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'tag': ['tag2']}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'tag': ['tag3']}, self.context) - self.assertEqual(0, len(res)) - - def test_package_search_type(self): - api.package_upload( - self._stub_package( - type="Application", - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - type="Library", - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - res = api.package_search( - {'type': 'Library'}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'type': 'Application'}, self.context) - self.assertEqual(1, len(res)) - - def test_package_search_disabled(self): - api.package_upload( - self._stub_package( - is_public=True, - enabled=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - is_public=True, - enabled=False, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - res = api.package_search( - {'include_disabled': 'false'}, self.context) - self.assertEqual(1, len(res)) - res = api.package_search( - {'include_disabled': 'true'}, self.context) - self.assertEqual(2, len(res)) - - def test_package_search_owned(self): - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id_2) - - res = api.package_search({'owned': 'true'}, self.context_admin) - self.assertEqual(1, len(res)) - res = api.package_search({'owned': 'false'}, self.context_admin) - self.assertEqual(2, len(res)) - - def test_package_search_no_filters_catalog(self): - res = api.package_search({}, self.context, catalog=True) - self.assertEqual(0, len(res)) - - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - is_public=False, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id_2) - api.package_upload( - self._stub_package( - is_public=False, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id_2) - - # catalog=True should show public + mine - res = api.package_search({}, self.context, catalog=True) - self.assertEqual(3, len(res)) - - res = api.package_search({}, self.context_admin, catalog=True) - self.assertEqual(3, len(res)) - - def test_package_search_no_filters(self): - res = api.package_search({}, self.context) - self.assertEqual(0, len(res)) - - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - api.package_upload( - self._stub_package( - is_public=False, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id) - - api.package_upload( - self._stub_package( - is_public=True, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id_2) - api.package_upload( - self._stub_package( - is_public=False, - fully_qualified_name=str(uuid.uuid4())), self.tenant_id_2) - - # I can only edit mine pkgs - res = api.package_search({}, self.context) - self.assertEqual(2, len(res)) - for pkg in res: - self.assertEqual(self.tenant_id, pkg.owner_id) - - # Admin can see everything - res = api.package_search({}, self.context_admin) - self.assertEqual(4, len(res)) - - def test_list_empty_categories(self): - res = api.category_get_names() - self.assertEqual(0, len(res)) - - def test_add_list_categories(self): - self._create_categories() - - res = api.categories_list() - self.assertEqual(2, len(res)) - - for cat in res: - self.assertIsNotNone(cat.id) - self.assertTrue(cat.name.startswith('cat')) - - def test_package_upload(self): - self._create_categories() - values = self._stub_package() - - package = api.package_upload(values, self.tenant_id) - - self.assertIsNotNone(package.id) - for k in values.keys(): - self.assertEqual(values[k], package[k]) - - def test_package_fqn_is_unique(self): - self._create_categories() - values = self._stub_package() - - api.package_upload(values, self.tenant_id) - self.assertRaises(db_exception.DBDuplicateEntry, - api.package_upload, values, self.tenant_id) - - def test_package_delete(self): - values = self._stub_package() - package = api.package_upload(values, self.tenant_id) - - api.package_delete(package.id, self.context) - - self.assertRaises(exc.HTTPNotFound, - api.package_get, package.id, self.context) - - def test_package_upload_to_different_tenants_with_same_fqn(self): - values = self._stub_package() - - api.package_upload(values, self.tenant_id) - api.package_upload(values, self.tenant_id_2) - - def test_package_upload_public_public_fqn_violation(self): - values = self._stub_package(is_public=True) - api.package_upload(values, self.tenant_id) - values = self._stub_package(is_public=True) - self.assertRaises(exc.HTTPConflict, api.package_upload, - values, self.tenant_id_2) - - def test_package_upload_public_private_no_fqn_violation(self): - values = self._stub_package(is_public=True) - api.package_upload(values, self.tenant_id) - values = self._stub_package(is_public=False) - api.package_upload(values, self.tenant_id_2) - - def test_package_upload_private_public_no_fqn_violation(self): - values = self._stub_package() - api.package_upload(values, self.tenant_id) - values = self._stub_package(is_public=True) - api.package_upload(values, self.tenant_id_2) - - def test_class_name_is_unique(self): - value = self._stub_package(class_definitions=('foo', 'bar')) - api.package_upload(value, self.tenant_id) - value = self._stub_package(class_definitions=('bar', 'baz'), - fully_qualified_name='com.example.package2') - self.assertRaises(exc.HTTPConflict, api.package_upload, value, - self.tenant_id) - - def test_class_name_uniqueness_not_enforced_in_different_tenants(self): - value = self._stub_package(class_definitions=('foo', 'bar')) - api.package_upload(value, self.tenant_id) - value = self._stub_package(class_definitions=('foo', 'bar'), - fully_qualified_name='com.example.package2') - api.package_upload(value, self.tenant_id_2) - - def test_class_name_public_public_violation(self): - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=True) - api.package_upload(value, self.tenant_id) - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=True, - fully_qualified_name='com.example.package2') - self.assertRaises(exc.HTTPConflict, api.package_upload, - value, self.tenant_id_2) - - def test_class_name_public_private_no_violation(self): - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=True) - api.package_upload(value, self.tenant_id) - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=False, - fully_qualified_name='com.example.package2') - api.package_upload(value, self.tenant_id_2) - - def test_class_name_private_public_no_violation(self): - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=False) - api.package_upload(value, self.tenant_id) - value = self._stub_package(class_definitions=('foo', 'bar'), - is_public=True, - fully_qualified_name='com.example.package2') - api.package_upload(value, self.tenant_id_2) - - def test_package_make_public(self): - id = api.package_upload(self._stub_package(), self.tenant_id).id - patch = self.get_change('replace', ['is_public'], True) - api.package_update(id, [patch], self.context) - package = api.package_get(id, self.context) - self.assertTrue(package.is_public) - - def test_package_update_public_public_fqn_violation(self): - id1 = api.package_upload(self._stub_package(), self.tenant_id).id - id2 = api.package_upload(self._stub_package(), self.tenant_id_2).id - patch = self.get_change('replace', ['is_public'], True) - api.package_update(id1, [patch], self.context) - self.assertRaises(exc.HTTPConflict, api.package_update, - id2, [patch], self.context_2) - - def test_package_update_public_public_class_name_violation(self): - id1 = api.package_upload(self._stub_package( - class_definitions=('foo', 'bar')), self.tenant_id).id - id2 = api.package_upload(self._stub_package( - class_definitions=('foo', 'bar'), - fully_qualified_name='com.example.package2'), self.tenant_id_2).id - patch = self.get_change('replace', ['is_public'], True) - api.package_update(id1, [patch], self.context) - self.assertRaises(exc.HTTPConflict, api.package_update, - id2, [patch], self.context_2) - - def test_category_paginate(self): - """Paginate through a list of categories using limit and marker""" - - category_names = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5'] - categories = [] - for name in category_names: - categories.append(api.category_add(name)) - uuids = [c.id for c in categories] - - page = api.categories_list(limit=2) - - self.assertEqual(category_names[:2], [c.name for c in page]) - - last = page[-1].id - page = api.categories_list(limit=3, marker=last) - self.assertEqual(category_names[2:5], [c.name for c in page]) - - page = api.categories_list(marker=uuids[-1]) - self.assertEqual([], page) - - category_names.reverse() - page = api.categories_list({'sort_dir': 'desc'}) - self.assertEqual(category_names, [c.name for c in page]) - - def test_category_get_delete_error(self): - category_id = 12 - self.assertRaises(exc.HTTPNotFound, api.category_get, category_id) - self.assertRaises(exc.HTTPNotFound, api.category_delete, category_id) - - def test_get_categories_error(self): - category_names = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5'] - cat_session = None - self.assertRaises(exc.HTTPBadRequest, api._get_categories, - category_names, cat_session) - - def test_authorize_package_delete_error(self): - values = self._stub_package() - package = api.package_upload(values, self.tenant_id) - self.assertRaises(exc.HTTPForbidden, api._authorize_package, package, - self.context_2) - self.assertRaises(exc.HTTPForbidden, - api.package_delete, package.id, self.context_2) - id = package.id - patch = self.get_change('replace', ['is_public'], False) - api.package_update(id, [patch], self.context) - self.assertRaises(exc.HTTPForbidden, api._authorize_package, package, - self.context_2, allow_public=True) diff --git a/murano/tests/unit/db/test_models.py b/murano/tests/unit/db/test_models.py deleted file mode 100644 index 4294128c4..000000000 --- a/murano/tests/unit/db/test_models.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 murano.db import models -from murano.db import session -from murano.tests.unit import base - - -class TestModels(base.MuranoWithDBTestCase): - def test_missing_blob(self): - """Fake a package with NULL supplier JSON blob to test bug 1342306.""" - con = session.get_session().connection() - con.execute("INSERT INTO package(id, fully_qualified_name, " - "owner_id, name, description, created, updated, type, " - "supplier) " - "VALUES (1, 'blob.test', 1, 'blob test', 'Desc', " - "'2014-07-15 00:00:00', '2014-07-15 00:00:00', " - "'Application', NULL)") - loaded_e = session.get_session().query(models.Package).get(1) - self.assertIsNone(loaded_e.supplier) diff --git a/murano/tests/unit/dsl/__init__.py b/murano/tests/unit/dsl/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/dsl/foundation/__init__.py b/murano/tests/unit/dsl/foundation/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/dsl/foundation/object_model.py b/murano/tests/unit/dsl/foundation/object_model.py deleted file mode 100644 index ebf7128cf..000000000 --- a/murano/tests/unit/dsl/foundation/object_model.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import helpers - - -class Object(object): - def __init__(self, __name, __id=None, class_version=None, **kwargs): - self.data = { - '?': { - 'type': __name, - 'id': __id or helpers.generate_id() - } - } - if class_version is not None: - self.data['?']['classVersion'] = class_version - self.data.update(kwargs) - - @property - def id(self): - return self.data['?']['id'] - - @property - def type_name(self): - return self.data['?']['type'] - - def __getitem__(self, item): - return self.data[item] - - def __setitem__(self, key, value): - self.data[key] = value - - def __contains__(self, item): - return item in self.data - - def __delitem__(self, key): - del self.data[key] - - -class Attribute(object): - def __init__(self, obj, key, value): - self._value = value - self._key = key - self._obj = obj - - @property - def obj(self): - return self._obj - - @property - def key(self): - return self._key - - @property - def value(self): - return self._value - - -class Ref(object): - def __init__(self, obj): - if isinstance(obj, str): - self._id = obj - else: - self._id = obj.id - - @property - def id(self): - return self._id - - -def build_model(root): - if isinstance(root, dict): - for key, value in root.items(): - root[key] = build_model(value) - elif isinstance(root, list): - for i in range(len(root)): - root[i] = build_model(root[i]) - elif isinstance(root, Object): - return build_model(root.data) - elif isinstance(root, Ref): - return root.id - elif isinstance(root, Attribute): - return [root.obj.id, root.obj.type_name, root.key, root.value] - return root diff --git a/murano/tests/unit/dsl/foundation/runner.py b/murano/tests/unit/dsl/foundation/runner.py deleted file mode 100644 index 0fa1bbe31..000000000 --- a/murano/tests/unit/dsl/foundation/runner.py +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 sys - -from murano.common import utils -from murano.dsl import context_manager -from murano.dsl import dsl -from murano.dsl import dsl_exception -from murano.dsl import dsl_types -from murano.dsl import executor -from murano.dsl import helpers -from murano.dsl import murano_object -from murano.dsl import serializer -from murano.dsl import yaql_integration -from murano.engine import execution_session -from murano.engine.system import yaql_functions -from murano.tests.unit.dsl.foundation import object_model - - -class TestContextManager(context_manager.ContextManager): - def __init__(self, functions): - self.__functions = functions - - def create_root_context(self, runtime_version): - root_context = super(TestContextManager, self).create_root_context( - runtime_version) - context = helpers.link_contexts( - root_context, yaql_functions.get_context(runtime_version)) - context = context.create_child_context() - for name, func in self.__functions.items(): - context.register_function(func, name) - return context - - -class Runner(object): - class DslObjectWrapper(object): - def __init__(self, obj, runner): - self._runner = runner - if isinstance(obj, (str,) + (dsl_types.MuranoType,)): - pass - elif isinstance(obj, (object_model.Object, object_model.Ref)): - obj = obj.id - elif isinstance(obj, murano_object.MuranoObject): - obj = obj.object_id - else: - raise ValueError( - 'obj must be object ID string, MuranoObject, MuranoType ' - 'or one of object_model helper classes (Object, Ref)') - if isinstance(obj, str): - self._receiver = runner.executor.object_store.get(obj) - else: - self._receiver = obj - - self._preserve_exception = False - - def __getattr__(self, item): - def call(*args, **kwargs): - return self._runner._execute( - item, self._receiver, *args, **kwargs) - if item.startswith('test'): - return call - - def __init__(self, model, package_loader, functions): - if isinstance(model, str): - model = object_model.Object(model) - model = object_model.build_model(model) - if 'Objects' not in model: - model = {'Objects': model} - - self.executor = executor.MuranoDslExecutor( - package_loader, TestContextManager(functions), - execution_session.ExecutionSession()) - self._root = self.executor.load(model) - if self._root: - self._root = self._root.object - if 'ObjectsCopy' in model: - self.executor.object_store.cleanup() - - def _execute(self, name, obj, *args, **kwargs): - try: - final_args = [] - final_kwargs = {} - for arg in args: - if isinstance(arg, object_model.Object): - arg = object_model.build_model(arg) - final_args.append(arg) - for name, arg in kwargs.items(): - if isinstance(arg, object_model.Object): - arg = object_model.build_model(arg) - final_kwargs[name] = arg - cls = obj if isinstance(obj, dsl_types.MuranoType) else obj.type - runtime_version = cls.package.runtime_version - yaql_engine = yaql_integration.choose_yaql_engine(runtime_version) - with helpers.with_object_store(self.executor.object_store): - return dsl.to_mutable(cls.invoke( - name, obj, tuple(final_args), final_kwargs), yaql_engine) - except dsl_exception.MuranoPlException as e: - if not self.preserve_exception: - original_exception = getattr(e, 'original_exception', None) - if original_exception and not isinstance( - original_exception, dsl_exception.MuranoPlException): - exc_traceback = getattr( - e, 'original_traceback', None) or sys.exc_info()[2] - utils.reraise( - type(original_exception), - original_exception, - exc_traceback) - raise - - def __getattr__(self, item): - if item.startswith('test'): - return getattr(Runner.DslObjectWrapper(self._root, self), item) - - def on(self, obj): - return Runner.DslObjectWrapper(obj, self) - - def on_class(self, class_name): - cls = self.executor.package_loader.load_class_package( - class_name, helpers.parse_version_spec(None)).find_class( - class_name, False) - return Runner.DslObjectWrapper(cls, self) - - @property - def root(self): - return self._root - - @property - def serialized_model(self): - return serializer.serialize_model(self._root, self.executor) - - @property - def preserve_exception(self): - return self._preserve_exception - - @preserve_exception.setter - def preserve_exception(self, value): - self._preserve_exception = value - - def session(self): - return helpers.with_object_store(self.executor.object_store) diff --git a/murano/tests/unit/dsl/foundation/test_case.py b/murano/tests/unit/dsl/foundation/test_case.py deleted file mode 100644 index f157521c6..000000000 --- a/murano/tests/unit/dsl/foundation/test_case.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 inspect -import os.path - -import eventlet.debug - -from murano.tests.unit import base -from murano.tests.unit.dsl.foundation import runner -from murano.tests.unit.dsl.foundation import test_package_loader - - -class DslTestCase(base.MuranoTestCase): - def setUp(self): - super(DslTestCase, self).setUp() - directory = os.path.join(os.path.dirname( - inspect.getfile(self.__class__)), 'meta') - root_meta_directory = os.path.join( - os.path.dirname(__file__), '../../../../../meta') - sys_package_loader = test_package_loader.TestPackageLoader( - os.path.join(root_meta_directory, 'io.murano/Classes'), - 'io.murano') - self._package_loader = test_package_loader.TestPackageLoader( - directory, 'tests', sys_package_loader) - self._functions = {} - self.register_function( - lambda data: self._traces.append(data), 'trace') - self._traces = [] - self._runners = [] - eventlet.debug.hub_exceptions(False) - - def new_runner(self, model): - r = runner.Runner(model, self.package_loader, self._functions) - self._runners.append(r) - return r - - def tearDown(self): - super(DslTestCase, self).tearDown() - for r in self._runners: - r.executor.finalize(r.root) - - @property - def traces(self): - return self._traces - - @traces.deleter - def traces(self): - self._traces = [] - - @property - def package_loader(self): - return self._package_loader - - def register_function(self, func, name): - self._functions[name] = func - - @staticmethod - def find_attribute(model, obj_id, obj_type, name): - for entry in model['Attributes']: - if tuple(entry[:3]) == (obj_id, obj_type, name): - return entry[3] diff --git a/murano/tests/unit/dsl/foundation/test_package_loader.py b/murano/tests/unit/dsl/foundation/test_package_loader.py deleted file mode 100644 index 6e094cfe5..000000000 --- a/murano/tests/unit/dsl/foundation/test_package_loader.py +++ /dev/null @@ -1,124 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 fnmatch -import os.path - - -from murano.dsl import constants -from murano.dsl import murano_package -from murano.dsl import namespace_resolver -from murano.dsl import package_loader -from murano.engine import yaql_yaml_loader -from murano.tests.unit.dsl.foundation import object_model - - -class TestPackage(murano_package.MuranoPackage): - def __init__(self, pkg_loader, name, version, - runtime_version, requirements, configs, meta): - self.__configs = configs - super(TestPackage, self).__init__( - pkg_loader, name, version, - runtime_version, requirements, meta) - - def get_class_config(self, name): - return self.__configs.get(name, {}) - - def get_resource(self, name): - pass - - -class TestPackageLoader(package_loader.MuranoPackageLoader): - _classes_cache = {} - - def __init__(self, directory, package_name, parent_loader=None, meta=None): - self._package_name = package_name - self._yaml_loader = yaql_yaml_loader.get_loader('1.0') - if directory in TestPackageLoader._classes_cache: - self._classes = TestPackageLoader._classes_cache[directory] - else: - self._classes = {} - self._build_index(directory) - TestPackageLoader._classes_cache[directory] = self._classes - self._parent = parent_loader - self._configs = {} - self._package = TestPackage( - self, package_name, None, constants.RUNTIME_VERSION_1_0, - None, self._configs, meta) - for name, payload in self._classes.items(): - self._package.register_class(payload, name) - super(TestPackageLoader, self).__init__() - - def load_package(self, package_name, version_spec): - if package_name == self._package_name: - return self._package - elif self._parent: - return self._parent.load_package(package_name, version_spec) - else: - raise KeyError(package_name) - - def load_class_package(self, class_name, version_spec): - if class_name in self._classes: - return self._package - elif self._parent: - return self._parent.load_class_package(class_name, version_spec) - else: - raise KeyError(class_name) - - def export_fixation_table(self): - return {} - - def import_fixation_table(self, fixations): - pass - - def compact_fixation_table(self): - pass - - def _build_index(self, directory): - yamls = [ - os.path.join(dirpath, f) - for dirpath, _, files in os.walk(directory) - for f in fnmatch.filter(files, '*.yaml') - if f != 'manifest.yaml' - ] - for class_def_file in yamls: - self._load_classes(class_def_file) - - def _load_classes(self, class_def_file): - with open(class_def_file, 'rb') as stream: - data_lst = self._yaml_loader(stream.read(), class_def_file) - - last_ns = {} - for data in data_lst: - last_ns = data.get('Namespaces', last_ns.copy()) - if 'Name' not in data: - continue - - for name, method in (data.get('Methods') or data.get( - 'Workflow') or {}).items(): - if name.startswith('test'): - method['Scope'] = 'Public' - - ns = namespace_resolver.NamespaceResolver(last_ns) - class_name = ns.resolve_name(data['Name']) - self._classes[class_name] = data_lst - - def set_config_value(self, class_name, property_name, value): - if isinstance(class_name, object_model.Object): - class_name = class_name.type_name - self._configs.setdefault(class_name, {})[ - property_name] = value - - def register_package(self, package): - super(TestPackageLoader, self).register_package(package) diff --git a/murano/tests/unit/dsl/meta/AgentListenerTests.yaml b/murano/tests/unit/dsl/meta/AgentListenerTests.yaml deleted file mode 100644 index 8411867aa..000000000 --- a/murano/tests/unit/dsl/meta/AgentListenerTests.yaml +++ /dev/null @@ -1,16 +0,0 @@ -Name: AgentListenerTests - -Namespaces: - sys: io.murano.system - -Properties: - agentListener: - Contract: $.class(sys:AgentListener) - Usage: Runtime - - -Methods: - testAgentListener: - Body: - - $.agentListener: new(sys:AgentListener, $this, name => 'hello') - - Return: $.agentListener diff --git a/murano/tests/unit/dsl/meta/CommonParent.yaml b/murano/tests/unit/dsl/meta/CommonParent.yaml deleted file mode 100644 index 2e1f4ee18..000000000 --- a/murano/tests/unit/dsl/meta/CommonParent.yaml +++ /dev/null @@ -1,31 +0,0 @@ -Name: CommonParent - -Properties: - rootProperty: - Contract: $.string() - - templateProperty: - Contract: $.template('io.murano.Object') - -Methods: - testRootMethod: - Body: - - trace('CommonParent::testRootMethod') - - trace($.rootProperty) - - setPrivatePropertyChain: - Body: - - $.privateName: 'CommonParent' - - trace($.privateName) - - virtualMethod: - Body: - - trace('CommonParent::virtualMethod') - ---- - -Name: TemplateTestParent - -Properties: - foo: - Contract: $.int().notNull() \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/ConcurrencyTest.yaml b/murano/tests/unit/dsl/meta/ConcurrencyTest.yaml deleted file mode 100644 index e92837673..000000000 --- a/murano/tests/unit/dsl/meta/ConcurrencyTest.yaml +++ /dev/null @@ -1,124 +0,0 @@ -# Licensed 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. - -Namespaces: - std: io.murano - m: io.murano.metadata.engine - -Name: TestConcurrency - -Methods: - isolated: - Arguments: - - call: - Contract: $.string() - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - isolatedWithDefault: - Meta: - - m:Synchronize: - Arguments: - - call: - Contract: $.string() - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - concurrentExplicit: - Meta: - - m:Synchronize: - onThis: false - Arguments: - - call: - Contract: $.string() - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - isolatedExplicit: - Meta: - - m:Synchronize: - onThis: true - Arguments: - - call: - Contract: $.string() - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - argbasedPrimitive: - Meta: - - m:Synchronize: - onArgs: flag - Arguments: - - call: - Contract: $.string() - - flag: - Contract: $.string() - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - argBasedWithObject: - Meta: - - m:Synchronize: - onArgs: flag - Arguments: - - call: - Contract: $.string() - - flag: - Contract: $.class(std:Object) - Body: - - trace(format('call-{0}-before', $call)) - - yield() - - trace(format('call-{0}-after', $call)) - - - testCallIsolated: - Body: - - list(1, 2, 3).pselect($this.isolated($)) - - testCallIsolatedWithDefault: - Body: - - list(1, 2, 3).pselect($this.isolatedWithDefault($)) - - testCallConcurrentExplicit: - Body: - - list(1, 2, 3).pselect($this.concurrentExplicit($)) - - testCallIsolatedExplicit: - Body: - - list(1, 2, 3).pselect($this.isolatedExplicit($)) - - testCallArgbasedPrimitiveIsolated: - Body: - - list(1, 2, 3).pselect($this.argbasedPrimitive($, same)) - - testCallArgbasedPrimitiveConcurrent: - Body: - - list(1, 2, 3).pselect($this.argbasedPrimitive($, $)) - - testCallArgbasedWithObjectIsolated: - Body: - - $o: new(std:Object) - - list(1, 2, 3).pselect($this.argBasedWithObject($, $o)) - - testCallArgbasedWithObjectConcurrent: - Body: - - list(1, 2, 3).pselect($this.argBasedWithObject($, new(std:Object))) diff --git a/murano/tests/unit/dsl/meta/ConfigProperties.yaml b/murano/tests/unit/dsl/meta/ConfigProperties.yaml deleted file mode 100644 index 81165d8bd..000000000 --- a/murano/tests/unit/dsl/meta/ConfigProperties.yaml +++ /dev/null @@ -1,17 +0,0 @@ -Name: ConfigProperties - -Properties: - cfgProperty: - Usage: Config - Contract: $.int().notNull() - Default: 123 - - normalProperty: - Contract: $.string().notNull() - Default: DEFAULT - -Methods: - testPropertyValues: - Body: - - trace($.cfgProperty) - - trace($.normalProperty) diff --git a/murano/tests/unit/dsl/meta/ContractExamples.yaml b/murano/tests/unit/dsl/meta/ContractExamples.yaml deleted file mode 100644 index 3a6811cd6..000000000 --- a/murano/tests/unit/dsl/meta/ContractExamples.yaml +++ /dev/null @@ -1,288 +0,0 @@ -Name: ContractExamples - -Extends: CommonParent - -Properties: - sampleClass: - Contract: $.class(SampleClass1) - - ordinaryProperty: - Contract: $.string() - - templateProperty: - Contract: $.template(TemplateTestChild, excludeProperties => [bar]) - -Methods: - testStringContract: - Arguments: - arg: - Contract: $.string() - Body: - Return: $arg - - testIntContract: - Arguments: - arg: - Contract: $.int() - Body: - Return: $arg - - testBoolContract: - Arguments: - arg: - Contract: $.bool() - Body: - Return: $arg - - testClassContract: - Arguments: - arg: - Contract: $.class(SampleClass2) - Body: - Return: $arg - - testTemplateContract: - Arguments: - arg: - Contract: $.template(CreatedClass2) - Body: - Return: $arg - - testTemplateContractExcludePropertyFromMpl: - Body: - - $model: - :CreatedClass2: - property1: qwerty - property2: 'not integer' - - Return: $.testTemplateContractExcludeProperty($model) - - testTemplateContractExcludeProperty: - Arguments: - arg: - Contract: $.template(CreatedClass2, excludeProperties => [property2]) - Body: - Return: $arg - - testClassFromIdContract: - Arguments: - arg: - Contract: $.class(SampleClass1) - Body: - Return: $arg - - testCheckContract: - Arguments: - - arg1: - Contract: $.class(SampleClass2).check($.class2Property = qwerty) - - arg2: - Contract: $.int().check($ > 10) - - testOwnedContract: - Arguments: - - arg1: - Contract: $.class(SampleClass1).owned() - - arg2: - Contract: $.class(SampleClass2).owned() - - testNotOwnedContract: - Arguments: - - arg1: - Contract: $.class(SampleClass1).notOwned() - - arg2: - Contract: $.class(SampleClass2).notOwned() - - testScalarContract: - Arguments: - - arg1: - Contract: 'fixed' - - arg2: - Contract: 456 - - arg3: - Contract: true - Body: - Return: $arg1 - - testListContract: - Arguments: - - arg: - Contract: [$.int()] - Body: - Return: $arg - - testListWithMinLengthContract: - Arguments: - - arg: - Contract: [$.int(), 3] - Body: - Return: $arg - - testListWithMinMaxLengthContract: - Arguments: - - arg: - Contract: [$.int(), 2, 4] - Body: - Return: $arg - - testDictContract: - Arguments: - - arg: - Contract: - A: $.string() - B: $.int() - Body: - Return: $arg - - testDictExprContract: - Arguments: - - arg: - Contract: - $.int(): $.string() - B: $.int() - Body: - Return: $arg - - testDictMultiExprContract: - Arguments: - - arg: - Contract: - $.int(): $.string() - $.string(): $.int() - Body: - Return: $arg - - testNotNullContract: - Arguments: - - arg: - Contract: $.notNull() - Body: - Return: $arg - - testDefault: - Arguments: - - arg: - Contract: $.string() - Default: DEFAULT - Body: - Return: $arg - - testDefaultExpression: - Arguments: - - arg: - Contract: $.string() - Default: $.ordinaryProperty - Body: - Return: $arg - - testActionMeta: - Scope: Public - Meta: - - io.murano.metadata.Title: - text: "Title of the method" - - io.murano.metadata.Description: - text: "Description of the method" - - io.murano.metadata.HelpText: - text: "HelpText of the method" - - notAction: - Scope: Session - - testAction: - Scope: Public - ---- - -Name: TestedTarget - -Properties: - prop: - Contract: - - $.string() -Methods: - foo: - Arguments: - - contracted: - Contract: - - $.string() - Body: - - Return: $contracted[2] - - wildList: - Arguments: - - contracted: - Contract: - - $ - Body: - - Return: $contracted[1][2] - - wildContract: - Arguments: - - untyped: - Contract: $ - Body: - - Return: $untyped[1] - - typedList: - Arguments: - - contracted: - Contract: - - [$.string()] - Body: - - Return: $contracted[1][2] - - dictArgs: - Arguments: - - arg: - Contract: {$.string(): [$.int()]} - Body: - - Return: $arg.get('a')[1] - ---- - -Name: TestIteratorsTransform -Methods: - testProperties: - Body: - - $.target: new(TestedTarget, $this, prop => ['1', '2', '3'].where($)) - - Return: $.target.prop[2] - testArgs: - Body: - - $.target: new(TestedTarget, $this) - - Return: $.target.foo(['1', '2', '3'].where($)) - testUntypedArgs: - Body: - - $.target: new(TestedTarget, $this) - - Return: $.target.wildContract(['1', '2', '3'].where($)) - testNotTypedListArgs: - Body: - - $.target: new(TestedTarget, $this) - - Return: $.target.wildList([['1', '2', '3'].where($), ['4', '5', '6'].where($)].where($)) - testTypedList: - Body: - - $.target: new(TestedTarget, $this) - - Return: $.target.typedList([['1', '2', '3'].where($), ['4', '5', '6'].where($)].where($)) - testListDict: - Body: - - $.target: new(TestedTarget, $this) - - Return: $.target.dictArgs({'a' => [1, 2, 4].where($)}) - ---- - -Name: TemplateTestChild - -Properties: - bar: - Contract: $.int().notNull() - ---- - -Name: TemplatePropertyClass -Properties: - owned: - Contract: $.class(Node) - template: - Contract: $.template(Node) - -Methods: - testTemplateWithExternallyOwnedObject: - Body: - - Return: new($.template).nodes.select(id($)) diff --git a/murano/tests/unit/dsl/meta/CreatedClass1.yaml b/murano/tests/unit/dsl/meta/CreatedClass1.yaml deleted file mode 100644 index 2e0dfd226..000000000 --- a/murano/tests/unit/dsl/meta/CreatedClass1.yaml +++ /dev/null @@ -1,33 +0,0 @@ -Name: CreatedClass1 - -Properties: - property1: - Contract: $.string() - - property2: - Contract: $.int() - - xxx: - Contract: $ - Usage: Out - - -Methods: - .init: - Arguments: - - property1: - Contract: $.string() - Body: - - trace('CreatedClass1::.init') - - trace($property1) - - $.property1: STRING - - trace($.property1) - - trace($.property2) - - createClass2: - Arguments: - - parent: - Contract: $.class(CreatingClass) - Body: - - $.xxx: new(CreatedClass2, $parent, QQQ, property1 => STR, property2 => 99) - - Return: $ \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/CreatedClass2.yaml b/murano/tests/unit/dsl/meta/CreatedClass2.yaml deleted file mode 100644 index 01178bdb1..000000000 --- a/murano/tests/unit/dsl/meta/CreatedClass2.yaml +++ /dev/null @@ -1,20 +0,0 @@ -Name: CreatedClass2 - -Properties: - property1: - Contract: $.string() - - property2: - Contract: $.int() - - - - -Methods: - - .init: - Body: - - $.find(CreatingClass).require() - - trace('CreatedClass2::.init') - - diff --git a/murano/tests/unit/dsl/meta/CreatingClass.yaml b/murano/tests/unit/dsl/meta/CreatingClass.yaml deleted file mode 100644 index cbf642387..000000000 --- a/murano/tests/unit/dsl/meta/CreatingClass.yaml +++ /dev/null @@ -1,142 +0,0 @@ -Namespaces: - std: io.murano - -Name: CreatingClass - -Properties: - yyy: - Contract: $ - Usage: Out - - -Methods: - .init: - Body: - trace('CreatingClass::.init') - - testNew: - Body: - - new(CreatedClass1, property1 => string, property2 => 123) - - testNewWithOwnership: - Body: - - $.yyy: new(CreatedClass1, property1 => string, property2 => 123) - - Return: $.yyy.createClass2($this) - - testNewWithDict: - Body: - - $dict: - property1: string - property2: 123 - - Return: new(CreatedClass1, $dict, owner=>$this) - - testLoadCompexModel: - Body: - - $model: - :Node: - value: rootNode - nodes: - - :Node: - value: childNode1 - nodes: [node0, node2] - id: node1 - - '?': - id: node2 - type: Node - value: childNode2 - nodes: [node1, node2] - - node2 - id: node0 - - $obj: new($model, $this) - - Return: - - id($obj) - - id($obj.nodes[0]) - - id($obj.nodes[1]) - - id($obj.find(std:Object)) - - $obj.value - - $obj.nodes.select($.value) - - $obj.nodes[0].nodes[0] = $obj - - $obj.nodes[0].nodes[1] = $obj.nodes[1] - - $obj.nodes[2] = $obj.nodes[1] - - $obj.nodes[1].nodes[0] = $obj.nodes[0] - - $obj.nodes[1].nodes[1] = $obj.nodes[1] - - $obj.nodes[0].nodes[0].value - - $obj.nodes[0].nodes[1].value - - $obj.nodes[1].nodes[0].value - - testSingleContractInstantiation: - Body: - - $model: - :ConstructionSample: - - new(:ConstructionChild, prop => $model) - - testNestedNewLoadsInSeparateStore: - Body: - Return: new(:ConstructionFromInit).out.nodes[1] - - testReferenceAccessFromInit: - Body: - - $model: - :Node: - value: rootNode - nodes: - - childNode - - :NodeWithReferenceAccess: - value: childNode - id: childNode - - $.out: new($model, $this) - ---- - -Name: ConstructionSample -Methods: - .init: - Body: trace('ConstructionSample::init') - ---- - -Name: ConstructionParent -Properties: - prop: - Contract: $.class(ConstructionSample) - ---- - -Name: ConstructionChild -Extends: ConstructionParent -Properties: - prop: - Contract: $.class(ConstructionSample) - ---- - -Name: ConstructionFromInit -Properties: - out: - Contract: $.class(Node) - Usage: Out -Methods: - .init: - Body: - - $model: - :Node: - value: rootNode - nodes: - - :Node: - value: childNode1 - nodes: [childNode2] - id: childNode1 - - :Node: - value: childNode2 - nodes: [childNode1] - id: childNode2 - - $.out: new($model, $this) - ---- - -Name: NodeWithReferenceAccess -Extends: Node - -Methods: - .init: - Body: $.find(Node).nodes.select(trace($.value)) \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/DerivedFrom2Classes.yaml b/murano/tests/unit/dsl/meta/DerivedFrom2Classes.yaml deleted file mode 100644 index 97cd45345..000000000 --- a/murano/tests/unit/dsl/meta/DerivedFrom2Classes.yaml +++ /dev/null @@ -1,106 +0,0 @@ -Name: DerivedFrom2Classes - -Extends: [ParentClass1, ParentClass2] - -Properties: - ambiguousProperty: - Contract: $.string() - Usage: InOut - - usageTestProperty1: - Contract: $.int() - usageTestProperty2: - Contract: $.int() - Usage: In - usageTestProperty3: - Contract: $.int() - Usage: InOut - usageTestProperty4: - Contract: $.int() - Usage: Out - usageTestProperty5: - Contract: $.int() - Usage: Runtime - usageTestProperty6: - Contract: $.int() - Usage: Const - usageTestProperty7: - Contract: $.int() - Usage: Config - - -Methods: - setPrivateProperty: - Body: - - $.privateProperty: 99 - - testAccessAmbiguousPropertyWithResolver: - Body: - Return: $.ambiguousProperty - - testPropertyMerge: - Body: - - trace($.ambiguousProperty) - - $.setAmbiguousProperty() - - trace($.ambiguousProperty) - - trace($.getAmbiguousProperty()) - - trace($.cast(ParentClass1).ambiguousProperty) - - trace($.cast(ParentClass2).ambiguousProperty) - - Return: $.ambiguousProperty - - testModifyUsageTestProperty1: - Body: - - $.usageTestProperty1: 11 - - Return: $.usageTestProperty1 - - testModifyUsageTestProperty2: - Body: - - $.usageTestProperty2: 22 - - Return: $.usageTestProperty2 - - testModifyUsageTestProperty3: - Body: - - $.usageTestProperty3: 33 - - Return: $.usageTestProperty3 - - testModifyUsageTestProperty4: - Body: - - $.usageTestProperty4: 44 - - Return: $.usageTestProperty4 - - testModifyUsageTestProperty5: - Body: - - $.usageTestProperty5: 55 - - Return: $.usageTestProperty5 - - testModifyUsageTestProperty6: - Body: - - $.usageTestProperty6: 66 - - Return: $.usageTestProperty6 - - testModifyUsageTestProperty7: - Body: - - $.usageTestProperty7: 77 - - Return: $.usageTestProperty7 - - - testMixinOverride: - Body: - - $.virtualMethod() - - trace('-') - - cast($, CommonParent).virtualMethod() - - trace('-') - - $.cast(ParentClass1).virtualMethod() - - trace('-') - - $.cast(ParentClass2).virtualMethod() - - testSuper: - Body: - - super($, $.virtualMethod()) - - $.super($.virtualMethod()) - - testPsuper: - Body: - - psuper($, $.virtualMethod()) - - $.psuper($.virtualMethod()) - diff --git a/murano/tests/unit/dsl/meta/Empty.yaml b/murano/tests/unit/dsl/meta/Empty.yaml deleted file mode 100644 index eb556a969..000000000 --- a/murano/tests/unit/dsl/meta/Empty.yaml +++ /dev/null @@ -1 +0,0 @@ -Name: Empty diff --git a/murano/tests/unit/dsl/meta/ExceptionHandling.yaml b/murano/tests/unit/dsl/meta/ExceptionHandling.yaml deleted file mode 100644 index 46b43c484..000000000 --- a/murano/tests/unit/dsl/meta/ExceptionHandling.yaml +++ /dev/null @@ -1,55 +0,0 @@ -Name: ExceptionHandling - -Methods: - testThrow: - Arguments: - - enum: - Contract: $.int().notNull() - Body: - Try: - - trace('enter try') - - $.doThrow($enum) - - trace('exit try') - Catch: - - With: exceptionName - As: e - Do: - - trace($e.message) - - With: anotherExceptionName - As: e - Do: - - trace($e.message) - - trace(rethrow) - - Rethrow: - - As: e - Do: - - trace('catch all') - - trace($e.message) - Else: - - trace('else section') - Finally: - - trace('finally section') - - doThrow: - Arguments: - - enum: - Contract: $.int().notNull() - Body: - - Match: - 1: - - Throw: exceptionName - Message: exception message - 2: - - Throw: anotherExceptionName - Message: exception message 2 - 3: - - Throw: thirdExceptionName - Message: exception message 3 - 4: - - Return: - Value: $enum - - testStackTrace: - Body: - raisePythonException() - diff --git a/murano/tests/unit/dsl/meta/MacroExamples.yaml b/murano/tests/unit/dsl/meta/MacroExamples.yaml deleted file mode 100644 index 4b39bdc1b..000000000 --- a/murano/tests/unit/dsl/meta/MacroExamples.yaml +++ /dev/null @@ -1,254 +0,0 @@ -Name: MacroExamples - -Methods: - testIf: - Arguments: - arg: - Contract: $.int() - Body: - - If: $arg > 5 - Then: - Return: gt - - Return: def - - testIfElse: - Arguments: - arg: - Contract: $.int() - Body: - - If: $arg > 5 - Then: - Return: gt - Else: - Return: lt - - Return: def - - testIfNonBoolean: - Body: - - $res: 0 - - If: null - Then: - $res: $res + 1 - - If: list() - Then: - $res: $res + 10 - - If: qwerty - Then: - $res: $res + 100 - - If: 123 - Then: - $res: $res + 1000 - - Return: $res - - testWhile: - Arguments: - arg: - Contract: $.int() - Body: - - While: $arg > 0 - Do: - - trace($arg) - - $arg: $arg - 1 - - Return: $arg - - testWhileNonBoolean: - Body: - - $lst: list(1, 2, 3) - - While: $lst - Do: - - $lst: $lst.delete(0) - - Return: $lst - - testFor: - Body: - - For: t - In: [x, y, z] - Do: - - trace($t) - - $col: [1, 2, 3] - - For: t - In: $col.select($ * $) - Do: - - trace($t + 1) - - testRepeat: - Arguments: - count: - Contract: $.int() - Body: - - Repeat: $count - Do: - - trace(run) - - testBreak: - Body: - - For: t - In: range(0, 7) - Do: - - If: $t = 3 - Then: - - trace(breaking) - - Break: - - trace($t) - - trace(method_break) - - Break: - - testContinue: - Body: - - For: t - In: range(0, 7) - Do: - - If: $t >= 3 and $t < 5 - Then: - - Continue: - - trace($t) - - trace(method_continue) - - Continue: - - testMatch: - Arguments: - arg: - Contract: $.int() - Body: - - Match: - 2: - Return: x - 1: - Return: y - 3: - Return: z - Value: $arg - - testMatchDefault: - Arguments: - arg: - Contract: $.int() - Body: - - Match: - 2: - Return: x - 1: - - Return: y - 3: - Return: z - Default: - - Return: def - Value: $arg - - testSwitch: - Arguments: - arg: - Contract: $.int() - Body: - - Switch: - $arg > 10: - trace(gt) - $arg < 10: - trace(lt) - $arg > 100: - trace(gt100) - - testSwitchNonBoolean: - Body: - - $res: 0 - - Switch: - 0: - $res: $res + 1 - null: - $res: $res + 10 - list(): - $res: $res + 100 - '': - $res: $res + 1000 - qwerty: - $res: $res + 10000 - list(1): - $res: $res + 100000 - 12: - $res: $res + 1000000 - - Return: $res - - testSwitchDefault: - Arguments: - arg: - Contract: $.int() - Body: - - Switch: - $arg > 10: - trace(gt) - $arg < 0: - - trace(lt) - $arg > 100: - trace(gt100) - Default: - - trace(def) - - testCodeBlock: - Body: - - Do: - - trace(a) - - $res: 123 - - trace($res) - - Return: $res - - testParallel: - Body: - Parallel: - - Do: - - trace(enter) - - sleep(0) - - trace(exit) - - Do: - - trace(enter) - - sleep(0) - - trace(exit) - - testParallelWithLimit: - Body: - Parallel: - - Do: - - trace(enter) - - sleep(0) - - trace(exit) - - Do: - - trace(enter) - - sleep(0) - - trace(exit) - - Do: - - trace(enter) - - sleep(0) - - trace(exit) - Limit: 2 - - testScopeWithinMacro: - Body: - - $x: 0 - - $c: 1 - - If: $x = 0 - Then: - $x: $x + 1 - - While: $x = 1 - Do: - $x: $x + 20 - - For: t - In: [1] - Do: - $x: $x + 300 - - Repeat: 1 - Do: - $x: $x + 4000 - - Match: - 1: - $x: $x + 50000 - Value: $c - - Switch: - $c > 0: - $x: $x + 600000 - - Do: - $x: $x + 7000000 - - Parallel: - - Do: - $x: $x + 80000000/2 - - Do: - $x: $x + 80000000/2 - - Return: $x \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/Node.yaml b/murano/tests/unit/dsl/meta/Node.yaml deleted file mode 100644 index 2d4b2d650..000000000 --- a/murano/tests/unit/dsl/meta/Node.yaml +++ /dev/null @@ -1,8 +0,0 @@ -Name: Node - -Properties: - nodes: - Contract: - - $.class(Node) - value: - Contract: $.string() diff --git a/murano/tests/unit/dsl/meta/ParentClass1.yaml b/murano/tests/unit/dsl/meta/ParentClass1.yaml deleted file mode 100644 index a3db86161..000000000 --- a/murano/tests/unit/dsl/meta/ParentClass1.yaml +++ /dev/null @@ -1,24 +0,0 @@ -Name: ParentClass1 - -Extends: CommonParent - -Properties: - ambiguousProperty: - Contract: $.string() - Usage: InOut - -Methods: - method1: - Body: - - trace('ParentClass1::method1') - - setPrivatePropertyChain: - Body: - - $.privateName: 'ParentClass1' - - $.cast(CommonParent).setPrivatePropertyChain() - - trace($.privateName) - - - setAmbiguousProperty: - Body: - $.ambiguousProperty: '555' diff --git a/murano/tests/unit/dsl/meta/ParentClass2.yaml b/murano/tests/unit/dsl/meta/ParentClass2.yaml deleted file mode 100644 index d1ee5c060..000000000 --- a/murano/tests/unit/dsl/meta/ParentClass2.yaml +++ /dev/null @@ -1,21 +0,0 @@ -Name: ParentClass2 - -Extends: CommonParent - -Properties: - ambiguousProperty: - Contract: $.string() - Usage: InOut - -Methods: - method2: - Body: - - trace('ParentClass2::method2') - - getAmbiguousProperty: - Body: - Return: $.ambiguousProperty - - virtualMethod: - Body: - - trace('ParentClass2::virtualMethod') diff --git a/murano/tests/unit/dsl/meta/PropertyInit.yaml b/murano/tests/unit/dsl/meta/PropertyInit.yaml deleted file mode 100644 index 02131c551..000000000 --- a/murano/tests/unit/dsl/meta/PropertyInit.yaml +++ /dev/null @@ -1,43 +0,0 @@ -Name: PropertyInit - -Properties: - runtimePropertyWithoutDefault: - Usage: Runtime - Contract: $.string() - - runtimePropertyWithStrictContractWithoutDefault: - Usage: Runtime - Contract: $.string().notNull() - - runtimeProperty2WithStrictContractWithoutDefault: - Usage: Runtime - Contract: $.string().notNull() - - agentListener: - Contract: $.class('io.murano.system.AgentListener') - - runtimePropertyWithStrictContractAndDefault: - Usage: Runtime - Contract: $.string().notNull() - Default: DEFAULT - -Methods: - initialize: - Body: - $.runtimePropertyWithStrictContractWithoutDefault: VALUE - - testRuntimePropertyWithoutDefault: - Body: - - Return: $this.runtimePropertyWithoutDefault - - testRuntimePropertyDefault: - Body: - - Return: $this.runtimePropertyWithStrictContractAndDefault - - testRuntimePropertyWithStrictContractWithoutDefault: - Body: - - Return: $this.runtimePropertyWithStrictContractWithoutDefault - - testUninitializedRuntimeProperty: - Body: - - Return: $this.runtimeProperty2WithStrictContractWithoutDefault diff --git a/murano/tests/unit/dsl/meta/SampleClass1.yaml b/murano/tests/unit/dsl/meta/SampleClass1.yaml deleted file mode 100644 index f8ac583ba..000000000 --- a/murano/tests/unit/dsl/meta/SampleClass1.yaml +++ /dev/null @@ -1,94 +0,0 @@ -Name: SampleClass1 - -Properties: - stringProperty: - Contract: $.string().notNull() - classProperty: - Contract: $.class(SampleClass2).notNull() - assignedProperty: - Contract: $ - Usage: Runtime - arbitraryProperty: - Contract: $ - -Workflow: - testTrace: - Arguments: - - intArg: - Contract: $.int().notNull() - Body: - - trace($intArg) - - trace($.stringProperty) - - trace($.classProperty.class2Property) - - testException: - Body: - - raiseException() - - testReturn: - Arguments: - - intArg: - Contract: $.int().notNull() - Body: - Return: $intArg - - testCallAnotherMethod: - Body: - - trace(method1) - - $.anotherMethod() - - anotherMethod: - Body: - - trace(method2) - - testAttributes: - Arguments: - - arg: - Contract: $.string() - Body: - - $.setAttr(att1, $arg) - - $x: $.getAttr(att1) - - $y: $.getAttr(att2, ' Doe') - - Return: $x + $y - - testAssignment: - Body: - - $result: {} - - $result.Arr: [1, 2, [10, 11]] - - $index: 1 - - $result.Arr[0]: 3 - - $result.Arr[$index - 1]: 5 - - $result.Arr[$index + 1][1]: 123 - #- $result.Dict: {} - - $result.Dict.Key1: V1 - - $keyName: Key2 - - $result.Dict[$keyName]: {} - - $result.Dict[$keyName]['a_b']: V2 - - $result.Dict[$keyName][toUpper($keyName)]: V3 - - Return: $result - - testAssignmentOnProperty: - Body: - #- $.assignedProperty: {} - - $.assignedProperty.Arr: [1, 2, [10, 11]] - - $index: 1 - - $.assignedProperty.Arr[0]: 3 - - $.assignedProperty.Arr[$index - 1]: 5 - - $.assignedProperty.Arr[$index + 1][1]: 123 - #- $.assignedProperty.Dict: {} - - $.assignedProperty.Dict.Key1: V1 - - $keyName: Key2 - - $.assignedProperty.Dict[$keyName]: {} - - $.assignedProperty.Dict[$keyName]['a_b']: V2 - - $.assignedProperty.Dict[$keyName][toUpper($keyName)]: V3 - - Return: $.assignedProperty - - - testAssignByCopy: - Arguments: - - arg: - Contract: [$.int()] - Body: - - $x: $arg - - $x[0]: 321 - - Return: $arg diff --git a/murano/tests/unit/dsl/meta/SampleClass2.yaml b/murano/tests/unit/dsl/meta/SampleClass2.yaml deleted file mode 100644 index cee148cd8..000000000 --- a/murano/tests/unit/dsl/meta/SampleClass2.yaml +++ /dev/null @@ -1,18 +0,0 @@ -Name: SampleClass2 - -Properties: - class2Property: - Contract: $.string().notNull() - -Methods: - testMethod: - Body: - Return: - key1: abc - key2: [a, b, c] - key3: null - key4: false - key5: - x: y - key6: - - w: q diff --git a/murano/tests/unit/dsl/meta/SampleClass3.yaml b/murano/tests/unit/dsl/meta/SampleClass3.yaml deleted file mode 100644 index d151ba0db..000000000 --- a/murano/tests/unit/dsl/meta/SampleClass3.yaml +++ /dev/null @@ -1,35 +0,0 @@ -Name: SampleClass3 - -Properties: - multiClassProperty: - Contract: $.class(ParentClass1).class(ParentClass2) - -Methods: - testMultiContract: - Body: - - $.multiClassProperty.method1() - - $.multiClassProperty.method2() - - testPropertyAccessibleOnSeveralPaths: - Body: - Return: $.multiClassProperty.rootProperty - - testPrivateProperty: - Body: - - $.privateName: 'SampleClass3' - - $.multiClassProperty.setPrivatePropertyChain() - - trace($.privateName) - - testUninitializedPrivatePropertyAccess: - Body: - Return: $.privateName - - testReadOfPrivatePropertyOfOtherClass: - Body: - - $.multiClassProperty.setPrivateProperty() - - trace('accessing property') - - Return: $.multiClassProperty.privateProperty - - testWriteOfPrivatePropertyOfOtherClass: - Body: - $.multiClassProperty.privateProperty: 123 diff --git a/murano/tests/unit/dsl/meta/SingleInheritanceChild.yaml b/murano/tests/unit/dsl/meta/SingleInheritanceChild.yaml deleted file mode 100644 index 4d13cdc2a..000000000 --- a/murano/tests/unit/dsl/meta/SingleInheritanceChild.yaml +++ /dev/null @@ -1,18 +0,0 @@ -Name: SingleInheritanceChild - -Extends: SingleInheritanceParent - -Methods: - testVirtualCalls: - Body: - $.method1() - - method1: - Body: - - trace('SingleInheritanceChild::method1') - - super($, $.method1()) - - method2: - Body: - - trace('SingleInheritanceChild::method2') - - $.super($.method2()) \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/SingleInheritanceParent.yaml b/murano/tests/unit/dsl/meta/SingleInheritanceParent.yaml deleted file mode 100644 index 1e91de5a4..000000000 --- a/murano/tests/unit/dsl/meta/SingleInheritanceParent.yaml +++ /dev/null @@ -1,11 +0,0 @@ -Name: SingleInheritanceParent - -Methods: - method1: - Body: - - trace('SingleInheritanceParent::method1') - - $.method2() - - method2: - Body: - trace('SingleInheritanceParent::method2') \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/TestCall.yaml b/murano/tests/unit/dsl/meta/TestCall.yaml deleted file mode 100644 index 40f80fc14..000000000 --- a/murano/tests/unit/dsl/meta/TestCall.yaml +++ /dev/null @@ -1,45 +0,0 @@ -Name: OtherClass - -Methods: - toCall: - Arguments: - - source: - Contract: $.string() - - foo: - Contract: $ - - bar: - Contract: $ - - baz: - Contract: $ - Body: - - trace('called as ' + $source) - - staticMethod: - Usage: Static - Body: - - trace('called as static') ---- # ------------------------------------------------------------------------ - -Name: TestCall - -Methods: - .init: - Body: - - $.other: new(OtherClass) - - testCall: - Body: - - call('toCall', ['call', 'foo'], {baz=>1, bar=>2}, $.other) - - testMethodInvocation: - Body: - - $.other.toCall('method', 'foo', baz=>baz, bar=>bar) - - testCallStatic: - Body: - - call('staticMethod', [], {}, :OtherClass) - - testCallStaticAsInstance: - Body: - - call('staticMethod', [], {}, $.other) ---- # ------------------------------------------------------------------------ diff --git a/murano/tests/unit/dsl/meta/TestDump.yaml b/murano/tests/unit/dsl/meta/TestDump.yaml deleted file mode 100644 index 3dd85bf2f..000000000 --- a/murano/tests/unit/dsl/meta/TestDump.yaml +++ /dev/null @@ -1,107 +0,0 @@ -# Licensed 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. - - -Namespaces: - =: dumptests - std: io.murano - m: io.murano.metadata.engine ---- # ------------------------------------------------------------------ # --- - -Name: DumpTarget1 - -Properties: - foo: - Contract: $.string() - - bar: - Contract: - - $.int() - - baz: - Contract: - $.string(): $.int() - - -Methods: - getOwner: - Body: - - Return: $.find(DumpTarget2).require() - ---- # ------------------------------------------------------------------ # --- - -Name: DumpTarget2 - -Properties: - nested: - Usage: InOut - Contract: $.class(std:Object) - - another: - Contract: $.class(DumpTarget1) - - ref: - Usage: InOut - Contract: $.class(std:Object) - ---- # ------------------------------------------------------------------ # --- - -Name: DumpTarget3 - -Properties: - a: - Meta: - - m:Serialize: - as: copy - Contract: $.class(DumpTarget1) - b: - Meta: - - m:Serialize: - as: reference - Contract: $.class(DumpTarget1) - ---- # ------------------------------------------------------------------ # --- -Name: DumpTarget4 -Extends: DumpTarget1 -Properties: - qux: - Contract: $.string().notNull() - ---- # ------------------------------------------------------------------ # --- - - -Name: TestDump - -Methods: - testDump: - Arguments: - - object: - Contract: $.class(std:Object).notNull() - - serializationType: - Contract: $.string().check($ in [Serializable, Mixed, Inline]) - Default: 'Inline' - Body: - - Return: dump($object, $serializationType, true) - - testDumpWithUpcast: - Arguments: - - object: - Contract: $.class(std:Object).notNull() - - doUpcast: - Contract: $.bool().notNull() - - passIgnoreUpcast: - Contract: $.bool().notNull() - Body: - - If: $doUpcast - Then: - - $object: $object.cast(DumpTarget1) - - Return: dump($object, Inline, $passIgnoreUpcast) diff --git a/murano/tests/unit/dsl/meta/TestEngineFunctions.yaml b/murano/tests/unit/dsl/meta/TestEngineFunctions.yaml deleted file mode 100644 index d0c369b7b..000000000 --- a/murano/tests/unit/dsl/meta/TestEngineFunctions.yaml +++ /dev/null @@ -1,327 +0,0 @@ -Namespaces: - sys: io.murano.system - std: io.murano - -Name: TestEngineFunctions - -Properties: - target: - Usage: Runtime - Contract: $.class(std:Object) - - -Methods: - testJoin: - Body: - - $arr: [xx, 123] - - Return: (' '.join($arr)) - - testSplit: - Body: - - Return: ('x yy 123').split(' ') - - testLen: - Body: - - $a: str - - $b: [1, 2, 3, 4] - - $c: {'a': 'xxx' } - - Return: len($a) + len($b) + len($c) - - testCoalesce: - Arguments: - - arg1: - Contract: $.string() - - arg2: - Contract: $.string() - - arg3: - Contract: $.string() - Body: - Return: coalesce($arg1, $arg2, $arg3) - - testBase64Encode: - Arguments: - - arg: - Contract: $.string() - Body: - Return: base64encode($arg) - - testBase64Decode: - Arguments: - - arg: - Contract: $.string() - Body: - Return: base64decode($arg) - - testFormat: - Arguments: - - format: - Contract: $.string() - - arg1: - Contract: $.string() - - arg2: - Contract: $.string() - Body: - Return: $format.format($arg1, $arg2) - - testReplaceStr: - Arguments: - - what: - Contract: $.string() - - old: - Contract: $.string() - - new: - Contract: $.string() - Body: - Return: $what.replace($old, $new) - - testReplaceDict: - Arguments: - - what: - Contract: $.string() - - with: - Contract: - $.string(): $.string() - Body: - Return: $what.replace($with) - - testToLower: - Arguments: - - arg: - Contract: $.string() - Body: - Return: toLower($arg) - - testToUpper: - Arguments: - - arg: - Contract: $.string() - Body: - Return: toUpper($arg) - - testStartsWith: - Arguments: - - what: - Contract: $.string() - - arg: - Contract: $.string() - Body: - Return: $what.startsWith($arg) - - testEndsWith: - Arguments: - - what: - Contract: $.string() - - arg: - Contract: $.string() - Body: - Return: $what.endsWith($arg) - - testTrim: - Arguments: - - arg: - Contract: $.string() - Body: - Return: trim($arg) - - testSubstr: - Arguments: - - str: - Contract: $.string() - - arg1: - Contract: $.int() - - arg2: - Contract: $.int() - Body: - Return: $str.substr(0, $arg1) + - $str.substr($arg1, $arg2) + - $str.substr($arg1 + $arg2) - - testStr: - Arguments: - - arg: - Contract: $ - Body: - Return: str($arg) - - testInt: - Arguments: - - arg: - Contract: $.string() - Body: - Return: int($arg) - - testKeys: - Arguments: - - arg: - Contract: {} - Body: - Return: $arg.keys() - - testValues: - Arguments: - - arg: - Contract: {} - Body: - Return: $arg.values() - - testFlatten: - Arguments: - - arg: - Contract: [] - Body: - Return: $arg.flatten() - - testDictGet: - Arguments: - - dict: - Contract: - $.string(): $ - - key: - Contract: $.string().notNull() - Body: - Return: $dict.get($key) - - testRandomName: - Body: - Return: randomName() - - testPSelect: - Arguments: - - arg: - Contract: [$.int().notNull()] - Body: - Return: $arg.pselect($ * $) - - testBind: - Arguments: - - template: - Contract: {} - - args: - Contract: {} - Body: - Return: $template.bind($args) - - testPatch: - Body: - - $patches: - - op: add - path: '/foo' - value: bar - - op: add - path: '/baz' - value: [1, 2, 3] - - op: remove - path: '/baz/1' - - op: test - path: '/baz' - value: [1, 3] - - op: replace - path: '/baz/0' - value: 42 - - op: remove - path: '/baz/1' - - $doc: {} - - Return: $doc.patch($patches) - - testTake: - Arguments: - - list: - Contract: [$.int()] - - count: - Contract: $.int() - Body: - - Return: $list.take($count) - - testSkip: - Arguments: - - list: - Contract: [$.int()] - - count: - Contract: $.int() - Body: - - Return: $list.skip($count) - - testSkipTake: - Arguments: - - list: - Contract: [$.int()] - - start: - Contract: $.int() - - count: - Contract: $.int() - Body: - - $l: $list.skip($start) - - Return: $l.take($count) - - testSkipTakeChained: - Arguments: - - list: - Contract: [$.int()] - - start: - Contract: $.int() - - count: - Contract: $.int() - Body: - - Return: $list.skip($start).take($count) - - - testAggregate: - Arguments: - - list: - Contract: [$.int()] - Body: - - Return: $list.aggregate($1 + $2) - - testAggregateWithInitializer: - Arguments: - - list: - Contract: [$.int()] - - initializer: - Contract: $.int() - Body: - - Return: $list.aggregate($1 + $2, $initializer) - - testId: - Body: - Return: id($) + $.id() - - testType: - Body: - Return: type($) + $.type() - - testIsOperator: - Body: - - $logger: new(sys:Logger) - - $.innerVariable: new(sys:Logger) - - $derivedClassObject: new(DerivedFrom2Classes) - - Return: $logger is sys:Logger - and $ is 'TestEngineFunctions' - and $.innerVariable is 'io.murano.system.Logger' - and $derivedClassObject is ParentClass1 - and $derivedClassObject is ParentClass2 - - testNegativeIsOperator: - Arguments: - - nullArg: - Contract: $.class(sys:Logger) - Default: null - Body: - - $nullVariable: null - - Return: $nullVariable is sys:Logger - or $nullArg is sys:Logger - - testNewObjectAssignment: - Body: - - $newObject: new(std:Object) - # Assignment of object-contracted properties by IDs is possible only - # if the object with the given id is present in the object store. - # Thus this assignment tests the fix for bug #1597452 - - $this.target: id($newObject) - - Return: $this.target = $newObject - - testDecryptData: - Arguments: - - value: - Contract: $.string().notNull() - Body: - - Return: decryptData($value) diff --git a/murano/tests/unit/dsl/meta/TestExtensionMethods.yaml b/murano/tests/unit/dsl/meta/TestExtensionMethods.yaml deleted file mode 100644 index eb543efaa..000000000 --- a/murano/tests/unit/dsl/meta/TestExtensionMethods.yaml +++ /dev/null @@ -1,122 +0,0 @@ -Namespaces: - =: extcls - ---- # ------------------------------------------------------------------ # --- - -Name: Extended - -Properties: - prop: - Contract: $.int() - Default: 123 -Methods: - method: - Body: - Return: $.prop - - ---- # ------------------------------------------------------------------ # --- - -Name: Extender - -Methods: - importedExtensionMethod: - Usage: Extension - Arguments: - - obj: - Contract: $.class(Extended).notNull() - - n: - Contract: $.int().notNull() - Body: - Return: [$obj.prop * $n, $obj.method() * $n] - - nullableExtension: - Usage: Extension - Arguments: - - obj: - Contract: $.class(Extended) - Body: - Return: $obj?.prop - - extensionMethod: - Usage: Extension - Arguments: - - obj: - Contract: $.class(Extended).notNull() - Body: - Return: 222 - - toTileCase: - Usage: Extension - Arguments: - - str: - Contract: $.string().notNull() - Body: - Return: join($str.toCharArray().select( - selectCase($.toLower() = $).switchCase($.toUpper(), $.toLower())), '') - - ---- # ------------------------------------------------------------------ # --- - -Name: TestClass -Import: Extender - -Methods: - testSelfExtensionMethod: - Body: - Return: new(Extended).selfExtensionMethod() - - testImportedExtensionMethod: - Body: - Return: new(Extended).importedExtensionMethod(2) - - testNullableExtensionMethod: - Body: - Return: - - new(Extended).nullableExtension() - - null.nullableExtension() - - testExtensionsPrecedence: - Body: - Return: new(Extended).extensionMethod() - - testCallOnPrimitiveTypes: - Body: - Return: QwertY.toTileCase() - - testCallExtensionExplicitly: - Body: - Return: :Extender.extensionMethod(new(:Extended)) - - testExplicitCallDoenstWorkOnInstance: - Body: - Return: new(Extended).extensionMethod(new(Extended)) - - testCallPythonExtension: - Body: - Return: 4.pythonExtension() - - testCallPythonExtensionExplicitly: - Body: - Return: :Extender.pythonExtension(5) - - testCallPythonClassmethodExtension: - Body: - Return: 7.pythonExtension2() - - selfExtensionMethod: - Usage: Extension - Arguments: - - obj: - Contract: $.class(Extended).notNull() - Body: - Return: [$obj.prop, $obj.method()] - - extensionMethod: - Usage: Extension - Arguments: - - obj: - Contract: $.class(Extended).notNull() - Body: - Return: 111 - diff --git a/murano/tests/unit/dsl/meta/TestFindClass.yaml b/murano/tests/unit/dsl/meta/TestFindClass.yaml deleted file mode 100644 index 37374498b..000000000 --- a/murano/tests/unit/dsl/meta/TestFindClass.yaml +++ /dev/null @@ -1,14 +0,0 @@ -Name: TestFindClass - -Methods: - testFindClassWithPrefix: - Body: - - new('io.murano.extensions.io.murano.test.TestFixture') - - testFindClassShortName: - Body: - - new('io.murano.test.TestFixture') - - testClassWithPrefixNotFound: - Body: - - new('io.murano.extensions.io.murano.test.TestFixture1') \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/TestGC.yaml b/murano/tests/unit/dsl/meta/TestGC.yaml deleted file mode 100644 index 93b55d34b..000000000 --- a/murano/tests/unit/dsl/meta/TestGC.yaml +++ /dev/null @@ -1,180 +0,0 @@ -Namespaces: - sys: io.murano.system - std: io.murano - -Name: TestGCNode -Extends: Node - -Properties: - runtimeProperty: - Usage: Runtime - Contract: $ - -Methods: - .init: - Body: - - $this.runtimeProperty: new(Node) - foo: - Body: - - trace(foo) - - destructionHandler: - Arguments: - - obj: - Contract: $.class(TestGCNode).notNull() - Body: - - trace('Destruction of {0}'.format($obj.value)) - - .destroy: - Body: - - trace($.value) - - ---- - -Name: TestGCNode2 -Extends: TestGCNode - -Methods: - .destroy: - Body: - - trace(list($.nodes.select(sys:GC.isDoomed($)))) - - $owner: $.find(std:Object) - - trace(sys:GC.isDoomed($owner)) - ---- - -Name: TestGCNode3 -Extends: TestGCNode - -Methods: - .init: - Body: - - $this.undeclaredProp: new(TestGCNode, value => C) - ---- - -Name: TestGC - -Properties: - outNode: - Usage: Out - Contract: $.class(TestGCNode) - - staticPropertyNode: - Usage: Static - Contract: $.class(TestGCNode) - -Methods: - testObjectsCollect: - Body: - - $model: - :TestGCNode: - value: A - nodes: - - :TestGCNode: - value: B - - new($model) - - $localAssignedVariable: new($model) - - sys:GC.collect() - - testObjectsCollectWithSubscription: - Body: - - $model: - :TestGCNode: - value: A - nodes: - :TestGCNode: - value: B - - $x: new($model) - - sys:GC.subscribeDestruction($x, $this, _handler) - - sys:GC.subscribeDestruction($x.nodes[0], $this, _handler) - - sys:GC.subscribeDestruction($x.nodes[0], $x, destructionHandler) - - $x: null - - sys:GC.collect() - - _handler: - Arguments: - - obj: - Contract: $.class(TestGCNode).notNull() - Body: - - trace('Destroy ' + $obj.value) - - testCallOnDestroyedObject: - Body: - - $val: new(TestGCNode, value => X) - - sys:GC.subscribeDestruction($val, $this, _handler2) - - $val: null - - sys:GC.collect() - - $this.destroyed.foo() - - _handler2: - Arguments: - - obj: - Contract: $.class(TestGCNode).notNull() - Body: - - $obj.foo() - - $this.destroyed: $obj - - testIsDoomed: - Body: - - $model: - :TestGCNode2: - value: A - nodes: - - :TestGCNode2: - value: B - - new($model, $this) - - sys:GC.collect() - - testIsDestroyed: - Body: - - $val: new(Node, value => X) - - sys:GC.subscribeDestruction($val, $this, _handler3) - - $val: null - - sys:GC.collect() - - trace(sys:GC.isDestroyed($this.destroyed)) - - _handler3: - Arguments: - - obj: - Contract: $.class(Node).notNull() - Body: - - trace(sys:GC.isDestroyed($obj)) - - $this.destroyed: $obj - - testDestructionDependencySerialization: - Body: - - $model: - :TestGCNode: - value: A - nodes: - :TestGCNode: - value: B - - $.outNode: new($model) - - sys:GC.subscribeDestruction($.outNode, $this, _handler) - - sys:GC.subscribeDestruction($.outNode.nodes[0], $this, _handler) - - testStaticProperties: - Body: - - :TestGC.staticPropertyNode: new(TestGCNode, value => A) - - sys:GC.collect() - - methodWithArgs: - Arguments: - - obj: - Contract: $.class(TestGCNode).notNull() - Body: - - sys:GC.collect() - - testDestroyArgs: - Body: - - $.methodWithArgs(new(TestGCNode, value => A)) - - testReachableRuntimeProperties: - Body: - - $node: new(TestGCNode3, value => A) - - sys:GC.collect() - - trace(sys:GC.isDestroyed($node.runtimeProperty)) - - diff --git a/murano/tests/unit/dsl/meta/TestLogger.yaml b/murano/tests/unit/dsl/meta/TestLogger.yaml deleted file mode 100644 index c1f90c2b5..000000000 --- a/murano/tests/unit/dsl/meta/TestLogger.yaml +++ /dev/null @@ -1,107 +0,0 @@ - -Namespaces: - sys: io.murano.system - -Name: TestLogger - -Methods: - testCreate: - Body: - - Return: logger('name') - - testDebug: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.debug('str') - - $log.debug('тест') - - $log.debug('str', 1) - - $log.debug('str {0}', message) - - $log.debug('str {message}', message=>message) - - $log.debug('str {message}{0}') - - testTrace: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.trace('str') - - $log.trace('тест') - - $log.trace('str', 1) - - $log.trace('str {0}', message) - - $log.trace('str {message}', message=>message) - - $log.trace('str {message}{0}') - - testInfo: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.info('str') - - $log.info('тест') - - $log.info('str', 1) - - $log.info('str {0}', message) - - $log.info('str {message}', message=>message) - - $log.info('str {message}{0}') - - testWarning: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.warning('str') - - $log.warning('тест') - - $log.warning('str', 1) - - $log.warning('str {0}', message) - - $log.warning('str {message}', message=>message) - - $log.warning('str {message}{0}') - - testError: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.error('str') - - $log.error('тест') - - $log.error('str', 1) - - $log.error('str {0}', message) - - $log.error('str {message}', message=>message) - - $log.error('str {message}{0}') - - testCritical: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - - $log.critical('str') - - $log.critical('тест') - - $log.critical('str', 1) - - $log.critical('str {0}', message) - - $log.critical('str {message}', message=>message) - - $log.critical('str {message}{0}') - - testException: - Arguments: - - log: - Contract: $.class(sys:Logger).notNull() - Body: - Try: - - $.doThrow() - Catch: - With: exceptionName - As: e - Do: - - $log.exception($e, 'str') - - $log.exception($e, 'тест') - - $log.exception($e, 'str', 1) - - $log.exception($e, 'str {0}', message) - - $log.exception($e, 'str {message}', message=>message) - - $log.exception($e, 'str {message}{0}') - Finally: - - - doThrow: - Body: - - Throw: exceptionName - Message: exception message diff --git a/murano/tests/unit/dsl/meta/TestMeta.yaml b/murano/tests/unit/dsl/meta/TestMeta.yaml deleted file mode 100644 index ebb50dc3d..000000000 --- a/murano/tests/unit/dsl/meta/TestMeta.yaml +++ /dev/null @@ -1,275 +0,0 @@ -Namespaces: - =: metatests - -Name: InheritedMultiMeta -Usage: Meta -Cardinality: Many -Applies: All -Inherited: true - -Properties: - val: - Contract: $.int().notNull() - Default: 111 - ---- # ------------------------------------------------------------------------ - -Name: InheritedSingleMeta -Usage: Meta -Cardinality: One -Applies: All -Inherited: true - -Properties: - val: - Contract: $.int().notNull() - Default: 222 - ---- # ------------------------------------------------------------------------ - -Name: InheritedSingleMeta2 -Usage: Meta -Cardinality: One -Applies: All -Inherited: true -Extends: InheritedSingleMeta - ---- # ------------------------------------------------------------------------ - -Name: MultiMeta -Usage: Meta -Cardinality: Many -Applies: All -Inherited: false - -Properties: - val: - Contract: $.int().notNull() - Default: 333 - ---- # ------------------------------------------------------------------------ - -Name: SingleMeta -Usage: Meta -Cardinality: One -Applies: All -Inherited: false - -Properties: - val: - Contract: $.int().notNull() - Default: 444 - ---- # ------------------------------------------------------------------------ - -Name: SingleMeta2 -Usage: Meta -Cardinality: One -Applies: All -Inherited: false -Extends: SingleMeta - ---- # ------------------------------------------------------------------------ - -Name: ComplexMeta -Usage: Meta -Cardinality: Many -Properties: - cls: - Contract: $.class(PropertyType).notNull() - - ---- # ------------------------------------------------------------------------ - -Name: ParentClass0 -Meta: [InheritedMultiMeta, SingleMeta] - -Properties: - prop1: - Contract: $ - Meta: - - SingleMeta: - val: 1 - -Methods: - foo: - Meta: - - InheritedMultiMeta: - val: 1 - - InheritedSingleMeta: - val: 2 - - SingleMeta: - val: 3 - - InheritedSingleMeta2: - val: 10 - - SingleMeta2: - val: 11 - - Arguments: - arg: - Contract: $.string() - ---- # ------------------------------------------------------------------------ - -Name: ParentClass1 -Extends: ParentClass0 -Meta: - - InheritedMultiMeta: - val: 1 - - InheritedSingleMeta: - val: 6 - - SingleMeta: - val: 7 - -Methods: - foo: - Meta: - - InheritedMultiMeta: - val: 4 - - InheritedSingleMeta: - val: 5 - - SingleMeta: - val: 6 - - Arguments: - arg: - Contract: $.string() - - ---- # ------------------------------------------------------------------------ - -Name: ParentClass2 -Extends: ParentClass0 -Usage: Class -Meta: - - InheritedMultiMeta: - val: 2 - - SingleMeta: - val: 3 - -Properties: - prop2: - Contract: $ - Meta: - - InheritedMultiMeta: - val: 1 - - MultiMeta: - val: 2 - - SingleMeta: - val: 3 - ---- # ------------------------------------------------------------------------ - -Name: TestMeta -Extends: [ParentClass1, ParentClass2] -Meta: - - 'metatests.InheritedMultiMeta': - val: 4 - - :SingleMeta: - val: 5 - -Properties: - prop2: - Contract: $ - Meta: - - InheritedMultiMeta: - val: 4 - -Methods: - testClassMultiMeta: - Body: - - Return: typeinfo($).meta.where($ is InheritedMultiMeta).val - - testClassSingleMeta: - Body: - - Return: typeinfo($).meta. - where($ is SingleMeta or $ is InheritedSingleMeta).val - - testParentClassNotInheritedMeta: - Body: - - Return: typeinfo(ParentClass2).meta. - where(not typeinfo($).inherited).single().val - - testMethodMeta: - Body: - - Return: typeinfo($).methods.where($.name = foo).single().meta.val - - testMethodArgumentMeta: - Body: - - Return: typeinfo($). - methods.where($.name = foo).single(). - arguments.single().meta.val - - testInheritedPropertyMeta: - Body: - - Return: typeinfo($).properties. - where($.name = prop1).single().meta.val - - testOverriddenPropertyMeta: - Body: - - Return: typeinfo($).properties. - where($.name = prop2).single().meta.val - - testPackageMeta: - Body: - - Return: typeinfo($).package.meta - - testComplexMeta: - Body: - - Return: typeinfo($). - methods.where($.name = bar).single().meta.cls. - select([$.prop, typeinfo($).name]) - - foo: - Meta: - - InheritedMultiMeta: - val: 7 - - InheritedSingleMeta: - val: 8 - - SingleMeta: - val: 8 + 1 - - Arguments: - arg: - Contract: $.string() - Meta: - - SingleMeta: - val: 1 - - MultiMeta: - val: 2 - - MultiMeta: - val: 3 - bar: - Meta: - - ComplexMeta: - cls: - :PropertyType: - prop: 1 - - ComplexMeta: - cls: - prop: 2 - - :ComplexMeta: - cls: - :PropertyType2: - prop: 3 - - 'metatests.ComplexMeta': - cls: - prop: 4 - - ComplexMeta: - cls: - ?: - type: 'metatests.PropertyType2' - prop: 5 - ---- # ------------------------------------------------------------------------ - -Name: PropertyType -Properties: - prop: - Contract: $.int().notNull() - Default: 44 - ---- # ------------------------------------------------------------------------ - -Name: PropertyType2 -Extends: PropertyType diff --git a/murano/tests/unit/dsl/meta/TestMethodParamInheritance.yaml b/murano/tests/unit/dsl/meta/TestMethodParamInheritance.yaml deleted file mode 100644 index a873fd6a5..000000000 --- a/murano/tests/unit/dsl/meta/TestMethodParamInheritance.yaml +++ /dev/null @@ -1,30 +0,0 @@ -Name: TestMethodParamInheritanceBase - -Methods: - testRunWithParam: - Body: - - $this.method1('foo') - - testRunWithoutParam: - Body: - - $this.method2() - - method1: - Arguments: - - foo: - Contract: $.string().notNull() - method2: - - ---- # ------------------------------------------------------------------ # --- - -Name: TestMethodParamInheritanceDerived -Extends: TestMethodParamInheritanceBase - -Methods: - method1: - - method2: - Arguments: - - foo: - Contract: $.string().notNull() diff --git a/murano/tests/unit/dsl/meta/TestObjectsCopyMerge.yaml b/murano/tests/unit/dsl/meta/TestObjectsCopyMerge.yaml deleted file mode 100644 index a83b35229..000000000 --- a/murano/tests/unit/dsl/meta/TestObjectsCopyMerge.yaml +++ /dev/null @@ -1,24 +0,0 @@ -Name: TestObjectsCopyMergeSampleClass -Extends: Node - -Methods: - .init: - Body: - - $.testValue: null - - .destroy: - Body: - - trace($.value) - - $.nodes.select(trace($.value)) - - $.nodes.first().setValue('It works!') - - setValue: - Arguments: - - arg: - Contract: $.string().notNull() - Body: - - $.testValue: $arg - - testMethod: - Body: - Return: $.testValue diff --git a/murano/tests/unit/dsl/meta/TestReflection.yaml b/murano/tests/unit/dsl/meta/TestReflection.yaml deleted file mode 100644 index 2eba9612f..000000000 --- a/murano/tests/unit/dsl/meta/TestReflection.yaml +++ /dev/null @@ -1,85 +0,0 @@ -Name: TestReflection - -Properties: - property: - Contract: $.string() - Default: object - Usage: InOut - - staticProperty: - Contract: $.string() - Default: static - Usage: Static - -Methods: - testTypeInfo: - Body: - - $typeinfo: typeinfo($) - - Return: - name: $typeinfo.name - versionStr: str($typeinfo.version) - versionMajor: $typeinfo.version.major - versionMinor: $typeinfo.version.minor - versionPatch: $typeinfo.version.patch - ancestors: $typeinfo.ancestors.name - properties: $typeinfo.properties.name.orderBy($) - methods: $typeinfo.methods.name.where(not $.startsWith(test)).orderBy($) - package: $typeinfo.package.name - - testMethodInfo: - Body: - - $method: typeinfo($).methods.where($.name = foo).single() - - $bar: $method.arguments.where($.name = bar).single() - - $baz: $method.arguments.where($.name = baz).single() - - Return: - name: $method.name - arguments: $method.arguments.name - barHasDefault: $bar.hasDefault - bazHasDefault: $baz.hasDefault - declaringType: $method.declaringType.name - barMethod: $bar.declaringMethod.name - bazMethod: $baz.declaringMethod.name - - testPropertyInfo: - Body: - - $property: typeinfo($).properties.orderBy($.name).first() - - Return: - name: $property.name - hasDefault: $property.hasDefault - usage: $property.usage - - foo: - Arguments: - - bar: - Contract: $.string() - Default: null - - baz: - Contract: $.string() - Body: - Return: $bar + $baz - - testPropertyRead: - Body: - - $instanceValues: typeinfo($).properties.orderBy($.name). - select($.getValue($this)) - - $staticValues: typeinfo($).properties.orderBy($.name). - where($.usage = Static).select($.getValue(null)) - - Return: [$instanceValues, $staticValues] - - testPropertyWrite: - Body: - - $instanceProperty: typeinfo($).properties.where($.name = property).single() - - $instanceProperty.setValue($, 'new object') - - $staticProperty: typeinfo($).properties.where($.name = staticProperty).single() - - $staticProperty.setValue($, 'new static') - - Return: $.testPropertyRead() - - testMethodInvoke: - Body: - - $method: typeinfo($).methods.where($.name = foo).single() - - Return: $method.invoke($, 'bar ', baz => baz) - - testInstanceCreate: - Body: - - $obj: new(typeinfo($).type, property => test) - - Return: $obj.property diff --git a/murano/tests/unit/dsl/meta/TestSchema.yaml b/murano/tests/unit/dsl/meta/TestSchema.yaml deleted file mode 100644 index 55cb4a425..000000000 --- a/murano/tests/unit/dsl/meta/TestSchema.yaml +++ /dev/null @@ -1,132 +0,0 @@ -Namespaces: - m: io.murano.metadata - mf: io.murano.metadata.forms - -Name: TestSchema - -Meta: - - mf:Section: - name: mySection - title: Section Title - index: 1 - - mf:Section: - name: mySection - title: Another Section Title - index: 2 - -Properties: - stringProperty: - Contract: $.string() - - stringNotNullProperty: - Contract: $.string().notNull() - - intProperty: - Contract: $.int() - - intNotNullProperty: - Contract: $.int().notNull() - - boolProperty: - Contract: $.bool() - - boolNotNullProperty: - Contract: $.bool().notNull() - - listProperty: - Contract: - - $.string().notNull() - - dictProperty: - Contract: - key1: $.string().notNull() - key2: $.string().notNull() - $.string().notNull(): $.int() - - classProperty: - Contract: $.class(SampleClass1) - - templateProperty: - Contract: $.template(SampleClass1, excludeProperties => [stringProperty]) - - defaultProperty: - Contract: $.int() - Default: 999 - - complexProperty: - Contract: - $.string(): [$.int().notNull()] - - minimumContract: - Contract: $.int().notNull().check($ >= 5) - - maximumContract: - Contract: $.int().notNull().check($ < 15) - - rangeContract: - Contract: $.int().notNull().check($ > 0 and $ <= 10) - - chainContract: - Contract: $.int().notNull().check($ > 0).check($ <= 10) - - regexContract: - Contract: $.string().notNull().check($.matches(`\d+`)) - - enumContract: - Contract: $.string().notNull().check($ in [a, b]) - - enumFuncContract: - Contract: $.string().notNull().check($ in $this.staticEnumMethod()) - - decoratedProperty: - Contract: $.string().notNull() - Meta: - - m:Title: - text: Title! - - m:Description: - text: Description! - - m:HelpText: - text: Help! - - mf:Hidden - - mf:Position: - index: 1 - section: mySection - - -Methods: - staticEnumMethod: - Body: - Return: - - x - - y - Usage: Static - - modelBuilder: - Meta: - - m:ModelBuilder - - m:Title: - text: Model Builder! - Usage: Static - Scope: Public - Arguments: - - arg1: - Meta: - m:Title: - text: Arg1! - Contract: $.string().notNull() - - arg2: - Contract: $.int().notNull() - - invalidModelBuilder1: - Meta: - - m:ModelBuilder - - invalidModelBuilder2: - Meta: - - m:ModelBuilder - Usage: Static - - invalidModelBuilder3: - Meta: - - m:ModelBuilder - Scope: Public diff --git a/murano/tests/unit/dsl/meta/TestStatics.yaml b/murano/tests/unit/dsl/meta/TestStatics.yaml deleted file mode 100644 index a0c95bcf7..000000000 --- a/murano/tests/unit/dsl/meta/TestStatics.yaml +++ /dev/null @@ -1,256 +0,0 @@ -Namespaces: - ns: test - =: test - e: '' - ---- # --------------------------------------------------------------------- -# TestStaticsBase class - base class for TestStatics to test how static -# entities work in respect to class inheritance ---- # --------------------------------------------------------------------- -Name: TestStaticsBase - -Properties: - baseStaticProperty: - Contract: $.string() - Default: baseStaticProperty - Usage: Static - - conflictingStaticProperty: - Contract: $.string() - Default: 'conflictingStaticProperty-base' - Usage: Static - ---- # --------------------------------------------------------------------- -# TestStatics class - main class for the static tests ---- # --------------------------------------------------------------------- -Name: TestStatics - -Extends: TestStaticsBase - -Properties: - staticProperty: - Contract: $.string() - Usage: Static - Default: xxx - - staticDictProperty: - Contract: {} - Usage: Static - - conflictingStaticProperty: - Contract: $.string() - Default: 'conflictingStaticProperty-child' - Usage: Static - - instanceProperty: - Contract: $.int() - Default: 555 - - staticProperty2: - Contract: $.string() - Default: staticProperty - Usage: Static - -Methods: - testStaticTest: - Usage: Static - Body: - Return: $ - - testCallStaticMethodOnObject: - Body: - Return: $.simpleStaticMethod() - - testCallStaticMethodOnClassName: - Body: - Return: :TestStatics.simpleStaticMethod() - - testCallStaticMethodOnInvalidClass: - Body: - Return: e:TestUnicode.simpleStaticMethod() - - testCallStaticMethodOnClassNameWithNs: - Body: - Return: ns:TestStatics.simpleStaticMethod($.instanceProperty) - - testCallStaticMethodFromAnotherMethod: - Body: - Return: ns:TestStatics.simpleStaticMethod2() - - testStaticThis: - Body: - Return: $.returnStaticThis() - - testNoAccessToInstanceProperties: - Body: - Return: $.accessInstanceProperty() - - testAccessStaticPropertyFromInstanceMethod: - Body: - Return: $.staticProperty - - testAccessStaticPropertyFromStaticMethod: - Body: - Return: $.accessStaticProperty() - - simpleStaticMethod: - Usage: Static - Arguments: - arg: - Contract: $.int() - Default: 0 - Body: - Return: 123 + $arg - - simpleStaticMethod2: - Usage: Static - Body: - Return: $.simpleStaticMethod() + - $this.simpleStaticMethod() + - ns:TestStatics.simpleStaticMethod() + - :TestStatics.simpleStaticMethod() + - type('test.TestStatics').simpleStaticMethod() - - returnStaticThis: - Usage: Static - Body: - Return: $ - - accessInstanceProperty: - Usage: Static - Body: - Return: $.instanceProperty - - accessStaticProperty: - Usage: Static - Body: - Return: $.staticProperty - - testModifyStaticPropertyUsingDollar: - Body: - Return: $.modifyStaticPropertyUsingDollar() - - modifyStaticPropertyUsingDollar: - Usage: Static - Body: - - $.staticProperty: qq - - Return: $.staticProperty - - testModifyStaticPropertyUsingThis: - Body: - Return: $.modifyStaticPropertyUsingThis() - - modifyStaticPropertyUsingThis: - Usage: Static - Body: - - $this.staticProperty: qq - - Return: $this.staticProperty - - testModifyStaticPropertyUsingClassName: - Body: - Return: $.modifyStaticPropertyUsingClassName() - - modifyStaticPropertyUsingClassName: - Usage: Static - Body: - - :TestStatics.staticProperty: qq - - Return: :TestStatics.staticProperty - - testModifyStaticPropertyUsingNsClassName: - Body: - Return: $.modifyStaticPropertyUsingNsClassName() - - modifyStaticPropertyUsingNsClassName: - Usage: Static - Body: - - ns:TestStatics.staticProperty: qq - - Return: ns:TestStatics.staticProperty - - testModifyStaticPropertyUsingTypeFunc: - Body: - Return: $.modifyStaticPropertyUsingTypeFunc() - - modifyStaticPropertyUsingTypeFunc: - Usage: Static - Body: - - type('test.TestStatics').staticProperty: qq - - Return: type('test.TestStatics').staticProperty - - testModifyStaticDictProperty: - Body: - Return: $.modifyStaticDictProperty() - - modifyStaticDictProperty: - Usage: Static - Body: - - :TestStatics.staticDictProperty.key: value - - Return: $.staticDictProperty - - testPropertyIsStatic: - Body: - Return: $.modifyStaticPropertyOnInstance() - - modifyStaticPropertyOnInstance: - Usage: Static - Body: - - $obj1: new(TestStatics) - - $obj2: new(TestStatics) - - $obj1.modifyStaticPropertyUsingClassName() - - Return: $obj2.staticProperty - - testStaticPropertisNotLoaded: - Body: - Return: $.staticProperty2 - - testTypeIsSingleton: - Body: - - $t11: :TestStatics - - $t12: :TestStatics - - $t21: ns:TestStatics - - $t22: ns:TestStatics - - $t31: type('test.TestStatics') - - $t32: type('test.TestStatics') - - Return: $t11 = $t12 and $t21 = $t22 and $t31 = $t32 - - testStaticPropertyInheritance: - Body: - Return: $.baseStaticProperty + - :TestStaticsBase.baseStaticProperty + - :TestStatics.baseStaticProperty - - testStaticPropertyOverride: - Body: - Return: - - $.conflictingStaticProperty - - :TestStatics.conflictingStaticProperty - - :TestStaticsBase.conflictingStaticProperty - - type('test.TestStatics').conflictingStaticProperty - - type('test.TestStaticsBase').conflictingStaticProperty - - testTypeinfoOfType: - Body: - - $typeObj: type('test.TestStatics') - - $typeInfoOfType: typeinfo($typeObj) - - $obj: new('TestStatics') - - Return: typeinfo($obj) = $typeInfoOfType - - testCallPythonStaticMethod: - Body: - Return: - - $.staticPythonMethod(111) - - :TestStatics.staticPythonMethod(111) - - ns:TestStatics.staticPythonMethod(111) - - type('test.TestStatics').staticPythonMethod(111) - - testCallPythonClassMethod: - Body: - Return: - - $.classmethodPythonMethod('!') - - :TestStatics.classmethodPythonMethod('!') - - ns:TestStatics.classmethodPythonMethod('!') - - type('test.TestStatics').classmethodPythonMethod('!') - - testStaticAction: - Usage: Static - Body: - Return: 'It works!' \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/TestUnicode.yaml b/murano/tests/unit/dsl/meta/TestUnicode.yaml deleted file mode 100644 index f6dc3eb02..000000000 --- a/murano/tests/unit/dsl/meta/TestUnicode.yaml +++ /dev/null @@ -1,29 +0,0 @@ -Name: TestUnicode - -Methods: - testLiteral: - Body: - Return: солнце ♥ φεγγάρι - - testExpression: - Body: - - Return: ('солнце ♥' + ' φεγγάρι').toUpper() - - - testParameter: - Body: - - Return: $.foo('солнце ♥ φεγγάρι') - - - testException: - Body: - - Throw: Exception - Message: солнце ♥ φεγγάρι - - - foo: - Arguments: - arg: - Contract: $.string().notNull() - Body: - Return: $arg.toUpper() \ No newline at end of file diff --git a/murano/tests/unit/dsl/meta/TestVarKwArgs.yaml b/murano/tests/unit/dsl/meta/TestVarKwArgs.yaml deleted file mode 100644 index 213dff2ba..000000000 --- a/murano/tests/unit/dsl/meta/TestVarKwArgs.yaml +++ /dev/null @@ -1,65 +0,0 @@ -Name: TestVarKwArgs - -Methods: - testVarArgs: - Body: - Return: $.varArgsMethod(1, 2, 3, 4) - - testVarArgsContract: - Body: - Return: $.varArgsMethod(1, string) - - testDuplicateVarArgs: - Body: - Return: $.varArgsMethod(1, arg1 => 2) - - testExplicitVarArgs: - Body: - Return: $.varArgsMethod(1, rest => 2) - - varArgsMethod: - Arguments: - - arg1: - Contract: $.int() - - rest: - Contract: $.int() - Usage: VarArgs - Body: - Return: $rest - - testKwArgs: - Body: - Return: $.kwArgsMethod(arg1 => 1, arg2 => 2, arg3 => 3) - - testKwArgsContract: - Body: - Return: $.kwArgsMethod(arg1 => 1, arg2 => string) - - testDuplicateKwArgs: - Body: - Return: $.kwArgsMethod(1, arg1 => 2) - - kwArgsMethod: - Arguments: - - arg1: - Contract: $.int() - - rest: - Contract: $.int() - Usage: KwArgs - Body: - Return: $rest - - testArgs: - Body: - Return: $.argsMethod(1, 2, 3, arg1 => 4, arg2 => 5, arg3 => 6) - - argsMethod: - Arguments: - - args: - Contract: $.int() - Usage: VarArgs - - kwargs: - Contract: $.int() - Usage: KwArgs - Body: - Return: [$args, $kwargs] diff --git a/murano/tests/unit/dsl/test_agent.py b/murano/tests/unit/dsl/test_agent.py deleted file mode 100644 index 3a119bbb7..000000000 --- a/murano/tests/unit/dsl/test_agent.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 unittest import mock - -from murano.common import exceptions as exc -from murano.dsl import constants -from murano.dsl import dsl -from murano.dsl import helpers -from murano.dsl import yaql_integration -from murano.engine.system import agent -from murano.engine.system import agent_listener -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestAgentListener(test_case.DslTestCase): - def setUp(self): - super(TestAgentListener, self).setUp() - - # Register Agent class - self.package_loader.load_package('io.murano', None).register_class( - agent_listener.AgentListener) - model = om.Object( - 'AgentListenerTests') - self.runner = self.new_runner(model) - self.context = yaql_integration.create_empty_context() - self.context[constants.CTX_THIS] = mock.MagicMock( - dsl.MuranoObjectInterface) - - def test_listener_enabled(self): - self.override_config('disable_murano_agent', False, 'engine') - al = self.runner.testAgentListener().extension - self.assertTrue(al.enabled) - with self.runner.session(), helpers.contextual(self.context): - try: - al.subscribe('msgid', 'event') - self.assertEqual({'msgid': 'event'}, al._subscriptions) - finally: - al.stop() - - def test_listener_disabled(self): - self.override_config('disable_murano_agent', True, 'engine') - al = self.runner.testAgentListener().extension - self.assertFalse(al.enabled) - self.assertRaises(exc.PolicyViolationException, - al.subscribe, 'msgid', 'event') - - -class TestAgent(test_case.DslTestCase): - def test_agent_enabled(self): - self.override_config('disable_murano_agent', False, 'engine') - self.override_config('signing_key', False, group='engine') - agent_cls = 'murano.engine.system.agent.Agent' - a = agent.Agent(mock.MagicMock()) - self.assertTrue(a.enabled) - - with mock.patch(agent_cls + '._send') as s: - s.return_value = mock.MagicMock() - a.send_raw({}) - s.assert_called_with({}, False, 0) - - def test_agent_disabled(self): - self.override_config('disable_murano_agent', True, 'engine') - self.override_config('signing_key', False, group='engine') - a = agent.Agent(mock.MagicMock()) - self.assertFalse(a.enabled) - self.assertRaises(exc.PolicyViolationException, a.call, {}, None) - self.assertRaises(exc.PolicyViolationException, a.send, {}, None) - self.assertRaises(exc.PolicyViolationException, a.call_raw, {}) - self.assertRaises(exc.PolicyViolationException, a.send_raw, {}) diff --git a/murano/tests/unit/dsl/test_assignments.py b/murano/tests/unit/dsl/test_assignments.py deleted file mode 100644 index e463f5fc6..000000000 --- a/murano/tests/unit/dsl/test_assignments.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestAssignments(test_case.DslTestCase): - def setUp(self): - super(TestAssignments, self).setUp() - self._runner = self.new_runner( - om.Object( - 'SampleClass1', - stringProperty='string', - classProperty=om.Object( - 'SampleClass2', - class2Property='another string'))) - - def test_assignment(self): - self.assertEqual( - { - 'Arr': [5, 2, [10, 123]], - 'Dict': { - 'Key1': 'V1', - 'Key2': {'KEY2': 'V3', 'a_b': 'V2'} - } - }, self._runner.testAssignment()) - - def test_assignment_on_property(self): - self.assertEqual( - { - 'Arr': [5, 2, [10, 123]], - 'Dict': { - 'Key1': 'V1', - 'Key2': {'KEY2': 'V3', 'a_b': 'V2'} - } - }, self._runner.testAssignmentOnProperty()) - - def test_assign_by_copy(self): - self.assertEqual( - [1, 2, 3], - self._runner.testAssignByCopy([1, 2, 3])) diff --git a/murano/tests/unit/dsl/test_attribute_store.py b/murano/tests/unit/dsl/test_attribute_store.py deleted file mode 100644 index 1da7c003f..000000000 --- a/murano/tests/unit/dsl/test_attribute_store.py +++ /dev/null @@ -1,133 +0,0 @@ -# Licensed 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 unittest import mock - -from murano.dsl import attribute_store -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.tests.unit.dsl.foundation import test_case - - -class TestAttributeStore(test_case.DslTestCase): - def setUp(self): - super(TestAttributeStore, self).setUp() - self.attribute_store = attribute_store.AttributeStore() - self.fake_object = mock.MagicMock(object_id=mock.sentinel.oid) - self.tagged_obj = dsl.MuranoObjectInterface(self.fake_object) - self.owner_type = dsl_types.MuranoTypeReference(self.fake_object) - self.owner_type.type.name = mock.sentinel.typename - self.name = 'foobar' - - def test_get_attribute_key(self): - oid, typename = mock.sentinel.oid, mock.sentinel.typename - - key = self.attribute_store._get_attribute_key( - self.tagged_obj, self.owner_type, self.name) - expected_key = (oid, (typename, 'foobar')) - - self.assertEqual(expected_key, key) - - @mock.patch.object(attribute_store.AttributeStore, '_get_attribute_key', - return_value=(mock.sentinel.key1, mock.sentinel.key2)) - def test_get(self, mock_get_attr_key): - key1, key2 = mock.sentinel.key1, mock.sentinel.key2 - get_val = mock.sentinel.get_val - - self.attribute_store._attributes = mock.MagicMock() - self.attribute_store._attributes[key1].get.return_value = get_val - val = self.attribute_store.get( - self.tagged_obj, self.owner_type, self.name) - - mock_get_attr_key.assert_called_with( - self.tagged_obj, self.owner_type, self.name) - self.attribute_store._attributes[key1].get.assert_called_with(key2) - self.assertEqual(get_val, val) - - @mock.patch.object(attribute_store.AttributeStore, '_get_attribute_key', - return_value=(mock.sentinel.key1, mock.sentinel.key2)) - def test_set_object_if(self, mock_get_attr_key): - val = dsl.MuranoObjectInterface(self.fake_object) - self.attribute_store._attributes = mock.MagicMock() - self.attribute_store.set( - self.tagged_obj, self.owner_type, self.name, val) - - @mock.patch.object(attribute_store.AttributeStore, '_get_attribute_key', - return_value=(mock.sentinel.key1, mock.sentinel.key2)) - def test_set_object(self, mock_get_attr_key): - key1, key2 = mock.sentinel.key1, mock.sentinel.key2 - - val = dsl_types.MuranoObject() - val.object_id = mock.sentinel.oid - self.attribute_store.set( - self.tagged_obj, self.owner_type, self.name, val) - self.assertEqual(self.attribute_store._attributes[key1][key2], - mock.sentinel.oid) - - @mock.patch.object(attribute_store.AttributeStore, '_get_attribute_key', - return_value=(mock.sentinel.key1, mock.sentinel.key2)) - def test_set_none(self, mock_get_attr_key): - key1, key2 = mock.sentinel.key1, mock.sentinel.key2 - - val = None - self.attribute_store._attributes = mock.MagicMock() - self.attribute_store.set( - self.tagged_obj, self.owner_type, self.name, val) - - self.attribute_store._attributes[key1].pop.assert_called_with( - key2, None) - - def test_serialize(self): - known_objects = ['obj1', 'obj3'] - self.attribute_store._attributes = { - 'obj1': { - ('foo', 'obj11'): 11, - ('bar', 'obj12'): 12 - }, - 'obj2': { - ('baz', 'obj21'): 21 - }, - 'obj3': { - ('foobar', 'obj31'): 31 - } - } - val = self.attribute_store.serialize(known_objects) - expected = [ - ['obj1', 'foo', 'obj11', 11], - ['obj1', 'bar', 'obj12', 12], - ['obj3', 'foobar', 'obj31', 31]] - - self.assertEqual(sorted(expected), sorted(val)) - - def test_load(self): - data = [ - ['a', 'b', 'c', 'd'], - ['a', 'f', 'g', None], - ['b', 'i', 'j', 'k'] - ] - - self.attribute_store.load(data) - expected = {'a': {('b', 'c'): 'd'}, - 'b': {('i', 'j'): 'k'}} - self.assertEqual(expected, self.attribute_store._attributes) - - def test_forget_object_if(self): - obj = dsl.MuranoObjectInterface(mock.MagicMock(object_id='bar')) - self.attribute_store._attributes = {'foo': 42, 'bar': 43} - self.attribute_store.forget_object(obj) - self.assertEqual({'foo': 42}, self.attribute_store._attributes) - - def test_forget_object(self): - obj = dsl_types.MuranoObject() - obj.object_id = 'foo' - self.attribute_store._attributes = {'foo': 42, 'bar': 43} - self.attribute_store.forget_object(obj) - self.assertEqual({'bar': 43}, self.attribute_store._attributes) diff --git a/murano/tests/unit/dsl/test_call.py b/murano/tests/unit/dsl/test_call.py deleted file mode 100644 index a813e39c4..000000000 --- a/murano/tests/unit/dsl/test_call.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestCall(test_case.DslTestCase): - def setUp(self): - super(TestCall, self).setUp() - self._runner = self.new_runner(om.Object('TestCall')) - - def test_call(self): - self._runner.testCall() - self.assertEqual(['called as call'], self._traces) - - def test_method(self): - self._runner.testMethodInvocation() - self.assertEqual(['called as method'], self._traces) - - def test_call_static(self): - self._runner.testCallStatic() - self.assertEqual(['called as static'], self._traces) - - def test_call_static_as_instance(self): - self._runner.testCallStaticAsInstance() - self.assertEqual(['called as static'], self._traces) diff --git a/murano/tests/unit/dsl/test_concurrency.py b/murano/tests/unit/dsl/test_concurrency.py deleted file mode 100644 index 67ddb2684..000000000 --- a/murano/tests/unit/dsl/test_concurrency.py +++ /dev/null @@ -1,84 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 eventlet - - -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestConcurrency(test_case.DslTestCase): - def setUp(self): - super(TestConcurrency, self).setUp() - - def yield_(): - self.traces.append('yield') - eventlet.sleep(0) - - self.register_function(yield_, 'yield') - self._runner = self.new_runner(om.Object('TestConcurrency')) - - def check_isolated_traces(self): - for i in range(0, len(self.traces), 3): - before = self.traces[i] - switch = self.traces[i + 1] - after = self.traces[i + 2] - self.assertEqual('yield', switch) - self.assertEqual(before[0:6], after[0:6]) - self.assertTrue(before.endswith('-before')) - self.assertTrue(after.endswith('-after')) - - def check_concurrent_traces(self): - self.assertTrue(self.traces[0].endswith('-before')) - self.assertEqual('yield', self.traces[1]) - self.assertTrue(self.traces[2].endswith('-before')) - self.assertEqual('yield', self.traces[3]) - self.assertTrue(self.traces[4].endswith('-before')) - self.assertEqual('yield', self.traces[5]) - self.assertTrue(self.traces[6].endswith('-after')) - self.assertTrue(self.traces[7].endswith('-after')) - self.assertTrue(self.traces[8].endswith('-after')) - - def test_isolated(self): - self._runner.testCallIsolated() - self.check_isolated_traces() - - def test_isolated_default(self): - self._runner.testCallIsolatedWithDefault() - self.check_isolated_traces() - - def test_concurrent_explicit(self): - self._runner.testCallConcurrentExplicit() - self.check_concurrent_traces() - - def test_isolated_explicit(self): - self._runner.testCallIsolatedExplicit() - self.check_isolated_traces() - - def test_argbased_primitive_isolated(self): - self._runner.testCallArgbasedPrimitiveIsolated() - self.check_isolated_traces() - - def test_argbased_primitive_concurrent(self): - self._runner.testCallArgbasedPrimitiveConcurrent() - self.check_concurrent_traces() - - def test_argbased_object_isolated(self): - self._runner.testCallArgbasedWithObjectIsolated() - self.check_isolated_traces() - - def test_argbased_object_concurrent(self): - self._runner.testCallArgbasedWithObjectConcurrent() - self.check_concurrent_traces() diff --git a/murano/tests/unit/dsl/test_config_properties.py b/murano/tests/unit/dsl/test_config_properties.py deleted file mode 100644 index a88db1365..000000000 --- a/murano/tests/unit/dsl/test_config_properties.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestConfigProperties(test_case.DslTestCase): - def test_config_property(self): - obj = om.Object('ConfigProperties') - self.package_loader.set_config_value(obj, 'cfgProperty', '987') - runner = self.new_runner(obj) - runner.testPropertyValues() - self.assertEqual( - [987, 'DEFAULT'], - self.traces - ) - - def test_config_property_exclusion_from_obect_model(self): - obj = om.Object('ConfigProperties', cfgProperty=555) - runner = self.new_runner(obj) - runner.testPropertyValues() - self.assertEqual( - [123, 'DEFAULT'], - self.traces - ) - - def test_config_affects_default(self): - obj = om.Object('ConfigProperties') - self.package_loader.set_config_value(obj, 'normalProperty', 'custom') - runner = self.new_runner(obj) - runner.testPropertyValues() - self.assertEqual( - [123, 'custom'], - self.traces - ) - - def test_config_not_affects_in_properties(self): - obj = om.Object('ConfigProperties', normalProperty='qq') - self.package_loader.set_config_value(obj, 'normalProperty', 'custom') - runner = self.new_runner(obj) - runner.testPropertyValues() - self.assertEqual( - [123, 'qq'], - self.traces - ) diff --git a/murano/tests/unit/dsl/test_construction.py b/murano/tests/unit/dsl/test_construction.py deleted file mode 100644 index c521b5e4d..000000000 --- a/murano/tests/unit/dsl/test_construction.py +++ /dev/null @@ -1,73 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 testtools import matchers - -from murano.dsl import dsl -from murano.dsl import serializer -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestConstruction(test_case.DslTestCase): - def setUp(self): - super(TestConstruction, self).setUp() - self._runner = self.new_runner(om.Object('CreatingClass')) - - def test_new(self): - self._runner.testNew() - self.assertEqual( - ['CreatingClass::.init', 'CreatedClass1::.init', - 'string', 'STRING', 123], - self.traces) - - def test_new_with_ownership(self): - obj = serializer.serialize(self._runner.testNewWithOwnership(), - self._runner.executor, allow_refs=False) - self.assertEqual('STRING', obj.get('property1')) - self.assertIsNotNone('string', obj.get('xxx')) - self.assertEqual('STR', obj['xxx'].get('property1')) - self.assertEqual('QQQ', obj['xxx']['?'].get('name')) - - def test_new_with_dict(self): - self._runner.testNewWithDict() - self.assertEqual( - ['CreatingClass::.init', 'CreatedClass1::.init', - 'string', 'STRING', 123], - self.traces) - - def test_model_load(self): - res = self._runner.testLoadCompexModel() - for i in range(3): - self.assertThat(res[i], matchers.Not(matchers.Contains('node'))) - self.assertEqual(self._runner.root.object_id, res[3]) - self.assertEqual( - [ - 'rootNode', - ['childNode1', 'childNode2', 'childNode2'], - True, True, True, True, True, - 'rootNode', 'childNode2', 'childNode1' - ], res[4:]) - - def test_single_contract_instantiation(self): - self._runner.testSingleContractInstantiation() - self.assertEqual(1, self.traces.count('ConstructionSample::init')) - - def test_nested_new_loads_in_separate_store(self): - res = self._runner.testNestedNewLoadsInSeparateStore() - self.assertIsInstance(res, dsl.MuranoObjectInterface) - - def test_reference_access_from_init(self): - self._runner.testReferenceAccessFromInit() - self.assertEqual(2, self.traces.count('childNode')) diff --git a/murano/tests/unit/dsl/test_context_manager.py b/murano/tests/unit/dsl/test_context_manager.py deleted file mode 100644 index 9005d96b1..000000000 --- a/murano/tests/unit/dsl/test_context_manager.py +++ /dev/null @@ -1,44 +0,0 @@ -# Licensed 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 unittest import mock - -from murano.dsl import context_manager -from murano.dsl import yaql_integration -from murano.tests.unit.dsl.foundation import test_case - - -class TestContextManager(test_case.DslTestCase): - def setUp(self): - super(TestContextManager, self).setUp() - self.context_manager = context_manager.ContextManager() - - @mock.patch.object(yaql_integration, 'create_context', return_value='foo') - def test_create_root_context(self, mock_create_context): - val = self.context_manager.create_root_context('myrunver') - - self.assertEqual('foo', val) - mock_create_context.assert_called_with('myrunver') - - def test_create_package_context(self): - package = mock.MagicMock(context='mycontext') - self.assertEqual('mycontext', - self.context_manager.create_package_context(package)) - - def test_create_type_context(self): - murano_type = mock.MagicMock(context='mycontext') - self.assertEqual('mycontext', - self.context_manager.create_type_context(murano_type)) - - def test_create_object_context(self): - obj = 'obj' - self.assertIsNone(self.context_manager.create_object_context(obj)) diff --git a/murano/tests/unit/dsl/test_contracts.py b/murano/tests/unit/dsl/test_contracts.py deleted file mode 100644 index bf40b90cc..000000000 --- a/murano/tests/unit/dsl/test_contracts.py +++ /dev/null @@ -1,351 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl -from murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestContracts(test_case.DslTestCase): - def setUp(self): - super(TestContracts, self).setUp() - self._runner = self.new_runner( - om.Object( - 'ContractExamples', - ordinaryProperty='PROPERTY', - sampleClass=om.Object( - 'SampleClass1', - stringProperty='string1', - classProperty=om.Object( - 'SampleClass2', - class2Property='string2')))) - - def test_string_contract(self): - result = self._runner.testStringContract('qwerty') - self.assertIsInstance(result, str) - self.assertEqual('qwerty', result) - - def test_string_from_number_contract(self): - result = self._runner.testStringContract(123) - self.assertIsInstance(result, str) - self.assertEqual('123', result) - - def test_string_null_contract(self): - self.assertIsNone(self._runner.testStringContract(None)) - - def test_int_contract(self): - result = self._runner.testIntContract(123) - self.assertIsInstance(result, int) - self.assertEqual(123, result) - - def test_int_from_string_contract(self): - result = self._runner.testIntContract('456') - self.assertIsInstance(result, int) - self.assertEqual(456, result) - - def test_int_from_string_contract_failure(self): - self.assertRaises(exceptions.ContractViolationException, - self._runner.testIntContract, 'nan') - - def test_int_null_contract(self): - self.assertIsNone(self._runner.testIntContract(None)) - - def test_bool_contract(self): - result = self._runner.testBoolContract(True) - self.assertIsInstance(result, bool) - self.assertTrue(result) - - result = self._runner.testBoolContract(False) - self.assertIsInstance(result, bool) - self.assertFalse(result) - - def test_bool_from_int_contract(self): - result = self._runner.testBoolContract(10) - self.assertIsInstance(result, bool) - self.assertTrue(result) - - result = self._runner.testBoolContract(0) - self.assertIsInstance(result, bool) - self.assertFalse(result) - - def test_bool_from_string_contract(self): - result = self._runner.testBoolContract('something') - self.assertIsInstance(result, bool) - self.assertTrue(result) - - result = self._runner.testBoolContract('') - self.assertIsInstance(result, bool) - self.assertFalse(result) - - def test_bool_null_contract(self): - self.assertIsNone(self._runner.testIntContract(None)) - - def test_class_contract(self): - arg = om.Object('SampleClass2', class2Property='qwerty') - result = self._runner.testClassContract(arg) - self.assertIsInstance(result, dsl.MuranoObjectInterface) - - def test_class_contract_by_ref(self): - arg = om.Object('SampleClass2', class2Property='qwerty') - result = self._runner.testClassContract(arg) - self.assertNotEqual(arg.id, result.id) - - def test_class_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testClassContract, ['invalid type']) - - def test_class_contract_by_ref_failure(self): - self.assertRaises( - exceptions.NoObjectFoundError, - self._runner.testClassContract, 'NoSuchIdExists') - - def test_class_contract_from_dict(self): - self.assertEqual( - 'SampleClass2', - self._runner.testClassContract({ - 'class2Property': 'str'}).type.name) - - def test_class_from_id_contract(self): - object_id = self._runner.root.get_property('sampleClass').object_id - result = self._runner.testClassFromIdContract(object_id) - self.assertIsInstance(result, dsl.MuranoObjectInterface) - self.assertEqual(object_id, result.id) - - def test_template_contract(self): - arg = om.Object('CreatedClass2', property1='qwerty', property2=123) - result = self._runner.testTemplateContract(arg) - self.assertIsInstance(result, dict) - self.assertCountEqual(['?', 'property1', 'property2'], result.keys()) - - def test_template_property_contract(self): - template = { - 'foo': 123 - } - self.new_runner( - om.Object('ContractExamples', templateProperty=template)) - - def test_template_contract_fail_on_type(self): - arg = om.Object('SampleClass2', class2Property='qwerty') - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testTemplateContract, arg) - - def test_template_contract_with_property_exclusion(self): - arg = om.Object('CreatedClass2', property1='qwerty', - property2='INVALID') - result = self._runner.testTemplateContractExcludeProperty(arg) - self.assertIsInstance(result, dict) - self.assertCountEqual(['?', 'property1'], result.keys()) - - def test_template_contract_with_property_exclusion_from_mpl(self): - result = self._runner.testTemplateContractExcludePropertyFromMpl() - self.assertIsInstance(result, dict) - self.assertCountEqual(['?', 'property1'], result.keys()) - - def test_check_contract(self): - arg = om.Object('SampleClass2', class2Property='qwerty') - self.assertIsNone(self._runner.testCheckContract(arg, 100)) - - def test_check_contract_failure(self): - invalid_arg = om.Object('SampleClass2', class2Property='not qwerty') - self.assertRaises(exceptions.ContractViolationException, - self._runner.testCheckContract, invalid_arg, 100) - - def test_owned_contract(self): - arg1 = self._runner.root.get_property('sampleClass') - arg2 = arg1.get_property('classProperty') - self.assertIsNone(self._runner.testOwnedContract(arg1, arg2)) - - def test_owned_contract_on_null(self): - self.assertIsNone(self._runner.testOwnedContract(None, None)) - - def test_owned_contract_failure(self): - arg1 = self._runner.root.get_property('sampleClass') - arg2 = arg1.get_property('classProperty') - invalid_arg2 = om.Object('SampleClass2', class2Property='string2') - invalid_arg1 = om.Object( - 'SampleClass1', - stringProperty='string1', - classProperty=invalid_arg2) - - self.assertRaises(exceptions.ContractViolationException, - self._runner.testOwnedContract, invalid_arg1, arg2) - self.assertRaises(exceptions.ContractViolationException, - self._runner.testOwnedContract, invalid_arg2, arg1) - - def test_not_owned_contract(self): - arg2 = om.Object('SampleClass2', class2Property='string2') - arg1 = om.Object( - 'SampleClass1', - stringProperty='string1', - classProperty=arg2) - self.assertIsNone(self._runner.testNotOwnedContract(arg1, arg2)) - - def test_not_owned_contract_on_null(self): - self.assertIsNone(self._runner.testNotOwnedContract(None, None)) - - def test_not_owned_contract_failure(self): - invalid_arg1 = self._runner.root.get_property('sampleClass') - invalid_arg2 = invalid_arg1.get_property('classProperty') - arg2 = om.Object('SampleClass2', class2Property='string2') - arg1 = om.Object( - 'SampleClass1', - stringProperty='string1', - classProperty=arg2) - - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testNotOwnedContract, invalid_arg1, arg2) - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testNotOwnedContract, invalid_arg2, arg1) - - def test_scalar_contract(self): - self.assertEqual('fixed', self._runner.testScalarContract( - 'fixed', 456, True)) - - def test_scalar_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testScalarContract, - 'wrong', 456, True) - - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testScalarContract, - 'fixed', 123, True) - - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testScalarContract, - 'fixed', 456, False) - - def test_list_contract(self): - self.assertEqual([3, 2, 1], self._runner.testListContract( - ['3', 2, '1'])) - - def test_list_contract_from_scalar(self): - self.assertEqual([99], self._runner.testListContract('99')) - - def test_list_contract_from_null(self): - self.assertEqual([], self._runner.testListContract(None)) - - def test_list_with_min_length_contract(self): - self.assertEqual( - [1, 2, 3], - self._runner.testListWithMinLengthContract([1, 2, 3])) - self.assertEqual( - [1, 2, 3, 4], - self._runner.testListWithMinLengthContract([1, 2, 3, 4])) - - def test_list_with_min_length_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testListWithMinLengthContract, None) - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testListWithMinLengthContract, [1, 2]) - - def test_list_with_min_max_length_contract(self): - self.assertEqual( - [1, 2], - self._runner.testListWithMinMaxLengthContract([1, 2])) - self.assertEqual( - [1, 2, 3, 4], - self._runner.testListWithMinMaxLengthContract([1, 2, 3, 4])) - - def test_list_with_min_max_length_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testListWithMinMaxLengthContract, [1]) - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testListWithMinMaxLengthContract, [1, 2, 3, 4, 5]) - - def test_dict_contract(self): - self.assertEqual( - {'A': '123', 'B': 456}, - self._runner.testDictContract({'A': '123', 'B': '456'})) - self.assertEqual( - {'A': '123', 'B': 456}, - self._runner.testDictContract({'A': '123', 'B': '456', 'C': 'qq'})) - self.assertEqual( - {'A': '123', 'B': None}, - self._runner.testDictContract({'A': '123'})) - - def test_dict_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testDictContract, 'str') - - def test_dict_expressions_contract(self): - self.assertEqual( - {321: 'qwerty', 99: 'val', 'B': 456}, - self._runner.testDictExprContract({ - '321': 'qwerty', '99': 'val', 'B': 456})) - - def test_dict_expressions_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testDictExprContract, - {'321': 'qwerty', 'str': 'val', 'B': 456}) - - def test_invalid_dict_expr_contract(self): - self.assertRaises( - exceptions.DslContractSyntaxError, - self._runner.testDictMultiExprContract, - {'321': 'qwerty', 'str': 'val', 'B': 456}) - - def test_not_null_contract(self): - self.assertEqual('value', self._runner.testNotNullContract('value')) - - def test_not_null_contract_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self._runner.testNotNullContract, None) - - def test_default(self): - self.assertEqual('value', self._runner.testDefault('value')) - self.assertEqual('DEFAULT', self._runner.testDefault()) - - def test_default_expression(self): - self.assertEqual('PROPERTY', self._runner.testDefaultExpression()) - self.assertEqual('value', self._runner.testDefaultExpression('value')) - - def test_template_with_externally_owned_object(self): - node = om.Object('Node', 'OBJ_ID') - node_template = om.Object('Node', nodes=['OBJ_ID']) - model = om.Object( - 'TemplatePropertyClass', owned=node, template=node_template) - runner = self.new_runner(model) - self.assertEqual( - ['OBJ_ID'], runner.testTemplateWithExternallyOwnedObject()) - - -class TestContractsTransform(test_case.DslTestCase): - def setUp(self): - super(TestContractsTransform, self).setUp() - self._runner = self.new_runner(om.Object('TestIteratorsTransform')) - - def test_property(self): - self.assertEqual('3', self._runner.testProperties()) - - def test_argument(self): - self.assertEqual('3', self._runner.testArgs()) - self.assertEqual('2', self._runner.testUntypedArgs()) - self.assertEqual('6', self._runner.testNotTypedListArgs()) - self.assertEqual('6', self._runner.testTypedList()) - self.assertEqual(2, self._runner.testListDict()) diff --git a/murano/tests/unit/dsl/test_dump.py b/murano/tests/unit/dsl/test_dump.py deleted file mode 100644 index d5efe249a..000000000 --- a/murano/tests/unit/dsl/test_dump.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl_types -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestDump(test_case.DslTestCase): - def setUp(self): - super(TestDump, self).setUp() - self._runner = self.new_runner(om.Object('dumptests.TestDump')) - - def test_dump_simple_inline(self): - source = om.Object('dumptests.DumpTarget1', - foo='FOO', bar=[40, 41, 42], baz={'BAZ': 99}) - result = self._runner.testDump(source, 'Inline') - self.assertIn('id', result) - res = self._get_body(result) - self.assertEqual('FOO', res['foo']) - self.assertEqual([40, 41, 42], res['bar']) - self.assertEqual({'BAZ': 99}, res['baz']) - - def test_dump_simple_serializable(self): - source = om.Object('dumptests.DumpTarget1', - foo='FOO', bar=[40, 41, 42], baz={'BAZ': 99}) - result = self._runner.testDump(source, 'Serializable') - self.assertIn('?', result) - self.assertEqual('dumptests.DumpTarget1/0.0.0@tests', - result['?']['type']) - - def test_dump_simple_full_mixed(self): - source = om.Object('dumptests.DumpTarget1', - foo='FOO', bar=[40, 41, 42], baz={'BAZ': 99}) - - result = self._runner.testDump(source, 'Mixed') - self.assertIn('?', result) - self.assertNotIn('classVersion', result['?']) - self.assertNotIn('package', result['?']) - self.assertIsInstance(result['?']['type'], dsl_types.MuranoType) - self.assertEqual('dumptests.DumpTarget1', result['?']['type'].name) - - def test_nested(self): - n1 = om.Object('dumptests.DumpTarget1', foo='FOO') - n2 = om.Object('dumptests.DumpTarget1', foo='BAR') - n3 = om.Object('dumptests.DumpTarget1', foo='BAZ') - source = om.Object('dumptests.DumpTarget2', - nested=n1, another=n2, ref=n3) - result = self._runner.testDump(source) - res = self._get_body(result) - self.assertIsNotNone(res['ref']) - self.assertIsNotNone(res['another']) - self.assertIsNotNone(res['nested']) - self.assertEqual('FOO', self._get_body(res['nested'])['foo']) - self.assertEqual('BAR', self._get_body(res['another'])['foo']) - self.assertEqual('BAZ', self._get_body(res['ref'])['foo']) - - def test_same_ref_dump(self): - nested = om.Object('dumptests.DumpTarget1', foo='FOO') - source = om.Object('dumptests.DumpTarget2', - nested=nested, another=nested, ref=nested) - result = self._runner.testDump(source) - res = self._get_body(result) - string_keys = [k for k in res.keys() - if isinstance(res[k], str)] - obj_keys = [k for k in res.keys() - if isinstance(res[k], dict)] - self.assertEqual(2, len(string_keys)) - self.assertEqual(1, len(obj_keys)) - obj = self._get_body(res[obj_keys[0]]) - self.assertEqual('FOO', obj['foo']) - for ref_id in string_keys: - self.assertEqual(res[obj_keys[0]]['id'], res[ref_id]) - - def test_dump_with_meta_attributes(self): - n1 = om.Object('dumptests.DumpTarget1', foo='FOO') - n2 = om.Object('dumptests.DumpTarget1', foo='Bar') - source = om.Object('dumptests.DumpTarget3', a=n1, b=n2) - result = self._runner.testDump(source) - res = self._get_body(result) - self._get_body(res['a']) - self.assertIsInstance(res['b'], str) - - def test_dump_with_inheritance(self): - source = om.Object('dumptests.DumpTarget4', foo='FOO', qux='QUX') - result = self._runner.testDump(source) - res = self._get_body(result) - self.assertEqual('FOO', res['foo']) - self.assertEqual('QUX', res['qux']) - - def test_dump_with_inheritance_upcast_ignored(self): - source = om.Object('dumptests.DumpTarget4', foo='FOO', qux='QUX') - result = self._runner.testDumpWithUpcast(source, True, True) - res = self._get_body(result) - self.assertEqual('FOO', res['foo']) - self.assertEqual('QUX', res['qux']) - - def test_dump_with_inheritance_upcast_allowed(self): - source = om.Object('dumptests.DumpTarget4', foo='FOO', qux='QUX') - result = self._runner.testDumpWithUpcast(source, True, False) - res = self._get_body(result) - self.assertEqual('FOO', res['foo']) - self.assertNotIn('qux', res) - - def _get_body(self, obj): - body_key = [k for k in obj.keys() - if k not in ('id', 'name', 'metadata')][0] - self.assertIsInstance(body_key, dsl_types.MuranoType) - return obj[body_key] diff --git a/murano/tests/unit/dsl/test_engine_yaql_functions.py b/murano/tests/unit/dsl/test_engine_yaql_functions.py deleted file mode 100644 index 7f3e5b278..000000000 --- a/murano/tests/unit/dsl/test_engine_yaql_functions.py +++ /dev/null @@ -1,276 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 testtools import matchers -from unittest import mock -from yaql.language import exceptions as yaql_exceptions - -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - -from castellan.common import exception as castellan_exception - - -class TestEngineYaqlFunctions(test_case.DslTestCase): - def setUp(self): - super(TestEngineYaqlFunctions, self).setUp() - self._runner = self.new_runner(om.Object('TestEngineFunctions')) - - def test_join(self): - self.assertEqual('xx 123', self._runner.testJoin()) - - def test_split(self): - self.assertEqual( - ['x', 'yy', '123'], - self._runner.testSplit()) - - def test_len(self): - self.assertEqual(8, self._runner.testLen()) - - def test_coalesce(self): - self.assertEqual('a', self._runner.testCoalesce('a', 'b', 'c')) - self.assertEqual('b', self._runner.testCoalesce(None, 'b', 'c')) - self.assertEqual('c', self._runner.testCoalesce(None, None, 'c')) - - def test_base64(self): - encoded = 'VEVTVA==' - self.assertEqual( - encoded, - self._runner.testBase64Encode('TEST')) - self.assertEqual( - 'TEST', - self._runner.testBase64Decode(encoded)) - - def test_format(self): - self.assertEqual( - '2 + 3', - self._runner.testFormat('{0} + {1}', 2, 3)) - - def test_replace_str(self): - self.assertEqual( - 'John Doe', - self._runner.testReplaceStr('John Kennedy', 'Kennedy', 'Doe')) - - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testReplaceStr, None, 'Kennedy', 'Doe') - - def test_replace_dict(self): - self.assertEqual( - 'Marilyn Monroe', - self._runner.testReplaceDict('John Kennedy', { - 'John': 'Marilyn', - 'Kennedy': 'Monroe' - })) - - def test_lower(self): - self.assertEqual( - 'test', - self._runner.testToLower('TESt')) - - def test_upper(self): - self.assertEqual( - 'TEST', - self._runner.testToUpper('tEst')) - - def test_starts_with(self): - self.assertIs( - True, - self._runner.testStartsWith('TEST', 'TE')) - self.assertIs( - False, - self._runner.testStartsWith('TEST', 'te')) - - def test_ends_with(self): - self.assertIs( - True, - self._runner.testEndsWith('TEST', 'ST')) - self.assertIs( - False, - self._runner.testEndsWith('TEST', 'st')) - - def test_trim(self): - self.assertEqual( - 'test', - self._runner.testTrim('\n\t test \t\n')) - - def test_substr(self): - self.assertEqual( - 'teststr', - self._runner.testSubstr('teststr', 2, 3)) - - def test_str(self): - self.assertEqual( - '123', - self._runner.testStr(123)) - self.assertEqual( - 'true', - self._runner.testStr(True)) - self.assertEqual( - 'false', - self._runner.testStr(False)) - self.assertEqual( - 'null', - self._runner.testStr(None)) - - def test_int(self): - self.assertEqual( - 123, - self._runner.testInt('123')) - self.assertEqual( - 0, - self._runner.testInt(None)) - - def test_keys(self): - self.assertThat( - self._runner.testKeys({True: 123, 5: 'Q', 'y': False}), - matchers.MatchesSetwise( - matchers.Equals('y'), - matchers.Is(True), - matchers.Equals(5))) - - def test_values(self): - self.assertThat( - self._runner.testValues({True: 123, 5: 'Q', 'y': False}), - matchers.MatchesSetwise( - matchers.Is(False), - matchers.Equals(123), - matchers.Equals('Q'))) - - def test_flatten(self): - self.assertEqual( - [1, 2, 3, 4], - self._runner.testFlatten([[1, 2], [3, 4]])) - - def test_dict_get(self): - self.assertEqual( - 2, - self._runner.testDictGet({'a': 'x', 'y': 2}, 'y')) - - def test_random_name(self): - name1 = self._runner.testRandomName() - name2 = self._runner.testRandomName() - - self.assertIsInstance(name1, str) - self.assertIsInstance(name2, str) - self.assertThat(len(name1), matchers.GreaterThan(12)) - self.assertThat(len(name2), matchers.GreaterThan(12)) - self.assertThat(name1, matchers.NotEquals(name2)) - - def test_pselect(self): - self.assertEqual( - [1, 4, 9, 16, 25, 36, 49, 64, 81, 100], - self._runner.testPSelect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) - - def test_bind(self): - self.assertEqual( - { - 'a': 5, - 'b': True, - 'c': { - 'd': 'x' - }, - 'e': ['qq-123'] - }, - self._runner.testBind( - { - 'a': '$a', - '$Z': True, - 'c': { - 'd': '$value of d' - }, - 'e': ['$qq-{suffix}'] - }, - {'a': 5, 'Z': 'b', 'value of d': 'x', 'suffix': 123})) - - def test_patch(self): - self.assertEqual( - {'foo': 'bar', 'baz': [42]}, - self._runner.testPatch()) - - def test_skip(self): - self.assertEqual( - [3, 4, 5, 6], - self._runner.testSkip([1, 2, 3, 4, 5, 6], 2) - ) - - def test_take(self): - self.assertEqual( - [1, 2, 3], - self._runner.testTake([1, 2, 3, 4, 5, 6], 3) - ) - - def test_skip_take(self): - self.assertEqual( - [3, 4, 5], - self._runner.testSkipTake([1, 2, 3, 4, 5, 6, 7, 8], - 2, 3) - ) - - def test_skip_take_chained(self): - self.assertEqual( - [3, 4, 5], - self._runner.testSkipTakeChained( - [1, 2, 3, 4, 5, 6, 7, 8], - 2, 3) - ) - - def test_aggregate(self): - self.assertEqual( - 10, - self._runner.testAggregate([1, 2, 3, 4]) - ) - - def test_aggregate_with_initializer(self): - self.assertEqual( - 21, - self._runner.testAggregateWithInitializer([1, 2, 3, 4], 11) - ) - - def test_id(self): - obj_id = self._runner.root.object_id - self.assertEqual(obj_id * 2, self._runner.testId()) - - def test_type(self): - self.assertEqual('TestEngineFunctions' * 2, self._runner.testType()) - - def test_is_operator(self): - self.assertTrue(self._runner.testIsOperator()) - self.assertFalse(self._runner.testNegativeIsOperator()) - - def test_new_object_assignment(self): - self.assertTrue(self._runner.testNewObjectAssignment()) - - @mock.patch('murano.engine.system.yaql_functions.key_manager') - @mock.patch('murano.engine.system.yaql_functions.castellan_utils') - def test_decrypt_data(self, mock_castellan_utils, mock_key_manager): - dummy_context = mock.MagicMock() - mock_castellan_utils.credential_factory.return_value = dummy_context - - encrypted_value = '91f784d0-5ef1-4b6f-9311-9b5a33d828d8' - decrypted_value = 'secret_password' - - mock_key_manager.API().get.return_value.get_encoded.return_value =\ - decrypted_value - self.assertEqual(decrypted_value, - self._runner.testDecryptData(encrypted_value)) - mock_key_manager.API().get.assert_called_once_with(dummy_context, - encrypted_value) - - @mock.patch('murano.engine.system.yaql_functions.LOG') - def test_decrypt_data_not_configured(self, mock_log): - encrypted_value = '91f784d0-5ef1-4b6f-9311-9b5a33d828d8' - self.assertRaises(castellan_exception.AuthTypeInvalidError, - self._runner.testDecryptData, encrypted_value) - mock_log.error.assert_called() diff --git a/murano/tests/unit/dsl/test_exceptions.py b/murano/tests/unit/dsl/test_exceptions.py deleted file mode 100644 index 703563d5d..000000000 --- a/murano/tests/unit/dsl/test_exceptions.py +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 inspect -import os.path -import re - -from testtools import matchers - -from murano.dsl import dsl_exception -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestExceptions(test_case.DslTestCase): - def setUp(self): - super(TestExceptions, self).setUp() - - def exception_func(): - exc = LookupError('just random Python exception') - frameinfo = inspect.getframeinfo(inspect.currentframe()) - exc._position = \ - os.path.basename(frameinfo.filename), frameinfo.lineno + 4 - # line below must be exactly 4 lines after currentframe() - raise exc - - self.register_function(exception_func, 'raisePythonException') - self._runner = self.new_runner(om.Object('ExceptionHandling')) - - def test_throw_catch(self): - self._runner.testThrow(1) - self.assertEqual( - ['enter try', u'exception message', 'finally section'], - self.traces) - - def test_rethrow(self): - e = self.assertRaises( - dsl_exception.MuranoPlException, - self._runner.testThrow, 2) - - self.assertEqual(['anotherExceptionName'], e.names) - self.assertEqual('exception message 2', e.message) - self.assertEqual('[anotherExceptionName]: exception message 2', str(e)) - - self.assertEqual( - ['enter try', 'exception message 2', - 'rethrow', 'finally section'], - self.traces) - - def test_catch_all_catch(self): - self._runner.testThrow(3) - self.assertEqual( - ['enter try', 'catch all', - 'exception message 3', 'finally section'], - self.traces) - - def test_no_throw(self): - self._runner.testThrow(4) - self.assertEqual( - ['enter try', 'exit try', 'else section', 'finally section'], - self.traces) - - def test_stack_trace(self): - self._runner.preserve_exception = True - e = self.assertRaises( - dsl_exception.MuranoPlException, - self._runner.testStackTrace) - call_stack = e.format() - self.assertThat( - call_stack, - matchers.StartsWith( - 'LookupError: just random Python exception')) - - self.assertIsInstance(e.original_exception, LookupError) - - filename, line = e.original_exception._position - self.assertThat( - call_stack, - matchers.MatchesRegex( - r'.*^ File \".*ExceptionHandling\.yaml\", ' - r'line \d+:\d+ in method testStackTrace .*' - r'of type ExceptionHandling$.*' - r'^ File \".*{0}\", line {1} ' - r'in method exception_func$.*'.format(filename, line), - re.MULTILINE | re.DOTALL)) diff --git a/murano/tests/unit/dsl/test_execution.py b/murano/tests/unit/dsl/test_execution.py deleted file mode 100644 index 2a94c05d1..000000000 --- a/murano/tests/unit/dsl/test_execution.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl_exception -from murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestExecution(test_case.DslTestCase): - def _load(self): - return self.new_runner( - om.Object('SampleClass1', stringProperty='STRING', - classProperty=om.Object( - 'SampleClass2', class2Property='ANOTHER_STRING'))) - - def test_load(self): - self._load() - - def test_load_failure(self): - self.assertRaises( - exceptions.ContractViolationException, - self.new_runner, - om.Object('SampleClass1')) - - def test_trace(self): - runner = self._load() - self.assertEqual([], self.traces) - runner.testTrace(123) - self.assertEqual([123, 'STRING', 'ANOTHER_STRING'], self.traces) - runner.testTrace(321) - self.assertEqual([123, 'STRING', 'ANOTHER_STRING', - 321, 'STRING', 'ANOTHER_STRING'], - self.traces) - - def test_exception(self): - class CustomException(Exception): - pass - - def raise_exception(): - raise CustomException() - - self.register_function(raise_exception, 'raiseException') - runner = self._load() - self.assertRaises(CustomException, runner.testException) - runner.preserve_exception = True - self.assertRaises(dsl_exception.MuranoPlException, - runner.testException) - - def test_return(self): - self.assertEqual(3, self._load().testReturn(3)) diff --git a/murano/tests/unit/dsl/test_extension_methods.py b/murano/tests/unit/dsl/test_extension_methods.py deleted file mode 100644 index 4ee80afb4..000000000 --- a/murano/tests/unit/dsl/test_extension_methods.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 yaql.language import exceptions -from yaql.language import specs -from yaql.language import yaqltypes - -from murano.dsl import dsl -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestExtensionMethods(test_case.DslTestCase): - def setUp(self): - @dsl.name('extcls.Extender') - class PythonClass(object): - def __init__(self, arg): - self.value = arg - - @staticmethod - @specs.meta('Usage', 'Extension') - @specs.parameter('arg', yaqltypes.Integer()) - def python_extension(arg): - return arg * arg - - @classmethod - @specs.meta('Usage', 'Extension') - @specs.parameter('arg', yaqltypes.Integer()) - def python_extension2(cls, arg): - return cls(2 * arg).value - - super(TestExtensionMethods, self).setUp() - self.package_loader.load_class_package( - 'extcls.Extender', None).register_class(PythonClass) - - self._runner = self.new_runner(om.Object('extcls.TestClass')) - - def test_call_self_extension_method(self): - self.assertEqual([123, 123], self._runner.testSelfExtensionMethod()) - - def test_call_imported_extension_method(self): - self.assertEqual( - [246, 246], self._runner.testImportedExtensionMethod()) - - def test_call_nullable_extension_method(self): - self.assertEqual( - [123, None], self._runner.testNullableExtensionMethod()) - - def test_extensions_precedence(self): - self.assertEqual(111, self._runner.testExtensionsPrecedence()) - - def test_explicit_call(self): - self.assertEqual(222, self._runner.testCallExtensionExplicitly()) - - def test_explicit_call_on_instance_fails(self): - self.assertRaises( - exceptions.NoMatchingMethodException, - self._runner.testExplicitCallDoenstWorkOnInstance) - - def test_call_on_primitive_types(self): - self.assertEqual('qWERTy', self._runner.testCallOnPrimitiveTypes()) - - def test_call_python_extension(self): - self.assertEqual(16, self._runner.testCallPythonExtension()) - - def test_call_python_extension_explicitly(self): - self.assertEqual(25, self._runner.testCallPythonExtensionExplicitly()) - - def test_call_python_classmethod_extension(self): - self.assertEqual(14, self._runner.testCallPythonClassmethodExtension()) diff --git a/murano/tests/unit/dsl/test_find_class.py b/murano/tests/unit/dsl/test_find_class.py deleted file mode 100644 index e1319f8e2..000000000 --- a/murano/tests/unit/dsl/test_find_class.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 warnings - -from murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestFindClass(test_case.DslTestCase): - - def setUp(self): - super(TestFindClass, self).setUp() - self._runner = self.new_runner(om.Object('TestFindClass')) - - def test_find_class_with_prefix(self): - with warnings.catch_warnings(record=True) as capture: - self.assertIsNone(self._runner.testFindClassWithPrefix()) - self.assertEqual(DeprecationWarning, capture[-1].category) - observed = capture[-1].message - expected = ("Plugin io.murano.extensions.io.murano.test.TestFixture " - "was not found, but a io.murano.test.TestFixture was " - "found instead and will be used. This could be caused by " - "recent change in plugin naming scheme. If you are " - "developing applications targeting this plugin consider " - "changing its name") - self.assertEqual(expected, str(observed)) - - def test_find_class_short_name(self): - self.assertIsNone(self._runner.testFindClassShortName()) - - def test_class_with_prefix_not_found(self): - observed = self.assertRaises(exceptions.NoClassFound, - self._runner.testClassWithPrefixNotFound) - expected = ('Class "io.murano.extensions.io.murano.test.TestFixture1" ' - 'is not found in io.murano/0.0.0, tests/0.0.0') - self.assertEqual(expected, str(observed)) diff --git a/murano/tests/unit/dsl/test_gc.py b/murano/tests/unit/dsl/test_gc.py deleted file mode 100644 index 92573244b..000000000 --- a/murano/tests/unit/dsl/test_gc.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 murano.dsl import exceptions -from murano.dsl.principal_objects import garbage_collector -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestGC(test_case.DslTestCase): - def setUp(self): - super(TestGC, self).setUp() - - self.package_loader.load_package('io.murano', None).register_class( - garbage_collector.GarbageCollector) - self.runner = self.new_runner(om.Object('TestGC')) - - def test_model_destroyed(self): - model = om.Object( - 'TestGCNode', 'root', - value='root', - nodes=[ - om.Object( - 'TestGCNode', 'node1', - value='node1', - nodes=['root', 'node2'] - ), - om.Object( - 'TestGCNode', 'node2', - value='node2', - nodes=['root', 'node1'] - ), - ] - ) - model = {'Objects': None, 'ObjectsCopy': model} - self.new_runner(model) - self.assertCountEqual(['node1', 'node2'], self.traces[:2]) - self.assertEqual('root', self.traces[-1]) - - def test_collect_from_code(self): - self.runner.testObjectsCollect() - self.assertEqual(['B', 'A'], self.traces) - - def test_collect_with_subscription(self): - self.runner.testObjectsCollectWithSubscription() - self.assertEqual( - ['Destroy A', 'Destroy B', 'Destruction of B', 'B', 'A'], - self.traces) - - def test_call_on_destroyed_object(self): - self.assertRaises( - exceptions.ObjectDestroyedError, - self.runner.testCallOnDestroyedObject) - self.assertEqual(['foo', 'X'], self.traces) - - def test_destruction_dependencies_serialization(self): - self.runner.testDestructionDependencySerialization() - node1 = self.runner.serialized_model['Objects']['outNode'] - node2 = node1['nodes'][0] - - deps = { - 'onDestruction': [{ - 'subscriber': self.runner.root.object_id, - 'handler': '_handler' - }] - } - self.assertEqual(deps, node1['?'].get('dependencies')) - - self.assertEqual( - node1['?'].get('dependencies'), - node2['?'].get('dependencies')) - - model = self.runner.serialized_model - model['Objects']['outNode'] = None - self.new_runner(model) - self.assertEqual(['Destroy A', 'Destroy B', 'B', 'A'], self.traces) - - def test_is_doomed(self): - self.runner.testIsDoomed() - self.assertEqual([[], True, 'B', [True], False, 'A'], self.traces) - - def test_is_destroyed(self): - self.runner.testIsDestroyed() - self.assertEqual([False, True], self.traces) - - def test_static_property_not_destroyed(self): - self.runner.testStaticProperties() - self.assertEqual([], self.traces) - - def test_args_not_destroyed(self): - self.runner.testDestroyArgs() - self.assertEqual([], self.traces) - - def test_runtime_property_not_destroyed(self): - self.runner.testReachableRuntimeProperties() - self.assertEqual([False, ], self.traces) diff --git a/murano/tests/unit/dsl/test_helpers.py b/murano/tests/unit/dsl/test_helpers.py deleted file mode 100644 index ec5ff2050..000000000 --- a/murano/tests/unit/dsl/test_helpers.py +++ /dev/null @@ -1,430 +0,0 @@ -# Licensed 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 copy -import semantic_version -import types -from unittest import mock -import weakref - -from oslo_utils.uuidutils import generate_uuid - -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.dsl import helpers -from murano.tests.unit import base - - -class TestDSLHelpers(base.MuranoTestCase): - @mock.patch.object(helpers, 'with_object_store', autospec=True) - def test_parallel_select_except_exception(self, mock_with_object_store): - mock_with_object_store.side_effect = ValueError - self.assertRaises(ValueError, helpers.parallel_select, - [mock.sentinel.foo], lambda: None) - - def test_enum(self): - self.assertEqual('Enum', helpers.enum().__name__) - - def test_cast_with_murano_type(self): - mock_attrs = { - 'name': mock.sentinel.class_type, - 'version': semantic_version.Version('1.0.0'), - 'ancestors.return_value': [] - } - mock_type = mock.Mock() - mock_type.configure_mock(**mock_attrs) - mock_obj = mock.Mock(type=mock_type) - mock_obj.cast.return_value = mock.sentinel.foo_cast_value - mock_murano_class = mock.Mock(spec=dsl_types.MuranoType) - mock_murano_class.name = mock.sentinel.class_type - mock_murano_class.version = semantic_version.Version('1.0.0') - - result = helpers.cast(mock_obj, mock_murano_class, - pov_or_version_spec=None) - self.assertEqual(mock.sentinel.foo_cast_value, result) - mock_obj.cast.assert_called_once_with(mock_type) - - def test_cast_except_value_error(self): - mock_attrs = { - 'name': mock.sentinel.class_type, - 'version': semantic_version.Version('1.0.0'), - 'ancestors.return_value': [] - } - mock_type = mock.Mock() - mock_type.configure_mock(**mock_attrs) - mock_obj = mock.Mock(type=mock_type) - mock_murano_class = mock.Mock(spec=dsl_types.MuranoType) - mock_murano_class.name = mock.sentinel.class_type - - e = self.assertRaises(ValueError, helpers.cast, mock_obj, - mock_murano_class, - pov_or_version_spec=mock.Mock()) - self.assertEqual('pov_or_version_spec of unsupported type {0}' - .format(type(mock.Mock())), str(e)) - - def test_cast_except_no_class_found(self): - mock_attrs = { - 'name': mock.sentinel.name, - 'package.name': mock.sentinel.package_name, - 'version': mock.sentinel.version, - 'ancestors.return_value': [] - } - mock_type = mock.Mock() - mock_type.configure_mock(**mock_attrs) - mock_obj = mock.Mock(type=mock_type) - mock_murano_class = mock.Mock(spec=dsl_types.MuranoTypeReference) - mock_murano_class.type = mock.sentinel.foo_class - mock_version_spec = mock.Mock(spec=dsl_types.MuranoPackage) - - e = self.assertRaises(exceptions.NoClassFound, helpers.cast, mock_obj, - mock_murano_class, - pov_or_version_spec=mock_version_spec) - self.assertIn('Class "sentinel.foo_class" is not found', str(e)) - - def test_cast_except_ambiguous_class_name(self): - mock_attrs = { - 'name': mock.sentinel.class_type, - 'version': semantic_version.Version('1.0.0') - } - mock_ancestor = mock.Mock() - mock_ancestor.configure_mock(**mock_attrs) - mock_attrs['ancestors.return_value'] = [mock_ancestor] - mock_type = mock.Mock() - mock_type.configure_mock(**mock_attrs) - mock_obj = mock.Mock(type=mock_type) - mock_murano_class = mock.Mock(spec=dsl_types.MuranoTypeReference) - mock_murano_class.type = mock.sentinel.class_type - - # pov_or_version_spec of '1' will be converted to - # semantic_version.Spec('>=1.0.0,<2.0.0-0') - self.assertRaises(exceptions.AmbiguousClassName, helpers.cast, - mock_obj, mock_murano_class, pov_or_version_spec='1') - - def test_inspect_is_method(self): - mock_cls = mock.Mock(foo=lambda: None, bar=None) - self.assertTrue(helpers.inspect_is_method(mock_cls, 'foo')) - self.assertFalse(helpers.inspect_is_method(mock_cls, 'bar')) - - def test_inspect_is_property(self): - data_descriptor = mock.MagicMock(__get__=None, __set__=None) - mock_cls = mock.Mock(foo=data_descriptor, bar=None) - self.assertTrue(helpers.inspect_is_property(mock_cls, 'foo')) - self.assertFalse(helpers.inspect_is_property(mock_cls, 'bar')) - - def test_updated_dict(self): - dict_ = {'foo': 'bar'} - self.assertEqual(dict_, helpers.updated_dict(dict_, {})) - - def test_updated_dict_with_null_arg(self): - dict_ = {'foo': 'bar'} - self.assertEqual(dict_, helpers.updated_dict(None, dict_)) - - def test_resolve_with_return_reference_true(self): - mock_value = mock.Mock(spec=dsl_types.MuranoTypeReference) - mock_scope_type = mock.Mock(spec=dsl_types.MuranoTypeReference) - result = helpers.resolve_type(mock_value, mock_scope_type, True) - self.assertEqual(mock_value, result) - - mock_value = mock.Mock() - mock_value.get_reference.return_value = mock.sentinel.foo_reference - mock_scope_type = mock.Mock() - mock_scope_type.package.find_class.return_value = mock_value - result = helpers.resolve_type(mock_value, mock_scope_type, True) - self.assertEqual(mock.sentinel.foo_reference, result) - - def test_resolve_type_with_null_value(self): - self.assertIsNone(helpers.resolve_type(None, None)) - - def test_assemble_object_definition(self): - test_parsed = { - 'type': mock.sentinel.type, - 'properties': {}, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'destroyed': True, - 'extra': {} - } - expected = { - '?': { - 'type': mock.sentinel.type, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'destroyed': True - } - } - - result = helpers.assemble_object_definition(test_parsed) - for key, val in expected.items(): - self.assertEqual(val, result[key]) - - @mock.patch.object(helpers, 'format_type_string', autospec=True) - def test_assemble_object_definition_with_serializable_model_format( - self, mock_format_type_string): - mock_format_type_string.return_value = mock.sentinel.type - - test_parsed = { - 'type': mock.sentinel.type, - 'properties': {}, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'destroyed': True, - 'extra': {} - } - expected = { - '?': { - 'type': mock.sentinel.type, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'destroyed': True - } - } - model_format = dsl_types.DumpTypes.Serializable - - result = helpers.assemble_object_definition(test_parsed, model_format) - for key, val in expected['?'].items(): - self.assertEqual(val, result['?'][key]) - - def test_assemble_object_definition_with_inline_model_format(self): - test_parsed = { - 'type': mock.sentinel.type, - 'properties': mock.sentinel.properties, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'dependencies': mock.sentinel.dependencies, - 'destroyed': mock.sentinel.destroyed, - 'extra': {} - } - model_format = dsl_types.DumpTypes.Inline - expected = copy.copy(test_parsed) - expected[mock.sentinel.type] = mock.sentinel.properties - for key in ['type', 'extra', 'properties']: - expected.pop(key) - - result = helpers.assemble_object_definition(test_parsed, model_format) - for key, val in expected.items(): - self.assertEqual(val, result[key]) - - def test_assemble_object_definition_except_value_error(self): - test_parsed = { - 'type': mock.sentinel.type, - 'properties': {}, - 'id': mock.sentinel.id, - 'name': mock.sentinel.name, - 'metadata': mock.sentinel.metadata, - 'destroyed': True, - 'extra': {} - } - e = self.assertRaises(ValueError, helpers.assemble_object_definition, - test_parsed, None) - self.assertEqual('Invalid Serialization Type', str(e)) - - def test_function(self): - def f(): - return - self.assertTrue(isinstance(helpers.function(f), types.FunctionType)) - - def test_function_from_method(self): - class C: - def m(self): - return - c = C() - self.assertTrue(isinstance(helpers.function(c.m), types.FunctionType)) - - def test_weak_proxy(self): - self.assertIsNone(helpers.weak_proxy(None)) - - def test_weak_proxy_with_reference_type(self): - result = helpers.weak_proxy(weakref.ReferenceType(int)) - self.assertEqual('int', result.__name__) - - @mock.patch.object(helpers, 'get_object_store', autospec=True) - def test_weak_ref(self, mock_get_object_store): - mock_object_store = mock.Mock( - **{'get.return_value': mock.sentinel.res}) - mock_get_object_store.return_value = mock_object_store - - test_obj = dsl_types.MuranoObject() - setattr(test_obj, 'object_id', generate_uuid()) - murano_object_weak_ref = helpers.weak_ref(test_obj) - setattr(murano_object_weak_ref, 'ref', lambda *args: None) - result = murano_object_weak_ref.__call__() - - self.assertEqual(mock.sentinel.res, result) - self.assertEqual(weakref.ReferenceType.__name__, - murano_object_weak_ref.ref.__class__.__name__) - - def test_weak_ref_with_null_obj(self): - self.assertIsNone(helpers.weak_ref(None)) - - @mock.patch.object(helpers, 're', autospec=True) - def test_parse_type_string_with_null_res(self, mock_re): - mock_re.compile.return_value = mock.Mock( - **{'match.return_value': None}) - self.assertIsNone(helpers.parse_type_string('', None, None)) - - def test_format_type_string(self): - inner_type_obj = mock.Mock(spec=dsl_types.MuranoType) - inner_type_obj.configure_mock(**{'name': 'foo', 'version': 'foo_ver'}) - inner_type_obj_pkg = mock.Mock() - inner_type_obj_pkg.configure_mock(name='foo_pkg') - setattr(inner_type_obj, 'package', inner_type_obj_pkg) - type_obj = mock.Mock(spec=dsl_types.MuranoTypeReference, - type=inner_type_obj) - result = helpers.format_type_string(type_obj) - self.assertEqual('foo/foo_ver@foo_pkg', result) - - def test_format_type_string_except_value_error(self): - type_obj = mock.Mock(spec=dsl_types.MuranoTypeReference, type=None) - e = self.assertRaises(ValueError, helpers.format_type_string, type_obj) - self.assertEqual('Invalid argument', str(e)) - - def test_patch_dict(self): - path = 'foo.bar.baz' - fake_dict = mock.MagicMock(spec=dict) - # Make the dict return itself to test whether all the parts are called. - fake_dict.get.return_value = fake_dict - helpers.patch_dict(fake_dict, path, None) - fake_dict.get.assert_has_calls([mock.call('foo'), mock.call('bar')]) - fake_dict.pop.assert_not_called() - - def test_patch_dict_without_dict(self): - path = 'foo.bar.baz' - not_a_dict = mock.Mock() - helpers.patch_dict(not_a_dict, path, None) - not_a_dict.get.assert_not_called() - not_a_dict.pop.assert_not_called() - - @mock.patch.object(helpers, 'gc') - def test_walk_gc_with_towards_true(self, mock_gc, autospec=True): - mock_gc.get_referrers.side_effect = [ - [mock.sentinel.second], [mock.sentinel.third] - ] - first_obj = mock.sentinel.first - handler = mock.MagicMock() - handler.return_value = True - - expected = [ - [mock.sentinel.first], - [mock.sentinel.first, mock.sentinel.second], - [mock.sentinel.first, mock.sentinel.second, mock.sentinel.third] - ] - actual = [] - for obj in helpers.walk_gc(first_obj, True, handler): - actual.append(obj) - self.assertEqual(expected, actual) - - @mock.patch.object(helpers, 'gc', autospec=True) - def test_walk_gc_with_towards_false(self, mock_gc): - mock_gc.get_referents.side_effect = [ - # Trigger the continue by duplicating entries. - [mock.sentinel.second], [mock.sentinel.second] - ] - first_obj = mock.sentinel.first - handler = mock.MagicMock() - handler.return_value = True - - expected = [ - [mock.sentinel.first], - [mock.sentinel.second, mock.sentinel.first] - ] - actual = [] - for obj in helpers.walk_gc(first_obj, False, handler): - actual.append(obj) - self.assertEqual(expected, actual) - - -class TestMergeDicts(base.MuranoTestCase): - def check(self, dict1, dict2, expected): - result = helpers.merge_dicts(dict1, dict2) - self.assertEqual(expected, result) - - def test_dicts_plain(self): - dict1 = {"a": "1"} - dict2 = {"a": "100", "ab": "12"} - expected = {"a": "100", "ab": "12"} - self.check(dict1, dict2, expected) - - def test_different_types_none(self): - dict1 = {"a": "1"} - dict2 = {"a": None, "ab": "12"} - expected = {"a": "1", "ab": "12"} - self.check(dict1, dict2, expected) - - def test_different_types_of_iterable(self): - dict1 = {"a": {"ab": "1"}} - dict2 = {"a": ["ab", "1"]} - self.assertRaises(TypeError, helpers.merge_dicts, dict1, dict2) - - def test_merge_nested_dicts(self): - dict1 = {"a": {"ab": {}, "abc": "1"}} - dict2 = {"a": {"abc": "123"}} - expected = {"a": {"ab": {}, "abc": "123"}} - self.check(dict1, dict2, expected) - - def test_merge_nested_dicts_with_max_levels(self): - dict1 = {"a": {"ab": {"abcd": "1234"}, "abc": "1"}} - dict2 = {"a": {"ab": {"y": "9"}, "abc": "123"}} - expected = {"a": {"ab": {"y": "9"}, "abc": "123"}} - result = helpers.merge_dicts(dict1, dict2, max_levels=2) - self.assertEqual(expected, result) - - def test_merge_with_lists(self): - dict1 = {"a": [1, 2]} - dict2 = {"a": [1, 3, 2, 4]} - expected = {"a": [1, 2, 3, 4]} - self.check(dict1, dict2, expected) - - -class TestParseVersionSpec(base.MuranoTestCase): - def check(self, expected, version_spec): - self.assertEqual(expected, helpers.parse_version_spec(version_spec)) - - def test_empty_version_spec(self): - version_spec = "" - expected = semantic_version.Spec('>=0.0.0', '<1.0.0') - self.check(expected, version_spec) - - def test_empty_kind(self): - version_spec = "1.11.111" - expected = semantic_version.Spec('==1.11.111') - self.check(expected, version_spec) - - def test_implicit_major(self): - version_spec = ">=2" - expected = semantic_version.Spec('>=2.0.0') - self.check(expected, version_spec) - - def test_implicit_minor(self): - version_spec = ">=2.1" - expected = semantic_version.Spec('>=2.1.0') - self.check(expected, version_spec) - - def test_remove_spaces(self): - version_spec = "< = 2 .1" - expected = semantic_version.Spec('<2.2.0') - self.check(expected, version_spec) - - def test_input_version(self): - version_spec = semantic_version.Version('1.11.111') - expected = semantic_version.Spec('==1.11.111') - self.check(expected, version_spec) - - def test_input_spec(self): - version_spec = semantic_version.Spec('<=1', '<=1.11') - expected = semantic_version.Spec('<1.12.0', '<2.0.0') - self.check(expected, version_spec) diff --git a/murano/tests/unit/dsl/test_logger.py b/murano/tests/unit/dsl/test_logger.py deleted file mode 100644 index c3143f45f..000000000 --- a/murano/tests/unit/dsl/test_logger.py +++ /dev/null @@ -1,140 +0,0 @@ -# coding: utf-8 -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 unittest import mock - -from murano.dsl import helpers -from murano.engine.system import logger -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestLogger(test_case.DslTestCase): - - FORMAT_CALLS = [ - mock.call(mock.ANY, 'str', (), {}), - mock.call(mock.ANY, u'тест', (), {}), - mock.call(mock.ANY, 'str', (1,), {}), - mock.call(mock.ANY, 'str {0}', ('message',), {}), - mock.call(mock.ANY, 'str {message}', (), {'message': 'message'}), - mock.call(mock.ANY, 'str {message}{0}', (), {})] - - LOG_CALLS = FORMAT_CALLS - - def setUp(self): - super(TestLogger, self).setUp() - self._runner = self.new_runner(om.Object('TestLogger')) - self.package_loader.load_package('io.murano', None).register_class( - logger.Logger) - - def test_create(self): - logger_instance = self._runner.testCreate() - self.assertTrue( - helpers.is_instance_of(logger_instance, 'io.murano.system.Logger'), - 'Function should return io.murano.system.Logger instance') - - def _create_logger_mock(self): - logger_instance = self._runner.testCreate() - logger_ext = logger_instance.extension - - underlying_logger_mock = mock.MagicMock() - logger_ext._underlying_logger = underlying_logger_mock - logger_ext._underlying_logger.return_value = None - - format_mock = mock.MagicMock(return_value='format_mock') - # do not verify number of conversions to string - format_mock.__str__ = mock.MagicMock(return_value='format_mock') - format_mock.__unicode__ = mock.MagicMock(return_value='format_mock') - - logger_ext._format_without_exceptions = format_mock - - return logger_instance, format_mock, underlying_logger_mock - - def test_trace(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.trace = log_method - self._runner.testTrace(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) - - def test_debug(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.debug = log_method - self._runner.testDebug(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) - - def test_info(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.info = log_method - self._runner.testInfo(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) - - def test_warning(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.warning = log_method - self._runner.testWarning(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) - - def test_error(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.error = log_method - self._runner.testError(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.FORMAT_CALLS)) - - def test_critical(self): - logger_instance, format_mock, underlying_logger_mock \ - = self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.critical = log_method - self._runner.testCritical(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) - - def test_exception(self): - logger_instance, format_mock, underlying_logger_mock = \ - self._create_logger_mock() - log_method = mock.MagicMock() - - underlying_logger_mock.error = log_method - self._runner.testException(logger_instance) - - format_mock.assert_has_calls(self.FORMAT_CALLS, any_order=False) - self.assertEqual(log_method.call_count, len(self.LOG_CALLS)) diff --git a/murano/tests/unit/dsl/test_macros.py b/murano/tests/unit/dsl/test_macros.py deleted file mode 100644 index dbc815bba..000000000 --- a/murano/tests/unit/dsl/test_macros.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 testtools import matchers - -from murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestMacros(test_case.DslTestCase): - def setUp(self): - super(TestMacros, self).setUp() - self._runner = self.new_runner(om.Object('MacroExamples')) - - def test_if(self): - self.assertEqual('gt', self._runner.testIf(6)) - self.assertEqual('def', self._runner.testIf(4)) - self.assertEqual('gt', self._runner.testIfElse(6)) - self.assertEqual('lt', self._runner.testIfElse(4)) - - def test_if_non_boolean(self): - self.assertEqual(1100, self._runner.testIfNonBoolean()) - - def test_while(self): - self.assertEqual(0, self._runner.testWhile(3)) - self.assertEqual([3, 2, 1], self.traces) - - def test_while_non_boolean(self): - self.assertEqual([], self._runner.testWhileNonBoolean()) - - def test_for(self): - self.assertIsNone(self._runner.testFor()) - self.assertEqual(['x', 'y', 'z', 2, 5, 10], self.traces) - - def test_repeat(self): - self._runner.testRepeat(4) - self.assertEqual(['run', 'run', 'run', 'run'], self.traces) - - def test_break(self): - self.assertRaises(exceptions.DslInvalidOperationError, - self._runner.testBreak) - self.assertEqual([0, 1, 2, 'breaking', 'method_break'], self.traces) - - def test_continue(self): - self.assertRaises(exceptions.DslInvalidOperationError, - self._runner.testContinue) - self.assertEqual([0, 1, 2, 5, 6, 'method_continue'], self.traces) - - def test_match(self): - self.assertEqual('y', self._runner.testMatch(1)) - self.assertEqual('x', self._runner.testMatch(2)) - self.assertEqual('z', self._runner.testMatch(3)) - self.assertIsNone(self._runner.testMatch(0)) - self.assertEqual('y', self._runner.testMatchDefault(1)) - self.assertEqual('x', self._runner.testMatchDefault(2)) - self.assertEqual('z', self._runner.testMatchDefault(3)) - self.assertEqual('def', self._runner.testMatchDefault(0)) - - def test_switch(self): - self.assertIsNone(self._runner.testSwitch(20)) - self.assertEqual(['gt'], self.traces) - del self.traces - self.assertIsNone(self._runner.testSwitch(200)) - self.assertThat( - self.traces, - matchers.MatchesSetwise( - matchers.Equals('gt100'), matchers.Equals('gt'))) - del self.traces - self.assertIsNone(self._runner.testSwitch(2)) - self.assertEqual(['lt'], self.traces) - - def test_switch_with_default(self): - self.assertIsNone(self._runner.testSwitchDefault(20)) - self.assertEqual(['gt'], self.traces) - del self.traces - self.assertIsNone(self._runner.testSwitchDefault(200)) - self.assertThat( - self.traces, - matchers.MatchesSetwise( - matchers.Equals('gt100'), matchers.Equals('gt'))) - del self.traces - self.assertIsNone(self._runner.testSwitchDefault(-20)) - self.assertEqual(['lt'], self.traces) - del self.traces - self.assertIsNone(self._runner.testSwitchDefault(5)) - self.assertEqual(['def'], self.traces) - - def test_switch_non_boolean(self): - self.assertEqual(1110000, self._runner.testSwitchNonBoolean()) - - def test_code_block(self): - self.assertEqual(123, self._runner.testCodeBlock()) - self.assertEqual(['a', 123], self.traces) - - def test_parallel(self): - self.assertIsNone(self._runner.testParallel()) - self.assertEqual(['enter', 'enter', 'exit', 'exit'], self.traces) - - def test_parallel_with_limit(self): - self.assertIsNone(self._runner.testParallelWithLimit()) - self.assertEqual(['enter', 'enter', - 'exit', 'exit', - 'enter', 'exit'], self.traces) - - def test_scope_within_macro(self): - self.assertEqual( - 87654321, - self._runner.testScopeWithinMacro()) diff --git a/murano/tests/unit/dsl/test_meta.py b/murano/tests/unit/dsl/test_meta.py deleted file mode 100644 index f72e752b8..000000000 --- a/murano/tests/unit/dsl/test_meta.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestMeta(test_case.DslTestCase): - def setUp(self): - super(TestMeta, self).setUp() - self._runner = self.new_runner(om.Object('metatests.TestMeta')) - - def test_class_multi_meta(self): - self.assertCountEqual( - [4, 1, 111, 2], self._runner.testClassMultiMeta()) - - def test_class_single_meta(self): - self.assertCountEqual( - [5, 6], self._runner.testClassSingleMeta()) - - def test_parent_class_not_inherited_meta(self): - self.assertEqual(3, self._runner.testParentClassNotInheritedMeta()) - - def test_method_meta(self): - self.assertCountEqual( - [7, 8, 9, 4, 1, 10], self._runner.testMethodMeta()) - - def test_method_argument_meta(self): - self.assertCountEqual( - [1, 2, 3], self._runner.testMethodArgumentMeta()) - - def test_inherited_property_meta(self): - self.assertEqual( - [1], self._runner.testInheritedPropertyMeta()) - - def test_overridden_property_meta(self): - self.assertCountEqual( - [1, 4], self._runner.testOverriddenPropertyMeta()) - - def test_package_meta(self): - self.assertEqual( - [], self._runner.testPackageMeta()) - - def test_complex_meta(self): - self.assertCountEqual([ - [1, 'metatests.PropertyType'], - [2, 'metatests.PropertyType'], - [3, 'metatests.PropertyType2'], - [4, 'metatests.PropertyType'], - [5, 'metatests.PropertyType2'] - ], self._runner.testComplexMeta()) diff --git a/murano/tests/unit/dsl/test_method_param_inheritance.py b/murano/tests/unit/dsl/test_method_param_inheritance.py deleted file mode 100644 index 53232c419..000000000 --- a/murano/tests/unit/dsl/test_method_param_inheritance.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 yaql.language import exceptions as yaql_exceptions - -from murano.dsl import exceptions as dsl_exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestMethodParamInheritance(test_case.DslTestCase): - def setUp(self): - super(TestMethodParamInheritance, self).setUp() - model = om.Object('TestMethodParamInheritanceDerived') - self._runner = self.new_runner(model) - - def test_different_set_of_params_causes_exception(self): - - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testRunWithParam) - self.assertRaises( - dsl_exceptions.ContractViolationException, - self._runner.testRunWithoutParam) diff --git a/murano/tests/unit/dsl/test_multiple_inheritance.py b/murano/tests/unit/dsl/test_multiple_inheritance.py deleted file mode 100644 index ae0af39c5..000000000 --- a/murano/tests/unit/dsl/test_multiple_inheritance.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestMultipleInheritance(test_case.DslTestCase): - def setUp(self): - super(TestMultipleInheritance, self).setUp() - self._multi_derived = om.Object( - 'DerivedFrom2Classes', - rootProperty='ROOT') - model = om.Object( - 'SampleClass3', - multiClassProperty=self._multi_derived - ) - self._runner = self.new_runner(model) - - def test_multi_class_contract(self): - self._runner.testMultiContract() - self.assertEqual( - ['ParentClass1::method1', 'ParentClass2::method2'], - self.traces) - - def test_root_method_resolution(self): - self._runner.on(self._multi_derived).testRootMethod() - self.assertEqual( - ['CommonParent::testRootMethod', 'ROOT'], - self.traces) - - def test_property_accessible_on_several_paths(self): - self.assertEqual( - 'ROOT', - self._runner.testPropertyAccessibleOnSeveralPaths()) - - def test_specialized_mixin_override(self): - self._runner.on(self._multi_derived).testMixinOverride() - self.assertEqual( - ['ParentClass2::virtualMethod', '-', - 'CommonParent::virtualMethod', '-', - 'CommonParent::virtualMethod', '-', - 'ParentClass2::virtualMethod'], - self.traces) - - def test_super(self): - self._runner.on(self._multi_derived).testSuper() - self.assertCountEqual( - ['CommonParent::virtualMethod', 'ParentClass2::virtualMethod', - 'CommonParent::virtualMethod', 'ParentClass2::virtualMethod'], - self.traces) - - def test_psuper(self): - self._runner.on(self._multi_derived).testPsuper() - self.assertCountEqual( - ['CommonParent::virtualMethod', 'ParentClass2::virtualMethod', - 'CommonParent::virtualMethod', 'ParentClass2::virtualMethod'], - self.traces) diff --git a/murano/tests/unit/dsl/test_objects_copy_merge.py b/murano/tests/unit/dsl/test_objects_copy_merge.py deleted file mode 100644 index 93de92092..000000000 --- a/murano/tests/unit/dsl/test_objects_copy_merge.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 copy - -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestObjectsCopyMerge(test_case.DslTestCase): - def test_merged(self): - gen1_model = om.Object( - 'TestObjectsCopyMergeSampleClass', 'rootNode', - value='node1', - nodes=[ - om.Object('TestObjectsCopyMergeSampleClass', - value='node2', - nodes=[om.Ref('rootNode')]) - ]) - - gen2_model = copy.deepcopy(gen1_model) - gen2_model['nodes'] = [] - gen2_model['value'] = 'node1-updated' - - model = { - 'Objects': gen2_model, - 'ObjectsCopy': gen1_model - } - - runner = self.new_runner(model) - self.assertEqual(['node2', 'node1-updated'], self.traces) - self.assertEqual('It works!', runner.testMethod()) diff --git a/murano/tests/unit/dsl/test_property_access.py b/murano/tests/unit/dsl/test_property_access.py deleted file mode 100644 index 2139c922e..000000000 --- a/murano/tests/unit/dsl/test_property_access.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestPropertyAccess(test_case.DslTestCase): - def setUp(self): - super(TestPropertyAccess, self).setUp() - self._multi_derived = om.Object( - 'DerivedFrom2Classes', - rootProperty='ROOT', - ambiguousProperty=321) - model = om.Object( - 'SampleClass3', - multiClassProperty=self._multi_derived - ) - self._runner = self.new_runner(model) - - def test_private_property_access(self): - self.assertIsNone(self._runner.testPrivateProperty()) - self.assertEqual( - ['CommonParent', 'ParentClass1', 'SampleClass3'], - self.traces) - - def test_private_property_access_failure(self): - e = self.assertRaises( - exceptions.UninitializedPropertyAccessError, - self._runner.testUninitializedPrivatePropertyAccess) - self.assertEqual( - 'Access to uninitialized property "privateName" ' - 'in class "SampleClass3" is forbidden', str(e)) - - def test_read_of_private_property_of_other_class(self): - e = self.assertRaises( - exceptions.PropertyAccessError, - self._runner.testReadOfPrivatePropertyOfOtherClass) - self.assertEqual( - 'Property "privateProperty" in class "DerivedFrom2Classes" ' - 'cannot be read', str(e)) - self.assertEqual(['accessing property'], self.traces) - - def test_write_of_private_property_of_other_class(self): - e = self.assertRaises( - exceptions.PropertyAccessError, - self._runner.testWriteOfPrivatePropertyOfOtherClass) - self.assertEqual( - 'Property "privateProperty" in class "DerivedFrom2Classes" ' - 'cannot be written', str(e)) - - def test_access_ambiguous_property_with_resolver(self): - self.assertEqual( - '321', - self._runner.on(self._multi_derived). - testAccessAmbiguousPropertyWithResolver()) - - def test_property_merge(self): - self.assertEqual( - '555', - self._runner.on(self._multi_derived). - testPropertyMerge()) - self.assertEqual( - ['321', '555', '555', '555', '555'], - self.traces) - - def test_property_usage(self): - e = self.assertRaises( - exceptions.NoWriteAccessError, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty1) - self.assertEqual( - 'Property "usageTestProperty1" is immutable to the caller', - str(e)) - self.assertRaises( - exceptions.NoWriteAccessError, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty2) - self.assertEqual( - 33, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty3()) - self.assertEqual( - 44, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty4()) - self.assertEqual( - 55, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty5()) - self.assertRaises( - exceptions.NoWriteAccessError, - self._runner.on(self._multi_derived). - testModifyUsageTestProperty6) - self.assertEqual( - 77, - self._runner.on( - self._multi_derived).testModifyUsageTestProperty7()) diff --git a/murano/tests/unit/dsl/test_property_inititialization.py b/murano/tests/unit/dsl/test_property_inititialization.py deleted file mode 100644 index ef91ea17a..000000000 --- a/murano/tests/unit/dsl/test_property_inititialization.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestPropertyInitialization(test_case.DslTestCase): - def setUp(self): - super(TestPropertyInitialization, self).setUp() - model = om.Object( - 'PropertyInit' - ) - self._runner = self.new_runner(model) - - def test_runtime_property_default(self): - self.assertEqual( - 'DEFAULT', - self._runner.testRuntimePropertyDefault()) - - def test_runtime_property_without_default(self): - self.assertRaises( - exceptions.UninitializedPropertyAccessError, - self._runner.testRuntimePropertyWithoutDefault) - - def test_runtime_property_with_strict_contract_without_default(self): - self.assertEqual( - 'VALUE', - self._runner.testRuntimePropertyWithStrictContractWithoutDefault()) - - def test_uninitialized_runtime_property_with_strict_contract(self): - self.assertRaises( - exceptions.UninitializedPropertyAccessError, - self._runner.testUninitializedRuntimeProperty) diff --git a/murano/tests/unit/dsl/test_reflection.py b/murano/tests/unit/dsl/test_reflection.py deleted file mode 100644 index bfcc4a5f8..000000000 --- a/murano/tests/unit/dsl/test_reflection.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestReflection(test_case.DslTestCase): - def setUp(self): - super(TestReflection, self).setUp() - self._runner = self.new_runner(om.Object('TestReflection')) - - def test_type_info(self): - self.assertEqual( - { - 'name': 'TestReflection', - 'versionStr': '0.0.0', - 'versionMajor': 0, - 'versionMinor': 0, - 'versionPatch': 0, - 'ancestors': ['io.murano.Object'], - 'properties': ['property', 'staticProperty'], - 'package': 'tests', - 'methods': ['foo', 'getAttr', 'setAttr'] - }, - self._runner.testTypeInfo()) - - def test_method_info(self): - self.assertEqual( - { - 'name': 'foo', - 'arguments': ['bar', 'baz'], - 'barHasDefault': True, - 'bazHasDefault': False, - 'barMethod': 'foo', - 'bazMethod': 'foo', - 'declaringType': 'TestReflection' - }, - self._runner.testMethodInfo()) - - def test_property_info(self): - self.assertEqual( - { - 'name': 'property', - 'hasDefault': True, - 'usage': 'InOut' - }, - self._runner.testPropertyInfo()) - - def test_property_read(self): - self.assertEqual( - [['object', 'static'], ['static']], - self._runner.testPropertyRead()) - - def test_property_write(self): - self.assertEqual( - [['new object', 'new static'], ['new static']], - self._runner.testPropertyWrite()) - - def test_method_invoke(self): - self.assertEqual('bar baz', self._runner.testMethodInvoke()) - - def test_instance_create(self): - self.assertEqual('test', self._runner.testInstanceCreate()) diff --git a/murano/tests/unit/dsl/test_results_serializer.py b/murano/tests/unit/dsl/test_results_serializer.py deleted file mode 100644 index d457f5eea..000000000 --- a/murano/tests/unit/dsl/test_results_serializer.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 testtools import matchers - -from murano.dsl import serializer -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -# Tests for correctness of serialization of MuranoPL object tree -# back to Object Model - -class TestResultsSerializer(test_case.DslTestCase): - def setUp(self): - super(TestResultsSerializer, self).setUp() - self._class2 = om.Object('SampleClass2', - class2Property='string2') - self._class1 = om.Object( - 'SampleClass1', 'def', - stringProperty='string1', - arbitraryProperty={'a': [1, 2]}, - classProperty=self._class2) - self._root_class = om.Object('ContractExamples', 'abc', - sampleClass=self._class1) - - self._runner = self.new_runner(self._root_class) - - def _test_data_in_section(self, name, serialized): - """Test that Model -> Load -> Serialize = Model - - Test that a section of Object Model has expected structure and - property values that are equal to those originally loaded - (e.g. that Model -> Load -> Serialize = Model) - - :param name: section name - :param serialized: serialized Object Model - """ - - self.assertEqual('abc', - serialized[name]['?']['id']) - self.assertEqual('ContractExamples/0.0.0@tests', - serialized['Objects']['?']['type']) - self.assertIsInstance(serialized[name]['sampleClass'], dict) - self.assertEqual('def', - serialized[name]['sampleClass']['?']['id']) - self.assertEqual('SampleClass1/0.0.0@tests', - serialized[name]['sampleClass']['?']['type']) - self.assertEqual('string1', - serialized[name]['sampleClass']['stringProperty']) - - def test_results_serialize(self): - """Test that serialized contains same values and headers - - Test that serialized Object Model has both Objects and ObjectsCopy - sections and they both contain the same property values and object - headers. Note, that Objects section may contain additional designer - metadata and information on available actions that is not needed in - ObjectsCopy thus we cannot test for ObjectsCopy be strictly equal to - Objects - """ - - serialized = self._runner.serialized_model - self.assertIn('Objects', serialized) - self.assertIn('ObjectsCopy', serialized) - self._test_data_in_section('Objects', serialized) - self._test_data_in_section('ObjectsCopy', serialized) - - def test_actions(self): - """Test that information on actions can be invoked - - Test that information on actions can be invoked on each MuranoPL - object are persisted into object header ('?' key) during serialization - of Objects section of Object Model - - """ - serialized = self._runner.serialized_model - actions = serialized['Objects']['?'].get('_actions') - self.assertIsInstance(actions, dict) - action_names = [action['name'] for action in actions.values()] - self.assertIn('testAction', action_names) - self.assertNotIn('notAction', action_names) - self.assertIn('testRootMethod', action_names) - action_meta = None - for action in actions.values(): - self.assertIsInstance(action.get('enabled'), bool) - self.assertIsInstance(action.get('name'), str) - self.assertThat( - action['name'], - matchers.StartsWith('test')) - if action['name'] == 'testActionMeta': - action_meta = action - else: - self.assertEqual(action['title'], action['name']) - self.assertIsNotNone(action_meta) - self.assertEqual(action_meta['title'], "Title of the method") - self.assertEqual(action_meta['description'], - "Description of the method") - self.assertEqual(action_meta['helpText'], "HelpText of the method") - - def test_attribute_serialization(self): - """Test that attributes produced by MuranoPL code are persisted - - Test that attributes produced by MuranoPL code are persisted in - dedicated section of Object Model. Attributes are values that are - stored in special key-value storage that is private to each class. - Classes can store state data there. Attributes are persisted across - deployment sessions but are not exposed via API (thus cannot be - accessed by dashboard) - """ - - self._runner.on(self._class1).testAttributes('VALUE') - serialized = self._runner.serialized_model - self.assertIsInstance(serialized.get('Attributes'), list) - self.assertThat( - serialized['Attributes'], - matchers.HasLength(1)) - self.assertEqual( - [self._class1.id, 'SampleClass1', 'att1', 'VALUE'], - serialized['Attributes'][0]) - - def test_attribute_deserialization(self): - """Test that attributes are available - - Test that attributes that are put into Attributes section of - Object Model become available to appropriate MuranoPL classes - """ - - serialized = self._runner.serialized_model - serialized['Attributes'].append( - [self._class1.id, 'SampleClass1', 'att2', ' Snow']) - runner2 = self.new_runner(serialized) - self.assertEqual( - 'John Snow', - runner2.on(self._class1).testAttributes('John')) - - def test_value_deserialization(self): - """Test serialization of arbitrary values - - Test serialization of arbitrary values that can be returned - from action methods - """ - - runner = self.new_runner(self._class2) - result = runner.testMethod() - self.assertEqual( - { - 'key1': 'abc', - 'key2': ['a', 'b', 'c'], - 'key3': None, - 'key4': False, - 'key5': {'x': 'y'}, - 'key6': [{'w': 'q'}] - }, - serializer.serialize(result, runner.executor)) diff --git a/murano/tests/unit/dsl/test_schema_generation.py b/murano/tests/unit/dsl/test_schema_generation.py deleted file mode 100644 index 902a98643..000000000 --- a/murano/tests/unit/dsl/test_schema_generation.py +++ /dev/null @@ -1,238 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 testtools import matchers - -from murano.dsl import schema_generator -from murano.tests.unit.dsl.foundation import runner -from murano.tests.unit.dsl.foundation import test_case - - -class TestSchemaGeneration(test_case.DslTestCase): - def setUp(self): - super(TestSchemaGeneration, self).setUp() - schema = schema_generator.generate_schema( - self.package_loader, runner.TestContextManager({}), - 'TestSchema') - - self._class_schema = schema.pop('') - self._model_builders_schema = schema - - def test_general_structure(self): - self.assertIn('$schema', self._class_schema) - self.assertIn('type', self._class_schema) - self.assertIn('properties', self._class_schema) - self.assertEqual( - 'http://json-schema.org/draft-04/schema#', - self._class_schema['$schema']) - self.assertEqual('object', self._class_schema['type']) - - def _test_simple_property(self, name_or_schema, types): - if not isinstance(name_or_schema, dict): - props = self._class_schema['properties'] - self.assertIn(name_or_schema, props) - schema = props[name_or_schema] - else: - schema = name_or_schema - self.assertIn('type', schema) - if isinstance(types, list): - self.assertCountEqual(schema['type'], types) - else: - self.assertEqual(schema['type'], types) - return schema - - def test_string_property(self): - self._test_simple_property('stringProperty', ['null', 'string']) - - def test_not_null_string_property(self): - self._test_simple_property('stringNotNullProperty', 'string') - - def test_int_property(self): - self._test_simple_property('intProperty', ['null', 'integer']) - - def test_not_null_int_property(self): - self._test_simple_property('intNotNullProperty', 'integer') - - def test_bool_property(self): - self._test_simple_property('boolProperty', ['null', 'boolean']) - - def test_not_null_bool_property(self): - self._test_simple_property('boolNotNullProperty', 'boolean') - - def test_class_property(self): - schema = self._test_simple_property( - 'classProperty', ['null', 'muranoObject']) - self.assertEqual('SampleClass1', schema.get('muranoType')) - - def test_template_property(self): - schema = self._test_simple_property( - 'templateProperty', ['null', 'muranoObject']) - self.assertEqual('SampleClass1', schema.get('muranoType')) - self.assertTrue(schema.get('owned')) - self.assertCountEqual( - ['stringProperty'], - schema.get('excludedProperties')) - - def test_default_property(self): - schema = self._test_simple_property( - 'defaultProperty', ['null', 'integer']) - self.assertEqual(999, schema.get('default')) - - def test_list_property(self): - schema = self._test_simple_property('listProperty', 'array') - self.assertIn('items', schema) - items = schema['items'] - self._test_simple_property(items, 'string') - - def test_dict_property(self): - schema = self._test_simple_property('dictProperty', 'object') - self.assertIn('properties', schema) - props = schema['properties'] - self.assertIn('key1', props) - self._test_simple_property(props['key1'], 'string') - self.assertIn('key2', props) - self._test_simple_property(props['key2'], 'string') - self.assertIn('additionalProperties', schema) - extra_props = schema['additionalProperties'] - self._test_simple_property(extra_props, ['null', 'integer']) - - def test_complex_property(self): - schema = self._test_simple_property('complexProperty', 'object') - self.assertIn('properties', schema) - self.assertEqual({}, schema['properties']) - self.assertIn('additionalProperties', schema) - extra_props = schema['additionalProperties'] - self._test_simple_property(extra_props, 'array') - self.assertIn('items', extra_props) - items = extra_props['items'] - self._test_simple_property(items, 'integer') - - def test_minimum_contract(self): - schema = self._test_simple_property('minimumContract', 'integer') - self.assertFalse(schema.get('exclusiveMinimum', True)) - self.assertEqual(5, schema.get('minimum')) - - def test_maximum_contract(self): - schema = self._test_simple_property('maximumContract', 'integer') - self.assertTrue(schema.get('exclusiveMaximum', False)) - self.assertEqual(15, schema.get('maximum')) - - def test_range_contract(self): - schema = self._test_simple_property('rangeContract', 'integer') - self.assertFalse(schema.get('exclusiveMaximum', True)) - self.assertTrue(schema.get('exclusiveMinimum', False)) - self.assertEqual(0, schema.get('minimum')) - self.assertEqual(10, schema.get('maximum')) - - def test_chain_contract(self): - schema = self._test_simple_property('chainContract', 'integer') - self.assertFalse(schema.get('exclusiveMaximum', True)) - self.assertTrue(schema.get('exclusiveMinimum', False)) - self.assertEqual(0, schema.get('minimum')) - self.assertEqual(10, schema.get('maximum')) - - def test_regex_contract(self): - schema = self._test_simple_property('regexContract', 'string') - self.assertEqual(r'\d+', schema.get('pattern')) - - def test_enum_contract(self): - schema = self._test_simple_property('enumContract', 'string') - self.assertEqual(['a', 'b'], schema.get('enum')) - - def test_enum_func_contract(self): - schema = self._test_simple_property('enumFuncContract', 'string') - self.assertEqual(['x', 'y'], schema.get('enum')) - - def test_ui_hints(self): - schema = self._test_simple_property('decoratedProperty', 'string') - self.assertEqual('Title!', schema.get('title')) - self.assertEqual('Description!', schema.get('description')) - self.assertEqual('Help!', schema.get('helpText')) - self.assertFalse(schema.get('visible')) - self.assertThat(schema.get('formIndex'), matchers.GreaterThan(-1)) - self.assertEqual('mySection', schema.get('formSection')) - - sections = self._class_schema.get('formSections') - self.assertIsInstance(sections, dict) - section = sections.get('mySection') - self.assertIsInstance(section, dict) - self.assertThat(section.get('index'), matchers.GreaterThan(-1)) - self.assertEqual('Section Title', section.get('title')) - - def test_model_builders(self): - self.assertEqual(1, len(self._model_builders_schema)) - schema = self._model_builders_schema.get('modelBuilder') - self.assertIsInstance(schema, dict) - self._class_schema = schema - self.test_general_structure() - self.assertEqual('Model Builder!', schema.get('title')) - args = schema['properties'] - self._test_simple_property(args.get('arg1'), 'string') - self._test_simple_property(args.get('arg2'), 'integer') - arg1 = args['arg1'] - self.assertEqual('Arg1!', arg1.get('title')) - - def test_generate_schema_with_extra_params(self): - schema = schema_generator.generate_schema( - self.package_loader, runner.TestContextManager({}), - 'TestSchema', method_names='modelBuilder', - package_name='tests') - expected_schema = { - '$schema': 'http://json-schema.org/draft-04/schema#', - 'additionalProperties': False, - 'formSections': {}, - 'properties': {'arg1': {'title': 'Arg1!', - 'type': 'string'}, - 'arg2': {'type': 'integer'}}, - 'title': 'Model Builder!', - 'type': 'object' - } - - self.assertIn('modelBuilder', schema) - self.assertEqual(expected_schema, schema['modelBuilder']) - - def test_translate_list(self): - contract = [1, 2, 3] - result = schema_generator.translate_list(contract, None, None) - self.assertEqual({'type': 'array'}, result) - - contract = [1, 2, 3, {'foo': 'bar'}] - result = schema_generator.translate_list(contract, None, None) - expected = { - 'items': {'additionalProperties': False, - 'properties': {'foo': None}, - 'type': 'object'}, - 'type': 'array' - } - self.assertEqual(sorted(expected.keys()), sorted(result.keys())) - for key, val in expected.items(): - self.assertEqual(val, result[key]) - - contract = [1, 2, 3, {'foo': 'bar'}, ['baz']] - result = schema_generator.translate_list(contract, None, None) - expected = { - 'additionalItems': {'items': None, 'type': 'array'}, - 'items': [{'additionalProperties': False, - 'properties': {'foo': None}, - 'type': 'object'}, - {'items': None, 'type': 'array'}], - 'type': 'array' - } - self.assertEqual(sorted(expected.keys()), sorted(result.keys())) - for key, val in expected.items(): - if isinstance(val, dict): - for key_, val_ in val.items(): - self.assertEqual(val_, val[key_]) - else: - self.assertEqual(val, result[key]) diff --git a/murano/tests/unit/dsl/test_session_local_storage.py b/murano/tests/unit/dsl/test_session_local_storage.py deleted file mode 100644 index 366a78152..000000000 --- a/murano/tests/unit/dsl/test_session_local_storage.py +++ /dev/null @@ -1,106 +0,0 @@ -# Licensed 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 unittest import mock - -from murano.dsl import helpers -from murano.dsl import session_local_storage -from murano.tests.unit.dsl.foundation import test_case - - -class FakeWithInit(session_local_storage._localbase): - def __init__(self, *args, **kwargs): - pass - - -class FakeNoInit(session_local_storage._localbase): - pass - - -class TestLocalbase(test_case.DslTestCase): - def test_new(self): - lb = FakeWithInit.__new__( - FakeWithInit, 42, 'foo', bar='baz') - self.assertEqual(((42, 'foo',), {'bar': 'baz'}), lb._local__args) - - def test_new_bad(self): - self.assertRaises( - TypeError, FakeNoInit.__name__) - - -class TestPatch(test_case.DslTestCase): - @mock.patch.object(helpers, 'get_execution_session', - return_value=mock.sentinel.session) - def test_patch(self, mock_ges): - fwi = FakeWithInit(foo='bar') - session_local_storage._patch(fwi) - self.assertEqual({}, fwi.__dict__) - - -class TestLocal(test_case.DslTestCase): - class FakeLocal(session_local_storage._local): - def __init__(self, foo): - self.foo = foo - - @mock.patch.object(session_local_storage, '_patch') - def setUp(self, mock_patch): - super(TestLocal, self).setUp() - self.fl = self.FakeLocal('bar') - - @mock.patch.object(session_local_storage, '_patch') - def test_getattribute(self, mock_patch): - self.assertEqual('bar', self.fl.foo) - mock_patch.assert_called_with(self.fl) - - @mock.patch.object(session_local_storage, '_patch') - def test_setattribute(self, mock_patch): - self.fl.foo = 'baz' - mock_patch.assert_called_with(self.fl) - - @mock.patch.object(session_local_storage, '_patch') - def test_delattribute(self, mock_patch): - del self.fl.foo - mock_patch.assert_called_with(self.fl) - - -class TestSessionLocalDict(test_case.DslTestCase): - def setUp(self): - super(TestSessionLocalDict, self).setUp() - self.sld = session_local_storage.SessionLocalDict(foo='bar') - - @mock.patch.object(helpers, 'get_execution_session', return_value=None) - def test_data_no_session(self, mock_ges): - self.assertEqual({'foo': 'bar'}, self.sld.data) - self.sld.data = {'foo': 'baz'} - - mock_ges.assert_called_with() - self.assertEqual({'foo': 'baz'}, self.sld.data) - - @mock.patch.object(helpers, 'get_execution_session', - return_value=mock.sentinel.session) - def test_data(self, mock_ges): - self.sld.data = mock.sentinel.data - - mock_ges.assert_called_with() - self.assertEqual(mock.sentinel.data, self.sld.data) - - -class TestExecutionSessionMemoize(test_case.DslTestCase): - @mock.patch.object(helpers, 'get_memoize_func', - return_value=mock.sentinel.mem_func) - def test_execution_session_memoize(self, mock_gef): - f = mock.MagicMock() - f.return_value = 'im a function' - new_f = session_local_storage.execution_session_memoize(f) - self.assertEqual(f, mock_gef.call_args[0][0]) - self.assertIsInstance(mock_gef.call_args[0][1], - session_local_storage.SessionLocalDict) - self.assertEqual(mock.sentinel.mem_func, new_f) diff --git a/murano/tests/unit/dsl/test_single_inheritance.py b/murano/tests/unit/dsl/test_single_inheritance.py deleted file mode 100644 index db46b027e..000000000 --- a/murano/tests/unit/dsl/test_single_inheritance.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestSingleInheritance(test_case.DslTestCase): - def setUp(self): - super(TestSingleInheritance, self).setUp() - model = om.Object( - 'SingleInheritanceChild' - ) - self._runner = self.new_runner(model) - - def test_virtual_calls(self): - self._runner.testVirtualCalls() - self.assertEqual( - ['SingleInheritanceChild::method1', - 'SingleInheritanceParent::method1', - 'SingleInheritanceChild::method2', - 'SingleInheritanceParent::method2'], - self.traces) diff --git a/murano/tests/unit/dsl/test_statics.py b/murano/tests/unit/dsl/test_statics.py deleted file mode 100644 index 3de6734be..000000000 --- a/murano/tests/unit/dsl/test_statics.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 yaql.language import exceptions as yaql_exceptions -from yaql.language import specs -from yaql.language import yaqltypes - -from murano.dsl import dsl -from murano.dsl import dsl_types -from murano.dsl import exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestStatics(test_case.DslTestCase): - def setUp(self): - @dsl.name('test.TestStatics') - class PythonClass(object): - @staticmethod - @specs.parameter('arg', yaqltypes.Integer()) - @specs.inject('receiver', yaqltypes.Receiver()) - def static_python_method(arg, receiver): - if isinstance(receiver, dsl_types.MuranoObjectInterface): - return 3 * arg - return 7 * arg - - @classmethod - @specs.inject('receiver', yaqltypes.Receiver()) - def classmethod_python_method(cls, arg, receiver): - if isinstance(receiver, dsl_types.MuranoObjectInterface): - return cls.__name__.upper() + str(arg) - return cls.__name__ + str(arg) - - super(TestStatics, self).setUp() - self.package_loader.load_class_package( - 'test.TestStatics', None).register_class(PythonClass) - self._runner = self.new_runner( - om.Object('test.TestStatics', staticProperty2='INVALID')) - - def test_call_static_method_on_object(self): - self.assertEqual(123, self._runner.testCallStaticMethodOnObject()) - - def test_call_static_method_on_class_name(self): - self.assertEqual(123, self._runner.testCallStaticMethodOnClassName()) - - def test_call_static_method_on_class_name_with_ns(self): - self.assertEqual( - 678, self._runner.testCallStaticMethodOnClassNameWithNs()) - - def test_call_static_method_from_another_method(self): - self.assertEqual( - 123 * 5, self._runner.testCallStaticMethodFromAnotherMethod()) - - def test_static_this(self): - self.assertIsInstance( - self._runner.testStaticThis(), dsl_types.MuranoTypeReference) - - def test_no_access_to_instance_properties(self): - self.assertRaises( - exceptions.NoPropertyFound, - self._runner.testNoAccessToInstanceProperties) - - def test_access_static_property_from_instance_method(self): - self.assertEqual( - 'xxx', self._runner.testAccessStaticPropertyFromInstanceMethod()) - - def test_access_static_property_from_static_method(self): - self.assertEqual( - 'xxx', self._runner.testAccessStaticPropertyFromStaticMethod()) - - def test_modify_static_property_using_dollar(self): - self.assertEqual( - 'qq', self._runner.testModifyStaticPropertyUsingDollar()) - - def test_modify_static_property_using_this(self): - self.assertEqual( - 'qq', self._runner.testModifyStaticPropertyUsingThis()) - - def test_modify_static_property_using_class_name(self): - self.assertEqual( - 'qq', self._runner.testModifyStaticPropertyUsingClassName()) - - def test_modify_static_property_using_ns_class_name(self): - self.assertEqual( - 'qq', self._runner.testModifyStaticPropertyUsingNsClassName()) - - def test_modify_static_property_using_type_func(self): - self.assertEqual( - 'qq', self._runner.testModifyStaticPropertyUsingTypeFunc()) - - def test_modify_static_dict_property(self): - self.assertEqual( - {'key': 'value'}, self._runner.testModifyStaticDictProperty()) - - def test_property_is_static(self): - self.assertEqual('qq', self._runner.testPropertyIsStatic()) - - def test_static_properties_excluded_from_object_model(self): - self.assertEqual( - 'staticProperty', - self._runner.testStaticPropertisNotLoaded()) - - def test_type_is_singleton(self): - self.assertTrue(self._runner.testTypeIsSingleton()) - - def test_static_property_inheritance(self): - self.assertEqual( - 'baseStaticProperty' * 3, - self._runner.testStaticPropertyInheritance()) - - def test_static_property_override(self): - self.assertEqual( - [ - 'conflictingStaticProperty-child', - 'conflictingStaticProperty-child', - 'conflictingStaticProperty-base', - 'conflictingStaticProperty-child', - 'conflictingStaticProperty-base' - ], self._runner.testStaticPropertyOverride()) - - def test_type_info_of_type(self): - self.assertTrue(self._runner.testTypeinfoOfType()) - - def test_call_python_static_method(self): - self.assertEqual( - [333] + [777] * 3, - self._runner.testCallPythonStaticMethod()) - - def test_call_python_classmethod(self): - self.assertEqual( - ['PYTHONCLASS!'] + ['PythonClass!'] * 3, - self._runner.testCallPythonClassMethod()) - - def test_call_static_method_on_invalid_class(self): - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testCallStaticMethodOnInvalidClass) - - def test_static_method_callable_from_python(self): - self.assertEqual( - 'It works!', - self._runner.on_class('test.TestStatics').testStaticAction()) diff --git a/murano/tests/unit/dsl/test_unicode.py b/murano/tests/unit/dsl/test_unicode.py deleted file mode 100644 index 1c8f964de..000000000 --- a/murano/tests/unit/dsl/test_unicode.py +++ /dev/null @@ -1,45 +0,0 @@ -# coding: utf-8 -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 murano.dsl import dsl_exception -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestUnicode(test_case.DslTestCase): - def setUp(self): - super(TestUnicode, self).setUp() - self._runner = self.new_runner(om.Object('TestUnicode')) - - def test_literal(self): - self.assertEqual( - u"солнце ♥ φεγγάρι", - self._runner.testLiteral()) - - def test_expression(self): - self.assertEqual( - u"СОЛНЦЕ ♥ ΦΕΓΓΆΡΙ", - self._runner.testExpression()) - - def test_parameter(self): - self.assertEqual( - u"СОЛНЦЕ ♥ ΦΕΓΓΆΡΙ", - self._runner.testParameter()) - - def test_exception(self): - x = self.assertRaises( - dsl_exception.MuranoPlException, - self._runner.testException) - self.assertEqual(u"солнце ♥ φεγγάρι", x.message) diff --git a/murano/tests/unit/dsl/test_varkwargs.py b/murano/tests/unit/dsl/test_varkwargs.py deleted file mode 100644 index e74dea9c9..000000000 --- a/murano/tests/unit/dsl/test_varkwargs.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2015 Mirantis, Inc. -# -# Licensed 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 yaql.language import exceptions as yaql_exceptions - -from murano.dsl import exceptions as dsl_exceptions -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestVarKwArgs(test_case.DslTestCase): - def setUp(self): - super(TestVarKwArgs, self).setUp() - self._runner = self.new_runner(om.Object('TestVarKwArgs')) - - def test_varargs(self): - self.assertEqual([2, 3, 4], self._runner.testVarArgs()) - - def test_kwargs(self): - self.assertEqual({'arg2': 2, 'arg3': 3}, self._runner.testKwArgs()) - - def test_duplicate_kwargs(self): - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testDuplicateKwArgs) - - def test_duplicate_varargs(self): - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testDuplicateVarArgs) - - def test_explicit_varargs(self): - self.assertRaises( - yaql_exceptions.NoMatchingMethodException, - self._runner.testExplicitVarArgs) - - def test_args(self): - self.assertEqual( - [[1, 2, 3], {'arg1': 4, 'arg2': 5, 'arg3': 6}], - self._runner.testArgs()) - - def test_varargs_contract(self): - self.assertRaises( - dsl_exceptions.ContractViolationException, - self._runner.testVarArgsContract) - - def test_kwargs_contract(self): - self.assertRaises( - dsl_exceptions.ContractViolationException, - self._runner.testKwArgsContract) diff --git a/murano/tests/unit/dsl/test_versioning.py b/murano/tests/unit/dsl/test_versioning.py deleted file mode 100644 index 65195a1cf..000000000 --- a/murano/tests/unit/dsl/test_versioning.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 semantic_version -from unittest import mock - -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import test_case - - -class TestVersioning(test_case.DslTestCase): - def test_provided_version(self): - version = '1.11.111' - sem_version = semantic_version.Spec('==' + version) - model = om.Object('Empty', class_version=version) - m = mock.MagicMock(return_value=self._package_loader._package) - self._package_loader.load_class_package = m - self.new_runner(model) - m.assert_called_once_with('Empty', sem_version) - - def test_empty_provided_version(self): - version = '' - sem_version = semantic_version.Spec('>=0.0.0', '<1.0.0') - model = om.Object('Empty', class_version=version) - m = mock.MagicMock(return_value=self._package_loader._package) - self._package_loader.load_class_package = m - self.new_runner(model) - m.assert_called_once_with('Empty', sem_version) - - def test_several_in_row(self): - version = '>3.0.0,<=4.1' - sem_version = semantic_version.Spec('>3.0.0', '<4.2.0') - model = om.Object('Empty', class_version=version) - m = mock.MagicMock(return_value=self._package_loader._package) - self._package_loader.load_class_package = m - self.new_runner(model) - m.assert_called_once_with('Empty', sem_version) diff --git a/murano/tests/unit/engine/__init__.py b/murano/tests/unit/engine/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/engine/meta/Classes/Mytest.yaml b/murano/tests/unit/engine/meta/Classes/Mytest.yaml deleted file mode 100644 index ae18eb633..000000000 --- a/murano/tests/unit/engine/meta/Classes/Mytest.yaml +++ /dev/null @@ -1,8 +0,0 @@ -Namespaces: - =: io.murano.test - -Extends: sys:TestFixture - -Name: MyTest - -Methods: diff --git a/murano/tests/unit/engine/meta/TestMock.yaml b/murano/tests/unit/engine/meta/TestMock.yaml deleted file mode 100644 index 1d7ecf7be..000000000 --- a/murano/tests/unit/engine/meta/TestMock.yaml +++ /dev/null @@ -1,84 +0,0 @@ -Namespaces: - test: io.murano.test - -Name: TestMocks - -Extends: test:TestFixture - -Properties: - logMessage: - Contract: $.string() - Default: 'Mock from property' - -Methods: - initialize: - Body: - - $.originalClass: new(TestMocksFixture) - - mock1: - Body: - - Return: 'This is mock1' - - testInjectMethodWithString: - Body: - - inject(TestMocksFixture, simpleMethod1, $this, mock1) - - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('This is mock1', $output) - - testInjectObjectWithString: - Body: - - inject($.originalClass, simpleMethod1, $this, mock1) - - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('This is mock1', $output) - - testInjectMethodWithYaqlExpr: - Body: - # Calling original method without mocking - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('method1', $output) - - - $mockText: 'I am mock' - - inject(TestMocksFixture, simpleMethod1, $mockText) - - # Calling original method after mocking - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('I am mock', $output) - - testInjectMethodWithYaqlExpr2: - Body: - # Calling original method without mocking - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('method1', $output) - - - inject(TestMocksFixture, simpleMethod1, $.logMessage) - - # Calling mocked method - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('Mock from property', $output) - - testInjectObjectWithYaqlExpr: - Body: - # Calling original method without mocking - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('method1', $output) - - - $mockText: 'I am mock' - - inject($.originalClass, simpleMethod1, $mockText) - - # Calling original method after mocking - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('I am mock', $output) - - testWithoriginal: - Body: - - inject(TestMocksFixture, simpleMethod1, withOriginal(t => $.originalClass.someProperty) -> $t) - - $output: $.originalClass.simpleMethod1() - - $.assertEqual(DEFAULT, $output) - - testOriginalMethod: - Body: - - inject(TestMocksFixture, simpleMethod1, originalMethod()) - - $output: $.originalClass.simpleMethod1() - - $.assertEqual('method1', $output) \ No newline at end of file diff --git a/murano/tests/unit/engine/meta/TestMockFixture.yaml b/murano/tests/unit/engine/meta/TestMockFixture.yaml deleted file mode 100644 index f713056ce..000000000 --- a/murano/tests/unit/engine/meta/TestMockFixture.yaml +++ /dev/null @@ -1,11 +0,0 @@ -Name: TestMocksFixture - -Properties: - someProperty: - Contract: $.string().notNull() - Default: DEFAULT - -Methods: - simpleMethod1: - Body: - - Return: 'method1' diff --git a/murano/tests/unit/engine/meta/manifest.yaml b/murano/tests/unit/engine/meta/manifest.yaml deleted file mode 100644 index aba9477ae..000000000 --- a/murano/tests/unit/engine/meta/manifest.yaml +++ /dev/null @@ -1,10 +0,0 @@ -Format: 1.0 -Type: Application -FullName: io.murano.test.MyTest -Name: Test case Example -Description: | - Example of simple package -Author: 'Mirantis, Inc' -Tags: [] -Classes: - io.murano.test.MyTest: Mytest.yaml diff --git a/murano/tests/unit/engine/system/__init__.py b/murano/tests/unit/engine/system/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/engine/system/execution_plans/DeployTelnet.template b/murano/tests/unit/engine/system/execution_plans/DeployTelnet.template deleted file mode 100644 index 39256f45e..000000000 --- a/murano/tests/unit/engine/system/execution_plans/DeployTelnet.template +++ /dev/null @@ -1,21 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Telnet - -Parameters: - appName: $appName - -Body: | - return deploy(args.appName).stdout - -Scripts: - deploy: - Type: Application - Version: 1.0.0 - EntryPoint: deployTelnet.sh - Files: - - installer.sh - - common.sh - Options: - captureStdout: true - captureStderr: true diff --git a/murano/tests/unit/engine/system/execution_plans/DeployTomcat.template b/murano/tests/unit/engine/system/execution_plans/DeployTomcat.template deleted file mode 100644 index 26f822a78..000000000 --- a/murano/tests/unit/engine/system/execution_plans/DeployTomcat.template +++ /dev/null @@ -1,16 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Tomcat -Parameters: - appName: $appName -Body: | - deploy(args.appName) -Scripts: - deploy: - Type: Application - Version: 1.0.0 - EntryPoint: deployTomcat.sh - Files: [] - Options: - captureStdout: false - captureStderr: true \ No newline at end of file diff --git a/murano/tests/unit/engine/system/execution_plans/application.template b/murano/tests/unit/engine/system/execution_plans/application.template deleted file mode 100644 index 1d1807f02..000000000 --- a/murano/tests/unit/engine/system/execution_plans/application.template +++ /dev/null @@ -1,21 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Tomcat - -Parameters: - appName: $appName - -Body: | - return deploy(args.appName).stdout - -Scripts: - deploy: - Type: Application - Version: 1.0.0 - EntryPoint: deployTomcat.sh - Files: - - installer: - - - Options: - captureStdout: true - captureStderr: true diff --git a/murano/tests/unit/engine/system/execution_plans/application_without_files.template b/murano/tests/unit/engine/system/execution_plans/application_without_files.template deleted file mode 100644 index 91cfbed39..000000000 --- a/murano/tests/unit/engine/system/execution_plans/application_without_files.template +++ /dev/null @@ -1,19 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Tomcat - -Parameters: - appName: $appName - -Body: | - return deploy(args.appName).stdout - -Scripts: - deploy: - Type: Application - Version: 1.0.0 - EntryPoint: deployTomcat.sh - Files: [] - Options: - captureStdout: true - captureStderr: true \ No newline at end of file diff --git a/murano/tests/unit/engine/system/execution_plans/chef.template b/murano/tests/unit/engine/system/execution_plans/chef.template deleted file mode 100644 index e3863f32b..000000000 --- a/murano/tests/unit/engine/system/execution_plans/chef.template +++ /dev/null @@ -1,21 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Chef - -Parameters: - appName: $appName - -Body: | - return deploy(args.appName).stdout - -Scripts: - deploy: - Type: Chef - Version: 1.0.0 - EntryPoint: cookbook/recipe - Files: - - https://github.com/tomcat.git - - java: https://github.com/java.git - Options: - captureStdout: true - captureStderr: true \ No newline at end of file diff --git a/murano/tests/unit/engine/system/execution_plans/template_with_files.template b/murano/tests/unit/engine/system/execution_plans/template_with_files.template deleted file mode 100644 index 84ef175e3..000000000 --- a/murano/tests/unit/engine/system/execution_plans/template_with_files.template +++ /dev/null @@ -1,26 +0,0 @@ -FormatVersion: 2.0.0 -Version: 1.0.0 -Name: Deploy Tomcat - -Parameters: - appName: $appName - -Body: | - return deploy(args.appName).stdout - -Files: - updateScript: - BodyType: Text - Name: updateScript - Body: text - -Scripts: - deploy: - Type: Application - Version: 1.0.0 - EntryPoint: deployTomcat.sh - Files: - - updateScript - Options: - captureStdout: true - captureStderr: true diff --git a/murano/tests/unit/engine/system/test_agent.py b/murano/tests/unit/engine/system/test_agent.py deleted file mode 100644 index e6d2796d5..000000000 --- a/murano/tests/unit/engine/system/test_agent.py +++ /dev/null @@ -1,725 +0,0 @@ -# Copyright (c) 2015 Telefonica I+D -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 copy -import datetime -import json -import os -import tempfile -from unittest import mock - -from oslo_serialization import base64 -import yaml as yamllib - -from murano.common import exceptions -from murano.dsl import dsl -from murano.dsl import murano_object -from murano.dsl import murano_type -from murano.dsl import object_store -from murano.engine.system import agent -from murano.engine.system import resource_manager -from murano.tests.unit import base - - -class TestAgent(base.MuranoTestCase): - def setUp(self): - super(TestAgent, self).setUp() - if hasattr(yamllib, 'CSafeLoader'): - self.yaml_loader = yamllib.CSafeLoader - else: - self.yaml_loader = yamllib.SafeLoader - - self.override_config('disable_murano_agent', False, group='engine') - self.override_config('signing_key', False, group='engine') - - mock_host = mock.MagicMock() - mock_host.id = '1234' - mock_host.find_owner = lambda *args, **kwargs: mock_host - mock_host().getRegion.return_value = mock.MagicMock( - __class__=dsl.MuranoObjectInterface) - self.rabbit_mq_settings = { - 'agentRabbitMq': {'login': 'test_login', - 'password': 'test_password', - 'host': 'test_host', - 'port': 123, - 'virtual_host': 'test_virtual_host'} - } - mock_host().getRegion()().getConfig.return_value =\ - self.rabbit_mq_settings - - self.agent = agent.Agent(mock_host) - self.resources = mock.Mock(spec=resource_manager.ResourceManager) - self.resources.string.return_value = 'text' - - self.addCleanup(mock.patch.stopall) - - def _read(self, path): - execution_plan_dir = os.path.abspath( - os.path.join(__file__, '../execution_plans/') - ) - with open(execution_plan_dir + "/" + path) as file: - return file.read() - - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_send_creates_queue(self, mock_kombu): - self.agent.send_raw({}) - - # Verify that MQClient was instantiated, by checking whether - # kombu.Connection was called. - mock_kombu.Connection.assert_called_with( - 'amqp://{login}:{password}@{host}:{port}/{virtual_host}'.format( - **self.rabbit_mq_settings['agentRabbitMq'] - ), ssl=None) - - # Verify that client.declare() was called by checking whether kombu - # functions were called. - self.assertEqual(1, mock_kombu.Exchange.call_count) - self.assertEqual(1, mock_kombu.Queue.call_count) - - @mock.patch('murano.engine.system.agent.LOG') - def test_send_with_murano_agent_disabled(self, mock_log): - self.override_config('disable_murano_agent', True, group='engine') - - self.assertRaises(exceptions.PolicyViolationException, - self.agent.send_raw, {}) - - @mock.patch('murano.engine.system.agent.Agent._sign') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_send(self, mock_kombu, mock_sign): - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - - self.agent._queue = 'test_queue' - mock_sign.return_value = 'SIGNATURE' - plan = self.agent.build_execution_plan(template, self.resources) - with mock.patch.object(self.agent, 'build_execution_plan', - return_value=plan): - self.agent.send(template, self.resources) - - self.assertEqual(1, mock_kombu.Producer.call_count) - mock_kombu.Producer().publish.assert_called_once_with( - exchange='', - routing_key='test_queue', - body=json.dumps(plan), - message_id=plan['ID'], - headers={'signature': 'SIGNATURE'} - ) - - @mock.patch('murano.engine.system.agent.eventlet.event.Event') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_is_ready(self, mock_kombu, mock_event): - v2_result = yamllib.load( - self._read('application.template'), - Loader=self.yaml_loader) - - mock_event().wait.side_effect = None - mock_event().wait.return_value = v2_result - - self.assertTrue(self.agent.is_ready(1)) - - mock_event().wait.side_effect = agent.eventlet.Timeout - - self.assertFalse(self.agent.is_ready(1)) - - @mock.patch('murano.engine.system.agent.Agent._sign') - @mock.patch('murano.engine.system.agent.eventlet.event.Event') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_call_with_v1_result(self, mock_kombu, mock_event, mock_sign): - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - - test_v1_result = { - 'FormatVersion': '1.0.0', - 'IsException': False, - 'Result': [ - { - 'IsException': False, - 'Result': 'test_result' - } - ] - } - - mock_event().wait.side_effect = None - mock_event().wait.return_value = test_v1_result - mock_sign.return_value = 'SIGNATURE' - - self.agent._queue = 'test_queue' - plan = self.agent.build_execution_plan(template, self.resources) - mock.patch.object( - self.agent, 'build_execution_plan', return_value=plan).start() - - result = self.agent.call(template, self.resources, None) - self.assertIsNotNone(result) - self.assertEqual('test_result', result) - - self.assertEqual(1, mock_kombu.Producer.call_count) - mock_kombu.Producer().publish.assert_called_once_with( - exchange='', - routing_key='test_queue', - body=json.dumps(plan), - message_id=plan['ID'], - headers={'signature': 'SIGNATURE'} - ) - - @mock.patch('murano.engine.system.agent.Agent._sign') - @mock.patch('murano.engine.system.agent.eventlet.event.Event') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_call_with_v2_result(self, mock_kombu, mock_event, mock_sign): - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - - v2_result = yamllib.load( - self._read('application.template'), - Loader=self.yaml_loader) - - mock_event().wait.side_effect = None - mock_event().wait.return_value = v2_result - mock_sign.return_value = 'SIGNATURE' - - self.agent._queue = 'test_queue' - plan = self.agent.build_execution_plan(template, self.resources) - mock.patch.object( - self.agent, 'build_execution_plan', return_value=plan).start() - - result = self.agent.call(template, self.resources, None) - self.assertIsNotNone(result) - self.assertEqual(v2_result['Body'], result) - - self.assertEqual(1, mock_kombu.Producer.call_count) - mock_kombu.Producer().publish.assert_called_once_with( - exchange='', - routing_key='test_queue', - body=json.dumps(plan), - message_id=plan['ID'], - headers={'signature': 'SIGNATURE'} - ) - - @mock.patch('murano.engine.system.agent.eventlet.event.Event') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_call_with_no_result(self, mock_kombu, mock_event): - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - - mock_event().wait.side_effect = None - mock_event().wait.return_value = None - - result = self.agent.call(template, self.resources, None) - self.assertIsNone(result) - - @mock.patch('murano.engine.system.agent.eventlet.event.Event') - @mock.patch('murano.common.messaging.mqclient.kombu') - def test_call_except_timeout(self, mock_kombu, mock_event): - self.override_config('agent_timeout', 1, group='engine') - - mock_event().wait.side_effect = agent.eventlet.Timeout - - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - - expected_error_msg = 'The murano-agent did not respond within 1 '\ - 'seconds' - with self.assertRaisesRegex(exceptions.TimeoutException, - expected_error_msg): - self.agent.call(template, self.resources, None) - - @mock.patch('murano.engine.system.agent.datetime') - def test_process_v1_result_with_error_code(self, mock_datetime): - now = datetime.datetime.now().isoformat() - mock_datetime.datetime.now().isoformat.return_value = now - - v1_result = { - 'IsException': True, - 'Result': [ - 'Error Type', - 'Error Message', - 'Error Command', - 'Error Details' - ] - } - - expected_error = { - 'source': 'execution_plan', - 'command': 'Error Command', - 'details': 'Error Details', - 'message': 'Error Message', - 'type': 'Error Type', - 'timestamp': now - } - - self.assertTrue(self._are_exceptions_equal( - agent.AgentException, expected_error, - self.agent._process_v1_result, v1_result)) - - v1_result = { - 'IsException': False, - 'Result': [ - 'Error Type', - 'Error Message', - 'Error Command', - 'Error Details', - { - 'IsException': True, - 'Result': [ - 'Nested Error Type', - 'Nested Error Message', - 'Nested Error Command', - 'Nested Error Details' - ] - } - ] - } - - expected_error = { - 'source': 'command', - 'command': 'Nested Error Command', - 'details': 'Nested Error Details', - 'message': 'Nested Error Message', - 'type': 'Nested Error Type', - 'timestamp': now - } - - self.assertTrue(self._are_exceptions_equal( - agent.AgentException, expected_error, - self.agent._process_v1_result, v1_result)) - - def test_process_v2_result_with_error_code(self): - v2_result = { - 'Body': { - 'Message': 'Test Error Message', - 'AdditionalInfo': 'Test Additional Info', - 'ExtraAttr': 'Test extra data' - }, - 'FormatVersion': '2.0.0', - 'Name': 'TestApp', - 'ErrorCode': 123, - 'Time': 'Right now' - } - - expected_error = { - 'errorCode': 123, - 'message': 'Test Error Message', - 'details': 'Test Additional Info', - 'time': 'Right now', - 'extra': {'ExtraAttr': 'Test extra data'} - } - - self.assertTrue(self._are_exceptions_equal( - agent.AgentException, expected_error, - self.agent._process_v2_result, v2_result)) - - def _are_exceptions_equal(self, exception, expected_error, function, - result): - """Checks whether expected and returned dict from exception are equal. - - Because casting dicts to strings changes the ordering of the keys, - manual comparison of the result and expected result is performed. - """ - try: - # deepcopy must be performed because _process_v1_result - # deletes attrs from the original dict passed in. - self.assertRaises(exception, function, copy.deepcopy(result)) - function(result) - except exception as e: - e_string = str(e).replace("'", "\"").replace('None', 'null') - e_dict = json.loads(e_string) - self.assertEqual(sorted(expected_error.keys()), - sorted(e_dict.keys())) - for key, val in expected_error.items(): - self.assertEqual(val, e_dict[key]) - except Exception: - return False - return True - - -class TestExecutionPlan(base.MuranoTestCase): - def setUp(self): - super(TestExecutionPlan, self).setUp() - if hasattr(yamllib, 'CSafeLoader'): - self.yaml_loader = yamllib.CSafeLoader - else: - self.yaml_loader = yamllib.SafeLoader - - self.override_config('signing_key', False, group='engine') - - self.mock_murano_class = mock.Mock(spec=murano_type.MuranoClass) - self.mock_murano_class.name = 'io.murano.system.Agent' - self.mock_murano_class.declared_parents = [] - self.mock_object_store = mock.Mock(spec=object_store.ObjectStore) - - object_interface = mock.Mock(spec=murano_object.MuranoObject) - object_interface.id = '1234' - object_interface.find_owner = lambda *args, **kwargs: object_interface - - self.agent = agent.Agent(object_interface) - self.resources = mock.Mock(spec=resource_manager.ResourceManager) - self.resources.string.return_value = 'text' - self.uuids = ['ID1', 'ID2', 'ID3', 'ID4'] - self.mock_uuid = self._stub_uuid(self.uuids) - time_mock = mock.patch('time.time').start() - time_mock.return_value = 2 - self.addCleanup(mock.patch.stopall) - - def _read(self, path): - execution_plan_dir = os.path.abspath( - os.path.join(__file__, '../execution_plans/') - ) - with open(execution_plan_dir + "/" + path) as file: - return file.read() - - def test_execution_plan_v2_application_type(self): - template = yamllib.load( - self._read('application.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - self.assertEqual(self._get_application(), template) - - def test_execution_plan_v2_chef_type(self): - template = yamllib.load( - self._read('chef.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - self.assertEqual(self._get_chef(), template) - - def test_execution_plan_v2_telnet_application(self): - template = yamllib.load( - self._read('DeployTelnet.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - self.assertEqual(self._get_telnet_application(), template) - - def test_execution_plan_v2_tomcat_application(self): - template = yamllib.load( - self._read('DeployTomcat.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - - def test_execution_plan_v2_app_without_files(self): - template = yamllib.load( - self._read('application_without_files.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - self.assertEqual(self._get_app_without_files(), template) - - def test_execution_plan_v2_app_with_file_in_template(self): - template = yamllib.load( - self._read('template_with_files.template'), - Loader=self.yaml_loader) - template = self.agent.build_execution_plan(template, self.resources) - self.assertEqual(self._get_app_with_files_in_template(), template) - - def _get_application(self): - return { - 'Action': 'Execute', - 'Body': 'return deploy(args.appName).stdout\n', - 'Files': { - self.uuids[1]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'deployTomcat.sh' - }, - self.uuids[2]: { - 'Body': 'dGV4dA==\n', - 'BodyType': 'Base64', - 'Name': 'installer' - }, - self.uuids[3]: { - 'Body': 'dGV4dA==\n', - 'BodyType': 'Base64', - 'Name': 'common.sh' - } - }, - 'FormatVersion': '2.0.0', - 'ID': self.uuids[0], - 'Stamp': 20000, - 'Name': 'Deploy Tomcat', - 'Parameters': { - 'appName': '$appName' - }, - 'Scripts': { - 'deploy': { - 'EntryPoint': self.uuids[1], - 'Files': [ - self.uuids[2], - self.uuids[3] - ], - 'Options': { - 'captureStderr': True, - 'captureStdout': True - }, - 'Type': 'Application', - 'Version': '1.0.0' - } - }, - 'Version': '1.0.0' - } - - def _get_app_with_files_in_template(self): - return { - 'Action': 'Execute', - 'Body': 'return deploy(args.appName).stdout\n', - 'Files': { - self.uuids[1]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'deployTomcat.sh' - }, - 'updateScript': { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'updateScript' - }, - }, - 'FormatVersion': '2.0.0', - 'ID': self.uuids[0], - 'Stamp': 20000, - 'Name': 'Deploy Tomcat', - 'Parameters': { - 'appName': '$appName' - }, - 'Scripts': { - 'deploy': { - 'EntryPoint': self.uuids[1], - 'Files': [ - 'updateScript' - ], - 'Options': { - 'captureStderr': True, - 'captureStdout': True - }, - 'Type': 'Application', - 'Version': '1.0.0' - } - }, - 'Version': '1.0.0' - } - - def _get_app_without_files(self): - return { - 'Action': 'Execute', - 'Body': 'return deploy(args.appName).stdout\n', - 'Files': { - self.uuids[1]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'deployTomcat.sh' - }, - }, - 'FormatVersion': '2.0.0', - 'ID': self.uuids[0], - 'Stamp': 20000, - 'Name': 'Deploy Tomcat', - 'Parameters': { - 'appName': '$appName' - }, - 'Scripts': { - 'deploy': { - 'EntryPoint': self.uuids[1], - 'Files': [], - 'Options': { - 'captureStderr': True, - 'captureStdout': True - }, - 'Type': 'Application', - 'Version': '1.0.0' - } - }, - 'Version': '1.0.0' - } - - def _get_chef(self): - return { - 'Action': 'Execute', - 'Body': 'return deploy(args.appName).stdout\n', - 'Files': { - self.uuids[1]: { - 'Name': 'tomcat.git', - 'Type': 'Downloadable', - 'URL': 'https://github.com/tomcat.git' - }, - self.uuids[2]: { - 'Name': 'java', - 'Type': 'Downloadable', - 'URL': 'https://github.com/java.git' - }, - - }, - 'FormatVersion': '2.0.0', - 'ID': self.uuids[0], - 'Stamp': 20000, - 'Name': 'Deploy Chef', - 'Parameters': { - 'appName': '$appName' - }, - 'Scripts': { - 'deploy': { - 'EntryPoint': 'cookbook/recipe', - 'Files': [ - self.uuids[1], - self.uuids[2] - ], - 'Options': { - 'captureStderr': True, - 'captureStdout': True - }, - 'Type': 'Chef', - 'Version': '1.0.0' - } - }, - 'Version': '1.0.0' - } - - def _get_telnet_application(self): - return { - 'Action': 'Execute', - 'Body': 'return deploy(args.appName).stdout\n', - 'Files': { - self.uuids[1]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'deployTelnet.sh' - }, - self.uuids[2]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'installer.sh' - }, - self.uuids[3]: { - 'Body': 'text', - 'BodyType': 'Text', - 'Name': 'common.sh' - } - }, - 'FormatVersion': '2.0.0', - 'ID': self.uuids[0], - 'Stamp': 20000, - 'Name': 'Deploy Telnet', - 'Parameters': { - 'appName': '$appName' - }, - 'Scripts': { - 'deploy': { - 'EntryPoint': self.uuids[1], - 'Files': [ - self.uuids[2], - self.uuids[3] - ], - 'Options': { - 'captureStderr': True, - 'captureStdout': True - }, - 'Type': 'Application', - 'Version': '1.0.0' - } - }, - 'Version': '1.0.0' - } - - def _stub_uuid(self, values=None): - class FakeUUID(object): - def __init__(self, v): - self.hex = v - - if values is None: - values = [] - mock_uuid4 = mock.patch('uuid.uuid4').start() - mock_uuid4.side_effect = [FakeUUID(v) for v in values] - return mock_uuid4 - - @mock.patch('murano.engine.system.resource_manager.ResourceManager' - '._get_package') - def test_file_line_endings(self, _get_package): - class FakeResources(object): - """Class with only string() method from ResourceManager class""" - @staticmethod - def string(name, owner=None, binary=False): - return resource_manager.ResourceManager.string( - receiver=None, name=name, owner=owner, binary=binary) - - # make path equal to provided name inside resources.string() - package = mock.Mock() - package.get_resource.side_effect = lambda m: m - _get_package.return_value = package - - text = b"First line\nSecond line\rThird line\r\nFourth line" - modified_text = u"First line\nSecond line\nThird line\nFourth line" - encoded_text = base64.encode_as_text(text) + "\n" - resources = FakeResources() - - with tempfile.NamedTemporaryFile() as script_file: - script_file.write(text) - script_file.file.flush() - os.fsync(script_file.file.fileno()) - - # check that data has been written correctly - script_file.seek(0) - file_data = script_file.read() - self.assertEqual(text, file_data) - - # check resources.string() output - # text file - result = resources.string(script_file.name) - self.assertEqual(modified_text, result) - # binary file - result = resources.string(script_file.name, binary=True) - self.assertEqual(text, result) - - # check _get_body() output - filename = os.path.basename(script_file.name) - folder = os.path.dirname(script_file.name) - # text file - body = self.agent._get_body(filename, resources, folder) - self.assertEqual(modified_text, body) - # binary file - filename = '<{0}>'.format(filename) - body = self.agent._get_body(filename, resources, folder) - self.assertEqual(encoded_text, body) - - def test_queue_name(self): - self.agent._queue = 'test_queue' - self.assertEqual(self.agent.queue_name(), self.agent._queue) - - def test_prepare_message(self): - template = {'test'} - msg_id = 12345 - msg = self.agent._prepare_message(template, msg_id) - self.assertEqual(msg.id, msg_id) - self.assertEqual(msg._body, template) - - def test_execution_plan_v1(self): - template = yamllib.load( - self._read('application.template'), - Loader=self.yaml_loader) - rtn_template = self.agent._build_v1_execution_plan(template, - self.resources) - self.assertEqual(template, rtn_template) - - def test_get_array_item(self): - array = [1, 2, 3] - index = 2 - self.assertEqual(array[2], self.agent._get_array_item(array, index)) - - index = 3 - self.assertIsNone(self.agent._get_array_item(array, index)) - - def test_execution_plan_error(self): - template = None - self.assertRaises(ValueError, - self.agent.build_execution_plan, - template, self.resources) diff --git a/murano/tests/unit/engine/system/test_agent_listener.py b/murano/tests/unit/engine/system/test_agent_listener.py deleted file mode 100644 index f1da147e2..000000000 --- a/murano/tests/unit/engine/system/test_agent_listener.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 unittest import mock - -from murano.engine.system import agent_listener -from murano.tests.unit import base - - -class TestExecutionPlan(base.MuranoTestCase): - def setUp(self): - super(TestExecutionPlan, self).setUp() - self.override_config("disable_murano_agent", False, "engine") - self.agent = agent_listener.AgentListener("test") - self.addCleanup(mock.patch.stopall) - - def test_agent_ready(self): - self.assertEqual({}, self.agent._subscriptions) - results_queue = str('-execution-results-test') - self.assertEqual(results_queue, self.agent._results_queue) - self.assertTrue(self.agent._enabled) - self.assertIsNone(self.agent._receive_thread) - - def test_queue_name(self): - self.assertEqual(self.agent._results_queue, self.agent.queue_name()) - - @mock.patch("murano.engine.system.agent_listener.dsl.get_this") - @mock.patch("murano.engine.system." - "agent_listener.dsl.get_execution_session") - def test_subscribe_unsubscribe(self, execution_session, mock_this): - self.agent.subscribe('msg_id', 'event') - self.assertIn('msg_id', self.agent._subscriptions) - self.agent.unsubscribe('msg_id') - self.assertNotIn('msg_id', self.agent._subscriptions) - self.assertTrue(execution_session.called) - self.assertTrue(mock_this.called) diff --git a/murano/tests/unit/engine/system/test_garbage_collector.py b/murano/tests/unit/engine/system/test_garbage_collector.py deleted file mode 100644 index e3daced97..000000000 --- a/murano/tests/unit/engine/system/test_garbage_collector.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2016 Mirantis, Inc. -# -# Licensed 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 unittest import mock -import weakref - -from testtools import matchers - -from murano.dsl import exceptions -from murano.dsl import murano_method -from murano.dsl import murano_object -from murano.dsl import murano_type -from murano.dsl.principal_objects import garbage_collector -from murano.tests.unit import base - - -class TestGarbageCollector(base.MuranoTestCase): - def setUp(self): - super(TestGarbageCollector, self).setUp() - - self.subscriber = mock.MagicMock(spec=murano_object.MuranoObject) - self.subscriber.real_this = self.subscriber - - mock_class = mock.MagicMock(spec=murano_type.MuranoClass) - mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) - mock_method.name = "mockHandler" - mock_class.methods = mock.PropertyMock( - return_value={"mockHandler": mock_method}) - - def find_single_method(name): - if name != 'mockHandler': - raise exceptions.NoMethodFound(name) - - mock_class.find_single_method = find_single_method - self.subscriber.type = mock_class - - self.publisher = mock.MagicMock(spec=murano_object.MuranoObject) - self.publisher.real_this = self.publisher - - def test_set_dd(self): - self.publisher.destruction_dependencies = [] - garbage_collector.GarbageCollector.subscribe_destruction( - self.publisher, self.subscriber, handler="mockHandler") - dep = self.publisher.destruction_dependencies - self.assertThat(dep, matchers.HasLength(1)) - dep = dep[0] - self.assertEqual("mockHandler", dep["handler"]) - self.assertEqual(self.subscriber, dep["subscriber"]()) - - def test_unset_dd(self): - self.publisher.destruction_dependencies = [{ - "subscriber": weakref.ref(self.subscriber), - "handler": "mockHandler" - }] - garbage_collector.GarbageCollector.unsubscribe_destruction( - self.publisher, self.subscriber, handler="mockHandler") - self.assertEqual( - [], self.publisher.destruction_dependencies) - - def test_set_wrong_handler(self): - self.assertRaises( - exceptions.NoMethodFound, - garbage_collector.GarbageCollector.subscribe_destruction, - self.publisher, self.subscriber, handler="invalidHandler") - self.assertRaises( - exceptions.NoMethodFound, - garbage_collector.GarbageCollector.unsubscribe_destruction, - self.publisher, self.subscriber, handler="invalidHandler") diff --git a/murano/tests/unit/engine/system/test_instance_reporter.py b/murano/tests/unit/engine/system/test_instance_reporter.py deleted file mode 100644 index ce3b04b83..000000000 --- a/murano/tests/unit/engine/system/test_instance_reporter.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 unittest import mock - -from murano.db import models -from murano.engine.system import instance_reporter -from murano.tests.unit import base - -LATEST_VERSION = 1 - - -class TestInstanceReporter(base.MuranoTestCase): - def setUp(self): - super(TestInstanceReporter, self).setUp() - - self.environment = models.Environment( - name='test_environment', tenant_id='test_tenant_id', - version=LATEST_VERSION - ) - - self.addCleanup(mock.patch.stopall) - - @mock.patch("murano.db.models") - def test_track_untrack_application(self, mock_models): - instance = mock_models.Instance() - self.i_r = instance_reporter.InstanceReportNotifier(self.environment) - self.assertEqual(self.environment.id, self.i_r._environment_id) - self.assertIsNone(self.i_r.track_application(instance)) - self.assertIsNone(self.i_r.untrack_application(instance)) - - @mock.patch("murano.db.models") - def test_track_untrack_cloud_instance(self, mock_models): - instance = mock_models.Instance() - self.i_r = instance_reporter.InstanceReportNotifier(self.environment) - self.assertEqual(self.environment.id, self.i_r._environment_id) - self.assertIsNone(self.i_r.track_cloud_instance(instance)) - self.assertIsNone(self.i_r.untrack_cloud_instance(instance)) diff --git a/murano/tests/unit/engine/system/test_metadef_browser.py b/murano/tests/unit/engine/system/test_metadef_browser.py deleted file mode 100644 index eb8264548..000000000 --- a/murano/tests/unit/engine/system/test_metadef_browser.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 unittest import mock - -from murano.dsl import murano_object -from murano.engine.system import metadef_browser -from murano.tests.unit import base - - -class TestMetadefBrowser(base.MuranoTestCase): - def setUp(self): - super(TestMetadefBrowser, self).setUp() - self.this = mock.MagicMock() - - self.glance_client_mock = mock.MagicMock() - metadef_browser.MetadefBrowser._get_client = mock.MagicMock( - return_value=self.glance_client_mock) - metadef_browser.MetadefBrowser._client = mock.MagicMock( - return_value=self.glance_client_mock) - - self.mock_object = mock.Mock(spec=murano_object.MuranoObject) - - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_objects(self, execution_session): - namespace = None - self.metadef = metadef_browser.MetadefBrowser(self.this) - self.assertIsNotNone(self.metadef.get_objects(namespace)) - self.assertTrue(execution_session.called) - self.assertTrue(metadef_browser.MetadefBrowser. - _client.metadefs_object.list.called) - - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_namespaces(self, execution_session): - self.metadef = metadef_browser.MetadefBrowser(self.this) - resource_type = self.mock_object - self.assertIsNotNone(self.metadef.get_namespaces(resource_type)) - self.assertTrue(execution_session.called) - self.assertTrue(metadef_browser.MetadefBrowser. - _client.metadefs_namespace.list.called) diff --git a/murano/tests/unit/engine/system/test_net_explorer.py b/murano/tests/unit/engine/system/test_net_explorer.py deleted file mode 100644 index a6678f347..000000000 --- a/murano/tests/unit/engine/system/test_net_explorer.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 unittest import mock - -from oslo_config import cfg - -from murano.dsl import murano_method -from murano.dsl import murano_type -from murano.engine.system import net_explorer -from murano.tests.unit import base - -CONF = cfg.CONF - - -class TestNetExplorer(base.MuranoTestCase): - def setUp(self): - super(TestNetExplorer, self).setUp() - self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass) - self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) - - self._this = mock.MagicMock() - self.region_name = "test-region" - - self.addCleanup(mock.patch.stopall) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_available_cidr(self, execution_session, - mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - router_id = 12 - net_id = 144 - self.assertIsNotNone(ne.get_available_cidr(router_id, net_id)) - self.assertTrue(execution_session.called) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_list(self, execution_session, mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - self.assertEqual(ne.list_networks(), - ne._client.list_networks()['networks']) - self.assertEqual(ne.list_subnetworks(), - ne._client.list_subnets()['subnets']) - self.assertEqual(ne.list_ports(), ne._client.list_ports()['ports']) - self.assertEqual(ne.list_neutron_extensions(), - ne._client.list_extensions()['extensions']) - self.assertEqual(ne.get_default_dns(), ne._settings.default_dns) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_router_error(self, execution_session, - mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - self.assertRaises(KeyError, ne.get_default_router) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_ext_network_id_router(self, execution_session, - mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - router_id = 12 - self.assertIsNone(ne.get_external_network_id_for_router(router_id)) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_ext_network_id_network(self, execution_session, - mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - net_id = 144 - self.assertEqual(net_id, - ne.get_external_network_id_for_network(net_id)) - - @mock.patch("murano.engine.system.net_explorer.nclient") - @mock.patch("murano.engine.system.net_explorer.auth_utils") - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_get_cidr_none_router(self, execution_session, - mock_authentication, mock_nclient): - ne = net_explorer.NetworkExplorer(self._this, self.region_name) - router_id = None - self.assertEqual([], ne._get_cidrs_taken_by_router(router_id)) diff --git a/murano/tests/unit/engine/system/test_test_fixture.py b/murano/tests/unit/engine/system/test_test_fixture.py deleted file mode 100644 index 868413412..000000000 --- a/murano/tests/unit/engine/system/test_test_fixture.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2016 AT&T -# -# Licensed 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 testtools -from unittest import mock - -from murano.dsl import murano_method -from murano.dsl import murano_type -from murano.dsl import object_store -from murano.engine.system import test_fixture -from murano.tests.unit import base - - -class TestTestFixture(base.MuranoTestCase): - def setUp(self): - super(TestTestFixture, self).setUp() - - self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass) - self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) - self.mock_object_store = mock.Mock(spec=object_store.ObjectStore) - - self.test_fixture = test_fixture.TestFixture() - - self.addCleanup(mock.patch.stopall) - - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_finish_env(self, execution_session): - self.assertIsNone(self.test_fixture.finish_env()) - self.assertTrue(execution_session.called) - - @mock.patch("murano.dsl.helpers.get_execution_session") - def test_start_env(self, execution_session): - self.assertIsNone(self.test_fixture.start_env()) - self.assertTrue(execution_session.called) - - @mock.patch("murano.dsl.helpers.get_executor") - def test_load(self, executor): - executor.return_value = self.mock_object_store - model = "test" - tf_load = self.test_fixture.load(model) - self.assertEqual(self.test_fixture.load(model), tf_load) - - def test_assert_true(self): - expr = (7 > 3) - message = None - # Calls assertTrue in super class - self.test_fixture.assert_true(expr, message) - - def test_assert_false(self): - expr = (3 != 3) - message = None - # Calls assertFalse in super class - self.test_fixture.assert_false(expr, message) - - def test_assert_in(self): - needle = 7 - haystack = [3, 7, 8, 9, 22] - message = None - # Calls assertIn in super class - self.test_fixture.assert_in(needle, haystack, message) - - def test_assert_not_in(self): - needle = 16 - haystack = [3, 7, 8, 9, 22] - message = None - # Calls assertNotIn in super class - self.test_fixture.assert_not_in(needle, haystack, message) - - def test_assert_true_fails(self): - expr = (7 < 3) - message = None - self.assertRaises(AssertionError, - self.test_fixture.assert_true, expr, message) - - def test_assert_false_fails(self): - expr = (3 == 3) - message = None - self.assertRaises(AssertionError, - self.test_fixture.assert_false, expr, message) - - def test_assert_in_fails(self): - needle = 25 - haystack = [3, 7, 8, 9, 22] - message = None - self.assertRaises(testtools.matchers._impl.MismatchError, - self.test_fixture.assert_in, needle, - haystack, message) - - def test_assert_not_in_fails(self): - needle = 22 - haystack = [3, 7, 8, 9, 22] - message = None - self.assertRaises(testtools.matchers._impl.MismatchError, - self.test_fixture.assert_not_in, needle, - haystack, message) diff --git a/murano/tests/unit/engine/system/test_workflowclient.py b/murano/tests/unit/engine/system/test_workflowclient.py deleted file mode 100644 index 309337ff9..000000000 --- a/murano/tests/unit/engine/system/test_workflowclient.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (c) 2016 AT&T -# All Rights Reserved. -# -# Licensed 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 random -from unittest import mock - -try: - from mistralclient.api import client as mistralcli -except ImportError: - mistralcli = None - -from oslo_config import cfg - -from murano.dsl import murano_method -from murano.dsl import murano_type - -from murano.engine.system import workflowclient -from murano.tests.unit import base - - -CONF = cfg.CONF - - -def rand_name(name='murano'): - """Generates random string. - - :param name: Basic name - :return: - """ - return name + str(random.randint(1, 0x7fffffff)) - - -class TestMistralClient(base.MuranoTestCase): - def setUp(self): - super(TestMistralClient, self).setUp() - self.mistral_client_mock = mock.Mock() - self.mistral_client_mock.client = mock.MagicMock( - spec=mistralcli.client) - self._patch_client() - - self.mock_class = mock.MagicMock(spec=murano_type.MuranoClass) - self.mock_method = mock.MagicMock(spec=murano_method.MuranoMethod) - - self._this = mock.MagicMock() - self._this.owner = None - - self.addCleanup(mock.patch.stopall) - - def _patch_client(self): - self.mock_client = mock.Mock(return_value=self.mistral_client_mock) - self.client_patcher = mock.patch.object(workflowclient.MistralClient, - '_client', self.mock_client) - self.client_patcher.start() - - self.mock_create_client = mock.Mock( - return_value=self.mistral_client_mock) - self.create_client_patcher = mock.patch.object( - workflowclient.MistralClient, '_create_client', - self.mock_create_client) - self.create_client_patcher.start() - - def _unpatch_client(self): - self.client_patcher.stop() - self.create_client_patcher.stop() - - def test_run_with_execution_success_state(self): - test_output = '{"openstack": "foo", "__execution": "bar", "task":'\ - ' "baz"}' - mock_execution = mock.MagicMock( - id='123', state='SUCCESS', output=test_output) - self.mock_client.executions.create.return_value = mock_execution - self.mock_client.executions.get.return_value = mock_execution - - run_name = rand_name('test') - timeout = 1 - mc = workflowclient.MistralClient(self._this, 'regionOne') - output = mc.run(run_name, timeout) - - for prop in ['openstack', '__execution', 'task']: - self.assertFalse(hasattr(output, prop)) - - self.assertEqual({}, output) - - def test_run_with_execution_error_state(self): - mock_execution = mock.MagicMock( - id='123', state='ERROR', output="{'test_attr': 'test_val'}") - self.mock_client.executions.create.return_value = mock_execution - self.mock_client.executions.get.return_value = mock_execution - - run_name = rand_name('test') - timeout = 1 - mc = workflowclient.MistralClient(self._this, 'regionOne') - - expected_error_msg = 'Mistral execution completed with ERROR.'\ - ' Execution id: {0}. Output: {1}'\ - .format(mock_execution.id, mock_execution.output) - with self.assertRaisesRegex(workflowclient.MistralError, - expected_error_msg): - mc.run(run_name, timeout) - - def test_run_except_timeout_error(self): - mock_execution = mock.MagicMock( - id='123', state='TEST_STATE', output="{'test_attr': 'test_val'}") - self.mock_client.executions.create.return_value = mock_execution - self.mock_client.executions.get.return_value = mock_execution - - run_name = rand_name('test') - timeout = 1 - mc = workflowclient.MistralClient(self._this, 'regionOne') - - expected_error_msg = 'Mistral run timed out. Execution id: {0}.'\ - .format(mock_execution.id) - with self.assertRaisesRegex(workflowclient.MistralError, - expected_error_msg): - mc.run(run_name, timeout) - - def test_run_with_immediate_timeout(self): - mock_execution = mock.MagicMock( - id='123', state='ERROR', output="{'test_attr': 'test_val'}") - self.mock_client.executions.create.return_value = mock_execution - - run_name = rand_name('test') - timeout = 0 - mc = workflowclient.MistralClient(self._this, 'regionOne') - self.assertEqual(mock_execution.id, mc.run(run_name, timeout)) - - def test_upload(self): - mc = workflowclient.MistralClient(self._this, 'regionOne') - definition = rand_name('test') - self.assertIsNone(mc.upload(definition)) - self.assertTrue(workflowclient.MistralClient. - _client.workflows.create.called) - - @mock.patch('murano.engine.system.workflowclient.auth_utils') - def test_client_property(self, _): - self._unpatch_client() - - test_mistral_settings = { - 'url': rand_name('test_mistral_url'), - 'project_id': rand_name('test_project_id'), - 'endpoint_type': rand_name('test_endpoint_type'), - 'auth_token': rand_name('test_auth_token'), - 'user_id': rand_name('test_user_id'), - 'insecure': rand_name('test_insecure'), - 'cacert': rand_name('test_ca_cert') - } - - with mock.patch('murano.engine.system.workflowclient.CONF')\ - as mock_conf: - mock_conf.mistral = mock.MagicMock(**test_mistral_settings) - region_name = rand_name('test_region_name') - mc = workflowclient.MistralClient(self._this, region_name) - - mistral_client = mc._client - self.assertIsNotNone(mistral_client) diff --git a/murano/tests/unit/engine/test_mock_context_manager.py b/murano/tests/unit/engine/test_mock_context_manager.py deleted file mode 100644 index b1d0aa5c6..000000000 --- a/murano/tests/unit/engine/test_mock_context_manager.py +++ /dev/null @@ -1,133 +0,0 @@ -# Licensed 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 unittest import mock - -from yaql import contexts -from yaql import specs - -from murano.dsl import constants -from murano.dsl import executor -from murano.dsl import murano_type -from murano.engine import execution_session -from murano.engine import mock_context_manager -from murano.engine.system import test_fixture -from murano.tests.unit import base -from murano.tests.unit.dsl.foundation import object_model as om -from murano.tests.unit.dsl.foundation import runner -from murano.tests.unit.dsl.foundation import test_case - - -FIXTURE_CLASS = 'io.murano.system.Agent' -FIXTURE_FUNC = 'call' - - -def _get_fd(set_to_extract): - return list(set_to_extract)[0] - - -class TestMockContextManager(mock_context_manager.MockContextManager): - def __init__(self, functions): - super(TestMockContextManager, self).__init__() - self.__functions = functions - - def create_root_context(self, runtime_version): - root_context = super(TestMockContextManager, self).create_root_context( - runtime_version) - context = root_context.create_child_context() - for name, func in self.__functions.items(): - context.register_function(func, name) - return context - - -class MockRunner(runner.Runner): - def __init__(self, model, package_loader, functions): - if isinstance(model, str): - model = om.Object(model) - model = om.build_model(model) - if 'Objects' not in model: - model = {'Objects': model} - self.executor = executor.MuranoDslExecutor( - package_loader, TestMockContextManager(functions), - execution_session.ExecutionSession()) - self._root = self.executor.load(model).object - - -class TestMockManager(base.MuranoTestCase): - - def test_create_type_context(self): - mock_manager = mock_context_manager.MockContextManager() - mock_murano_class = mock.MagicMock(spec=murano_type.MuranoClass) - mock_murano_class.name = FIXTURE_CLASS - original_function = mock.MagicMock(spec=specs.FunctionDefinition) - original_function.is_method = True - original_function.name = FIXTURE_FUNC - original_context = contexts.Context() - p = mock.patch("inspect.getargspec", new=mock.MagicMock()) - p.start() - original_context.register_function(original_function) - mock_murano_class.context = original_context - p.stop() - - mock_function = mock.MagicMock(spec=specs.FunctionDefinition) - mock_function.is_method = True - mock_function.name = FIXTURE_FUNC - - mock_manager.class_mock_ctx[FIXTURE_CLASS] = [mock_function] - - result_context = mock_manager.create_type_context(mock_murano_class) - all_functions = result_context.collect_functions(FIXTURE_FUNC) - - # Mock function should go first, but result context should contain both - self.assertIs(mock_function, _get_fd(all_functions[0])) - self.assertIs(original_function, _get_fd(all_functions[1])) - - def test_create_root_context(self): - mock_manager = mock_context_manager.MockContextManager() - ctx_to_check = mock_manager.create_root_context( - constants.RUNTIME_VERSION_1_1) - inject_count = ctx_to_check.collect_functions('inject') - with_original_count = ctx_to_check.collect_functions('withOriginal') - - self.assertEqual(2, len(inject_count[0])) - self.assertEqual(1, len(with_original_count[0])) - - -class TestMockYaqlFunctions(test_case.DslTestCase): - - def setUp(self): - super(TestMockYaqlFunctions, self).setUp() - self.package_loader.load_package('io.murano', None).register_class( - test_fixture.TestFixture) - self.runner = MockRunner(om.Object('TestMocks'), - self.package_loader, self._functions) - - def test_inject_method_with_str(self): - self.runner.testInjectMethodWithString() - - def test_inject_object_with_str(self): - self.runner.testInjectObjectWithString() - - def test_inject_method_with_yaql_expr(self): - self.runner.testInjectMethodWithYaqlExpr() - - def test_inject_method_with_yaql_expr2(self): - self.runner.testInjectMethodWithYaqlExpr2() - - def test_inject_object_with_yaql_expr(self): - self.runner.testInjectObjectWithYaqlExpr() - - def test_with_original(self): - self.runner.testWithoriginal() - - def test_original_method(self): - self.runner.testOriginalMethod() diff --git a/murano/tests/unit/engine/test_package_loader.py b/murano/tests/unit/engine/test_package_loader.py deleted file mode 100644 index 65120d0dd..000000000 --- a/murano/tests/unit/engine/test_package_loader.py +++ /dev/null @@ -1,638 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 collections -from muranoclient.common import exceptions as muranoclient_exc -import os -import shutil -import tempfile -from unittest import mock - -import semantic_version -import testtools - -from murano.dsl import exceptions as dsl_exceptions -from murano.dsl import murano_package as dsl_package -from murano.engine import package_loader -from murano.packages import exceptions as pkg_exc -from murano.tests.unit import base -from murano.tests.unit import utils - - -class TestPackageCache(base.MuranoTestCase): - - def setUp(self): - super(TestPackageCache, self).setUp() - - self.location = tempfile.mkdtemp() - self.override_config('enable_packages_cache', True, 'engine') - self.override_config('packages_cache', self.location, 'engine') - - self._patch_loader_client() - self.loader = package_loader.ApiPackageLoader(None) - - def tearDown(self): - shutil.rmtree(self.location, ignore_errors=True) - super(TestPackageCache, self).tearDown() - - def _patch_loader_client(self): - self.murano_client_patcher = mock.patch( - 'murano.engine.package_loader.ApiPackageLoader.client') - self.murano_client_patcher.start() - self.murano_client = package_loader.ApiPackageLoader.client - - def _unpatch_loader_client(self): - self.murano_client_patcher.stop() - - @mock.patch('murano.engine.package_loader.auth_utils') - @mock.patch('murano.engine.package_loader.versionutils') - def test_client_property(self, mock_versionutils, mock_auth_utils): - self._unpatch_loader_client() - session = mock_auth_utils.get_client_session() - session_params = mock_auth_utils.get_session_client_parameters - session.auth.get_token.return_value = 'test_token' - session.get_endpoint.return_value = 'test_endpoint/v3' - session_params.return_value = {'endpoint': 'test_endpoint/v3'} - - self.override_config('packages_service', 'glance', group='engine') - - client = self.loader.client - - mock_versionutils.report_deprecated_feature.assert_called_once_with( - package_loader.LOG, - "'glance' packages_service option has been renamed " - "to 'glare', please update your configuration") - - self.assertIsNotNone(client) - self.assertIsNotNone(self.loader._glare_client) - - # Test whether client is initialized using different CONF. - self.override_config('packages_service', 'test_service', - group='engine') - - client = self.loader.client - - self.assertIsNotNone(client) - - def test_import_fixations_table(self): - test_fixations = { - 'test_package_1': [semantic_version.Version('1.1.0'), - semantic_version.Version('1.1.0')], - 'test_package_2': [semantic_version.Version('2.1.0'), - semantic_version.Version('2.4.3')] - } - expected = collections.defaultdict(set) - expected['test_package_1'] = set([semantic_version.Version('1.1.0')]) - expected['test_package_2'] = set([semantic_version.Version('2.1.0'), - semantic_version.Version('2.4.3')]) - - self.loader.import_fixation_table(test_fixations) - self.assertEqual(expected, self.loader._fixations) - - def test_register_package(self): - test_version = semantic_version.Version('1.1.0') - package = mock.MagicMock() - package.name = 'test_package_name' - package.version = test_version - package.classes = ['test_class_1', 'test_class_2'] - - self.loader.register_package(package) - self.assertEqual( - package, - self.loader._package_cache['test_package_name'][test_version]) - for class_name in package.classes: - self.assertEqual( - package, - self.loader._class_cache[class_name][test_version]) - - def test_load_package(self): - test_version = semantic_version.Version('1.1.0') - - package = mock.MagicMock() - package.name = 'test_package_name' - package.version = test_version - self.loader.import_fixation_table({package.name: [test_version]}) - self.loader.register_package(package) - - version_spec = semantic_version.Spec('>=1.0.0,<2.4.0') - retrieved_pkg = self.loader.load_package(package.name, version_spec) - self.assertEqual(retrieved_pkg, package) - - @testtools.skipIf(os.name == 'nt', "Doesn't work on Windows") - @mock.patch('murano.engine.package_loader.ApiPackageLoader.' - '_to_dsl_package') - def test_load_package_with_get_definiton(self, mock_to_dsl_package): - fqn = 'io.murano.apps.test_package' - package = mock.MagicMock() - package.id = 'test_package_id' - package.name = 'test_package_name' - package.fully_qualified_name = fqn - package.version = '2.5.3' - - path, _ = utils.compose_package( - 'test_package', self.location, archive_dir=self.location, - version=package.version) - with open(path, 'rb') as f: - package_data = f.read() - - self.murano_client.packages.filter = mock.MagicMock( - return_value=[package]) - self.murano_client.packages.download = mock.MagicMock( - return_value=package_data) - mock_to_dsl_package.return_value = package - - spec = semantic_version.Spec('*') - retrieved_pkg = self.loader.load_package(fqn, spec) - self.assertEqual(retrieved_pkg, package) - - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, package.id))) - self.assertTrue(os.path.isfile(os.path.join( - self.location, fqn, package.version, package.id, 'manifest.yaml'))) - self.murano_client.packages.download.assert_called_once_with( - package.id) - - expected_fixations = collections.defaultdict(set) - expected_fixations[fqn] = set( - [semantic_version.Version(package.version)]) - self.assertEqual(expected_fixations, self.loader._fixations) - self.loader.cleanup() - - def test_load_package_except_lookup_error(self): - expected_error_msg = 'Package "test_package_name" is not found' - invalid_specs = [semantic_version.Spec('>=1.1.1'), - semantic_version.Spec('<1.1.0'), - semantic_version.Spec('>=1.1.1,<1.1.0'), - semantic_version.Spec('==1.1.1')] - - fqn = 'io.murano.apps.test' - test_version = semantic_version.Version('1.1.0') - package = mock.MagicMock() - package.name = fqn - package.fully_qualified_name = fqn - package.version = test_version - self.loader.import_fixation_table({fqn: [test_version]}) - self.loader.register_package(package) - - with self.assertRaisesRegex(dsl_exceptions.NoPackageFound, - expected_error_msg): - for spec in invalid_specs: - self.loader.load_package('test_package_name', spec) - - def test_load_class_package(self): - fqn = 'io.murano.apps.test' - package = mock.MagicMock() - package.fully_qualified_name = fqn - package.classes = ['test_class_1', 'test_class_2'] - test_version = semantic_version.Version('1.1.0') - package.version = test_version - self.loader.register_package(package) - - spec = semantic_version.Spec('*') - for class_name in ['test_class_1', 'test_class_2']: - retrieved_pkg = self.loader.load_class_package(class_name, spec) - self.assertEqual(retrieved_pkg, package) - - @testtools.skipIf(os.name == 'nt', "Doesn't work on Windows") - def test_load_class_package_with_get_definition(self): - fqn = 'io.murano.apps.test' - path, name = utils.compose_package( - 'test', - self.location, archive_dir=self.location) - with open(path, 'rb') as f: - package_data = f.read() - spec = semantic_version.Spec('*') - - first_id, second_id, third_id = '123', '456', '789' - package = mock.MagicMock() - package.fully_qualified_name = fqn - package.id = first_id - package.version = '0.0.1' - - self.murano_client.packages.filter = mock.MagicMock( - return_value=[package]) - self.murano_client.packages.download = mock.MagicMock( - return_value=package_data) - - # load the package - self.loader.load_class_package(fqn, spec) - - # assert that everything got created - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, first_id))) - self.assertTrue(os.path.isfile(os.path.join( - self.location, fqn, package.version, first_id, 'manifest.yaml'))) - - # assert that we called download - self.assertEqual(self.murano_client.packages.download.call_count, 1) - - # now that the cache is in place, call it for the 2d time - self.loader._package_cache = {} - self.loader._class_cache = {} - self.loader.load_class_package(fqn, spec) - - # check that we didn't download a thing - self.assertEqual(self.murano_client.packages.download.call_count, 1) - - # changing id, new package would be downloaded. - package.id = second_id - self.loader._package_cache = {} - self.loader._class_cache = {} - self.loader.load_class_package(fqn, spec) - - # check that we didn't download a thing - self.assertEqual(self.murano_client.packages.download.call_count, 2) - - # check that old directories were not deleted - # we did not call cleanup and did not release the locks - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, first_id))) - - # check that new directories got created correctly - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, second_id))) - self.assertTrue(os.path.isfile(os.path.join( - self.location, fqn, package.version, second_id, 'manifest.yaml'))) - - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version))) - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, second_id))) - - # changing id, new package would be downloaded. - package.id = third_id - self.loader._package_cache = {} - self.loader._class_cache = {} - - # release all the locks - self.loader.cleanup() - self.loader.load_class_package(fqn, spec) - - # check that we didn't download a thing - self.assertEqual(self.murano_client.packages.download.call_count, 3) - - # check that old directories were *deleted* - self.assertFalse(os.path.isdir(os.path.join( - self.location, fqn, package.version, first_id))) - self.assertFalse(os.path.isdir(os.path.join( - self.location, fqn, package.version, second_id))) - - # check that new directories got created correctly - self.assertTrue(os.path.isdir(os.path.join( - self.location, fqn, package.version, third_id))) - self.assertTrue(os.path.isfile(os.path.join( - self.location, fqn, package.version, third_id, 'manifest.yaml'))) - - def test_load_class_package_except_lookup_error(self): - invalid_specs = [semantic_version.Spec('>=1.1.1'), - semantic_version.Spec('<1.1.0'), - semantic_version.Spec('>=1.1.1,<1.1.0'), - semantic_version.Spec('==1.1.1')] - - fqn = 'io.murano.apps.test' - test_version = semantic_version.Version('1.1.0') - package = mock.MagicMock() - package.name = fqn - package.fully_qualified_name = fqn - package.version = test_version - package.classes = ['test_class_1', 'test_class_2'] - self.loader.import_fixation_table({fqn: [test_version]}) - self.loader.register_package(package) - - for class_ in package.classes: - expected_error_msg = 'Package for class "{0}" is not found'\ - .format(class_) - with self.assertRaisesRegex(dsl_exceptions.NoPackageForClassFound, - expected_error_msg): - for spec in invalid_specs: - self.loader.load_class_package(class_, spec) - - @mock.patch('murano.engine.package_loader.LOG') - def test_get_definition_with_multiple_packages_returned(self, mock_log): - self.loader._execution_session = mock.MagicMock() - self.loader._execution_session.project_id = 'test_project_id' - - matching_pkg = mock.MagicMock(owner_id='test_project_id', - is_public=False) - public_pkg = mock.MagicMock(owner_id='another_test_project_id', - is_public=True) - other_pkg = mock.MagicMock(owner_id='another_test_project_id', - is_public=False) - - # Test package with matching owner_id is returned, despite ordering. - self.loader.client.packages.filter.return_value =\ - [public_pkg, matching_pkg, other_pkg] - expected_pkg = matching_pkg - retrieved_pkg = self.loader._get_definition({}) - self.assertEqual(expected_pkg, retrieved_pkg) - - # Test public package is returned, despite ordering. - self.loader.client.packages.filter.return_value =\ - [other_pkg, public_pkg] - expected_pkg = public_pkg - retrieved_pkg = self.loader._get_definition({}) - self.assertEqual(expected_pkg, retrieved_pkg) - - # Test other package is returned. - self.loader.client.packages.filter.return_value =\ - [other_pkg, other_pkg] - expected_pkg = other_pkg - retrieved_pkg = self.loader._get_definition({}) - self.assertEqual(expected_pkg, retrieved_pkg) - - mock_log.debug.assert_any_call( - 'Ambiguous package resolution: more than 1 package found for query' - ' "{opts}", will resolve based on the ownership' - .format(opts={'catalog': True})) - - @mock.patch('murano.engine.package_loader.LOG') - def test_get_definition_except_lookup_error(self, mock_log): - self.loader.client.packages.filter.return_value = [] - - with self.assertRaisesRegex(LookupError, None): - self.loader._get_definition({}) - - mock_log.debug.assert_called_once_with( - "There are no packages matching filter {opts}" - .format(opts={'catalog': True})) - mock_log.debug.reset_mock() - - self.loader.client.packages.filter.side_effect =\ - muranoclient_exc.HTTPException - - with self.assertRaisesRegex(LookupError, None): - self.loader._get_definition({}) - - mock_log.debug.assert_called_once_with( - 'Failed to get package definition from repository') - - @testtools.skipIf(os.name == 'nt', "Doesn't work on Windows") - @mock.patch('murano.engine.package_loader.LOG') - @mock.patch('murano.engine.package_loader.os') - @mock.patch('murano.engine.package_loader.load_utils') - def test_get_package_by_definition_except_package_load_error( - self, mock_load_utils, mock_os, mock_log): - # Test that the first instance of the exception is caught. - temp_directory = tempfile.mkdtemp(prefix='test-package-loader-', - dir=tempfile.tempdir) - mock_os.path.isdir.return_value = True - mock_os.path.join.return_value = temp_directory - mock_load_utils.load_from_dir.side_effect = pkg_exc.PackageLoadError - - fqn = 'io.murano.apps.test' - path, _ = utils.compose_package( - 'test', self.location, archive_dir=self.location) - with open(path, 'rb') as f: - package_data = f.read() - - package = mock.MagicMock() - package.fully_qualified_name = fqn - package.id = '123' - package.version = '0.0.1' - - self.murano_client.packages.download = mock.MagicMock( - return_value=package_data) - - self.loader._get_package_by_definition(package) - - mock_log.exception.assert_called_once_with( - 'Unable to load package from cache. Clean-up.') - mock_log.exception.reset_mock() - - # Test that the second instance of the exception is caught. - mock_os.path.isdir.return_value = [False, True] - - self.loader._get_package_by_definition(package) - - mock_log.exception.assert_called_once_with( - 'Unable to load package from cache. Clean-up.') - os.remove(temp_directory) - - @testtools.skipIf(os.name == 'nt', "Doesn't work on Windows") - def test_get_package_by_definition_except_http_exception(self): - fqn = 'io.murano.apps.test' - path, _ = utils.compose_package( - 'test', self.location, archive_dir=self.location) - - package = mock.MagicMock() - package.fully_qualified_name = fqn - package.id = '123' - package.version = '0.0.1' - - self.murano_client.packages.download.side_effect =\ - muranoclient_exc.HTTPException - - expected_error_msg = 'Error loading package id {0}:'.format(package.id) - with self.assertRaisesRegex(pkg_exc.PackageLoadError, - expected_error_msg): - self.loader._get_package_by_definition(package) - - @testtools.skipIf(os.name == 'nt', "Doesn't work on Windows") - @mock.patch('murano.engine.package_loader.LOG') - @mock.patch('murano.engine.package_loader.tempfile') - def test_get_package_by_definition_except_io_error(self, mock_tempfile, - mock_log): - fqn = 'io.murano.apps.test' - path, _ = utils.compose_package( - 'test', self.location, archive_dir=self.location) - with open(path, 'rb') as f: - package_data = f.read() - - package = mock.MagicMock() - package.fully_qualified_name = fqn - package.id = '123' - package.version = '0.0.1' - - self.murano_client.packages.download = mock.MagicMock( - return_value=package_data) - mock_package_file = mock.MagicMock( - write=mock.MagicMock(side_effect=IOError)) - mock_package_file.configure_mock(name='test_package_file') - mock_tempfile.NamedTemporaryFile().__enter__.return_value =\ - mock_package_file - - expected_error_msg = 'Unable to extract package data for {0}'\ - .format(package.id) - with self.assertRaisesRegex(pkg_exc.PackageLoadError, - expected_error_msg): - self.loader._get_package_by_definition(package) - - def test_try_cleanup_cache_with_null_package_directory(self): - # Test null package directory causes early return. - result = self.loader.try_cleanup_cache(None, None) - self.assertIsNone(result) - - @mock.patch('murano.engine.package_loader.shutil') - @mock.patch('murano.engine.package_loader.m_utils') - @mock.patch('murano.engine.package_loader.usage_mem_locks') - @mock.patch('murano.engine.package_loader.LOG') - @mock.patch('murano.engine.package_loader.os') - def test_try_cleanup_cache_except_os_error(self, mock_os, mock_log, - mock_usage_mem_locks, *args): - # Test first instance of OSError is handled. - mock_os.listdir.side_effect = OSError - - result = self.loader.try_cleanup_cache(None, None) - self.assertIsNone(result) - - # Test second instance of OSError is handled. - mock_os.reset_mock() - mock_os.listdir.return_value = {'1', '2'} - mock_os.listdir.side_effect = None - mock_os.remove.side_effect = OSError - mock_usage_mem_locks.__getitem__().write_lock().__enter__.\ - return_value = True - - result = self.loader.try_cleanup_cache('test_package_directory', None) - self.assertIsNone(result) - - self.assertIn("Couldn't delete lock file:", - str(mock_log.warning.mock_calls[0])) - - -class TestCombinedPackageLoader(base.MuranoTestCase): - - def setUp(self): - super(TestCombinedPackageLoader, self).setUp() - - location = os.path.dirname(__file__) - self.override_config('load_packages_from', [location], 'engine') - self.execution_session = mock.MagicMock() - self.loader = package_loader.CombinedPackageLoader( - self.execution_session) - self._patch_api_loader() - - self.local_pkg_name = 'io.murano.test.MyTest' - self.api_pkg_name = 'test.mpl.v1.app.Thing' - - def _patch_api_loader(self): - self.api_loader_patcher = mock.patch.object( - self.loader, 'api_loader', return_value=mock.MagicMock()) - self.api_loader_patcher.start() - - def _unpatch_api_loader(self): - self.api_loader_patcher.stop() - - def test_loaders_initialized(self): - self.assertEqual(1, len(self.loader.directory_loaders), - 'One directory class loader should be initialized' - ' since there is one valid murano pl package in the' - ' provided directory in config.') - self.assertIsInstance(self.loader.directory_loaders[0], - package_loader.DirectoryPackageLoader) - - def test_get_package_by_class_directory_loader(self): - spec = semantic_version.Spec('*') - result = self.loader.load_class_package(self.local_pkg_name, spec) - self.assertIsInstance(result, dsl_package.MuranoPackage) - - def test_get_package_by_name_directory_loader(self): - spec = semantic_version.Spec('*') - result = self.loader.load_package(self.local_pkg_name, spec) - self.assertIsInstance(result, dsl_package.MuranoPackage) - - def test_get_package_by_class_api_loader(self): - spec = semantic_version.Spec('*') - self.loader.load_package(self.api_pkg_name, spec) - - self.loader.api_loader.load_package.assert_called_with( - self.api_pkg_name, spec) - - def test_get_package_api_loader(self): - spec = semantic_version.Spec('*') - self.loader.load_class_package(self.api_pkg_name, spec) - - self.loader.api_loader.load_class_package.assert_called_with( - self.api_pkg_name, spec) - - def test_register_package(self): - test_package = mock.MagicMock() - self.loader.register_package(test_package) - self.loader.api_loader.register_package.assert_called_once_with( - test_package) - - def test_import_fixation_table(self): - self.loader.directory_loaders = [ - mock.MagicMock(), mock.MagicMock(), mock.MagicMock() - ] - test_fixations = { - 'test_package_1': [semantic_version.Version('1.1.0')], - 'test_package_2': [semantic_version.Version('2.1.0'), - semantic_version.Version('2.4.3')] - } - self.loader.import_fixation_table(test_fixations) - self.loader.api_loader.import_fixation_table.assert_called_once_with( - test_fixations) - for loader in self.loader.directory_loaders: - loader.import_fixation_table.assert_called_once_with( - test_fixations) - - def test_compact_fixation_table(self): - self.loader.directory_loaders = [ - mock.MagicMock(), mock.MagicMock(), mock.MagicMock() - ] - self.loader.compact_fixation_table() - self.loader.api_loader.compact_fixation_table.assert_called_once_with() - for loader in self.loader.directory_loaders: - loader.compact_fixation_table.assert_called_once_with() - - def test_export_fixation_table(self): - self._unpatch_api_loader() - - test_fixations = { - 'test_package_1': [semantic_version.Version('1.1.1')], - 'test_package_2': [semantic_version.Version('2.2.2'), - semantic_version.Version('3.3.3')] - } - self.loader.api_loader.import_fixation_table(test_fixations) - - expected_table = {'test_package_1': ['1.1.1'], - 'test_package_2': sorted(['2.2.2', '3.3.3'])} - table = self.loader.export_fixation_table() - table['test_package_2'] = sorted(table['test_package_2']) - - self.assertEqual(sorted(expected_table.items()), - sorted(table.items())) - - test_fixations = { - 'test_package_1': [semantic_version.Version('4.4.4')], - 'test_package_2': [semantic_version.Version('5.5.5'), - semantic_version.Version('6.6.6')], - 'test_package_3': [semantic_version.Version('7.7.7')] - } - self.loader.directory_loaders[0].import_fixation_table(test_fixations) - - expected_table = {'test_package_1': ['1.1.1', '4.4.4'], - 'test_package_2': ['2.2.2', '3.3.3', '5.5.5', - '6.6.6'], - 'test_package_3': ['7.7.7']} - table = self.loader.export_fixation_table() - for key, value in table.items(): - table[key] = sorted(value) - - self.assertEqual(sorted(expected_table.items()), - sorted(table.items())) diff --git a/murano/tests/unit/packages/__init__.py b/murano/tests/unit/packages/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/packages/hot_package/__init__.py b/murano/tests/unit/packages/hot_package/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/packages/hot_package/test.hot.1/Resources/FullTestName b/murano/tests/unit/packages/hot_package/test.hot.1/Resources/FullTestName deleted file mode 100644 index 1dfd08e09..000000000 --- a/murano/tests/unit/packages/hot_package/test.hot.1/Resources/FullTestName +++ /dev/null @@ -1,70 +0,0 @@ -FullName: test -resources: -parameters: - foo: - type: number - label: foo_label - description: foo_description - default: Default Value - constraints: - - length: - min: 0 - max: 5 - - range: - min: 0 - max: 5 - description: Range Description - - allowed_values: [0, 1, 2, 3, 4] - description: Allowed Values Description - - allowed_pattern: "[A-Za-z0-9]" - description: Allowed Pattern Description - bar: - type: boolean - constraints: - - length: - min: 0 - - range: - min: 0 - baz: - type: string - constraints: - - length: - max: 5 - - range: - max: 5 - description: Range Description -parameter_groups: - - group1: - parameters: - foo: - type: number - label: foo_label - description: foo_description - default: Default Value - constraints: - - length: - min: 0 - max: 5 - - range: - min: 0 - max: 5 - description: Range Description - - allowed_values: [0, 1, 2, 3, 4] - description: Allowed Values Description - - allowed_pattern: "[A-Za-z0-9]" - description: Allowed Pattern Description - bar: - type: boolean - constraints: - - length: - min: 0 - - range: - min: 0 - baz: - type: string - constraints: - - length: - max: 5 - - range: - max: 5 - description: Range Description diff --git a/murano/tests/unit/packages/hot_package/test.hot.1/properties_manifest.yaml b/murano/tests/unit/packages/hot_package/test.hot.1/properties_manifest.yaml deleted file mode 100644 index 96e8d0b8f..000000000 --- a/murano/tests/unit/packages/hot_package/test.hot.1/properties_manifest.yaml +++ /dev/null @@ -1,35 +0,0 @@ -parameters: - param1: - type: boolean - constraints: - - allowed_values: [True, False] - param2: - type: string - constraints: - - allowed_values: [bar] - - length: - max: 50 - - length: - min: 0 - - allowed_pattern: "[A-Za-z0-9]" - param3: - type: number - constraints: - - allowed_values: [0, 1, 2, 3, 4] - - length: - min: 0 - max: 5 - - range: - min: 0 - max: 4 - param4: - type: number - constraints: - - range: - min: -1000 - - range: - max: 1000 - param5: - type: json - param6: - type: comma_delimited_list diff --git a/murano/tests/unit/packages/hot_package/test.hot.1/template.yaml b/murano/tests/unit/packages/hot_package/test.hot.1/template.yaml deleted file mode 100644 index 1dfd08e09..000000000 --- a/murano/tests/unit/packages/hot_package/test.hot.1/template.yaml +++ /dev/null @@ -1,70 +0,0 @@ -FullName: test -resources: -parameters: - foo: - type: number - label: foo_label - description: foo_description - default: Default Value - constraints: - - length: - min: 0 - max: 5 - - range: - min: 0 - max: 5 - description: Range Description - - allowed_values: [0, 1, 2, 3, 4] - description: Allowed Values Description - - allowed_pattern: "[A-Za-z0-9]" - description: Allowed Pattern Description - bar: - type: boolean - constraints: - - length: - min: 0 - - range: - min: 0 - baz: - type: string - constraints: - - length: - max: 5 - - range: - max: 5 - description: Range Description -parameter_groups: - - group1: - parameters: - foo: - type: number - label: foo_label - description: foo_description - default: Default Value - constraints: - - length: - min: 0 - max: 5 - - range: - min: 0 - max: 5 - description: Range Description - - allowed_values: [0, 1, 2, 3, 4] - description: Allowed Values Description - - allowed_pattern: "[A-Za-z0-9]" - description: Allowed Pattern Description - bar: - type: boolean - constraints: - - length: - min: 0 - - range: - min: 0 - baz: - type: string - constraints: - - length: - max: 5 - - range: - max: 5 - description: Range Description diff --git a/murano/tests/unit/packages/hot_package/test.hot.2/Resources/FullTestName b/murano/tests/unit/packages/hot_package/test.hot.2/Resources/FullTestName deleted file mode 100644 index 1723934c0..000000000 --- a/murano/tests/unit/packages/hot_package/test.hot.2/Resources/FullTestName +++ /dev/null @@ -1 +0,0 @@ -FullName: template with invalid format \ No newline at end of file diff --git a/murano/tests/unit/packages/hot_package/test.hot.2/template.yaml b/murano/tests/unit/packages/hot_package/test.hot.2/template.yaml deleted file mode 100644 index 1723934c0..000000000 --- a/murano/tests/unit/packages/hot_package/test.hot.2/template.yaml +++ /dev/null @@ -1 +0,0 @@ -FullName: template with invalid format \ No newline at end of file diff --git a/murano/tests/unit/packages/hot_package/test_hot_package.py b/murano/tests/unit/packages/hot_package/test_hot_package.py deleted file mode 100644 index 54f2148f1..000000000 --- a/murano/tests/unit/packages/hot_package/test_hot_package.py +++ /dev/null @@ -1,197 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 os -import yaml - -from murano.packages import exceptions -import murano.packages.hot_package -import murano.packages.load_utils as load_utils -import murano.tests.unit.base as test_base - - -class TestHotPackage(test_base.MuranoTestCase): - - def _get_hot_package(self, source_directory): - manifest = { - 'FullName': 'FullTestName', - 'Version': '1.0.0', - 'Type': 'Application', - 'Name': 'TestName', - 'Description': 'TestDescription', - 'Author': 'TestAuthor', - 'Supplier': 'TestSupplier', - 'Logo:': 'TestLogo', - 'Tags': ['Tag1', 'Tag2'] - } - return murano.packages.hot_package.HotPackage( - None, None, source_directory=source_directory, - manifest=manifest - ) - - @classmethod - def setUpClass(cls): - super(TestHotPackage, cls).setUpClass() - this_dir = os.path.dirname(os.path.realpath(__file__)) - cls.test_dirs = [ - os.path.join(this_dir, 'test.hot.1'), - os.path.join(this_dir, 'test.hot.2'), - os.path.join(this_dir, 'test.hot.3') - ] - - manifest_path = os.path.join(cls.test_dirs[0], 'template.yaml') - cls.manifest = {} - with open(manifest_path) as manifest_file: - for key, value in yaml.safe_load(manifest_file).items(): - cls.manifest[key] = value - - properties_manifest_path = os.path.join(cls.test_dirs[0], - 'properties_manifest.yaml') - cls.properties_manifest = {} - with open(properties_manifest_path) as manifest_file: - for key, value in yaml.safe_load(manifest_file).items(): - cls.properties_manifest[key] = value - - def test_heat_files_generated(self): - package_dir = os.path.abspath( - os.path.join(__file__, - '../../test_packages/test.hot.v1.app_with_files') - ) - load_utils.load_from_dir(package_dir) - - files = murano.packages.hot_package.HotPackage._translate_files( - package_dir) - expected_result = { - "testHeatFile", - "middle_file/testHeatFile", - "middle_file/inner_file/testHeatFile", - "middle_file/inner_file2/testHeatFile" - } - msg = "hot files were not generated correctly" - self.assertSetEqual(expected_result, set(files), msg) - - def test_heat_files_generated_empty(self): - package_dir = os.path.abspath( - os.path.join(__file__, - '../../test_packages/test.hot.v1.app') - ) - load_utils.load_from_dir(package_dir) - - files = murano.packages.hot_package.HotPackage \ - ._translate_files(package_dir) - msg = "heat files were not generated correctly. Expected empty list" - self.assertEqual([], files, msg) - - def test_build_properties(self): - result = murano.packages.hot_package.HotPackage._build_properties( - self.properties_manifest, - validate_hot_parameters=True) - - self.assertIn('templateParameters', result) - params = result['templateParameters'] - self.assertEqual(6, len(params['Contract'])) - - param1 = params['Contract']['param1'] - param2 = params['Contract']['param2'] - param3 = params['Contract']['param3'] - param4 = params['Contract']['param4'] - param5 = params['Contract']['param5'] - param6 = params['Contract']['param6'] - - self.assertEqual("$.bool().check($ in list(True, False))", param1.expr) - self.assertEqual("$.string().check($ in list('bar'))." - "check(len($) <= 50).check(len($) >= 0)." - "check(matches($, '[A-Za-z0-9]'))", param2.expr) - self.assertEqual("$.int().check($ in list(0, 1, 2, 3, 4))" - ".check(len($) >= 0 and len($) <= 5)." - "check($ >= 0 and $ <= 4)", param3.expr) - self.assertEqual("$.int().check($ >= -1000).check($ <= " - "1000)", param4.expr) - self.assertEqual("$.string()", param5.expr) - self.assertEqual("$.string()", param6.expr) - - result = murano.packages.hot_package.HotPackage._build_properties( - self.properties_manifest, - validate_hot_parameters=False) - expected_result = { - 'Contract': {}, - 'Default': {}, - 'Usage': 'In' - } - self.assertEqual(expected_result, result['templateParameters']) - - def test_translate_param_to_contract_with_inappropriate_value(self): - self.assertRaisesRegex( - ValueError, - 'Unsupported parameter type', - murano.packages.hot_package.HotPackage. - _translate_param_to_contract, - {'type': 'Inappropriate value'} - ) - - def test_get_class_name(self): - hot_package = self._get_hot_package(self.test_dirs[0]) - translated_class, _ = hot_package.get_class(hot_package.full_name) - self.assertIsNotNone(translated_class) - self.assertEqual(translated_class, hot_package._translated_class) - - def test_get_class_name_with_invalid_template_name(self): - hot_package = self._get_hot_package(self.test_dirs[0]) - self.assertRaisesRegex( - exceptions.PackageClassLoadError, - 'Class not defined in this package', - hot_package.get_class, - None) - - def test_get_class_name_with_invalid_template_format(self): - hot_package = self._get_hot_package(self.test_dirs[1]) - self.assertRaisesRegex( - exceptions.PackageFormatError, - 'Not a HOT template', - hot_package.get_class, - hot_package.full_name) - - def test_translate_ui(self): - hot_package = self._get_hot_package(self.test_dirs[0]) - yaml = hot_package._translate_ui() - self.assertIsNotNone(yaml) - expected_application = ''' - "Application": - "?": - "classVersion": "1.0.0" - "package": "FullTestName" - "type": "FullTestName" - "name": !yaql "$.group0.name" - "templateParameters": - "bar": !yaql "$.group1.bar" - "baz": !yaql "$.group1.baz" - "foo": !yaql "$.group1.foo" - ''' - self.assertIn(expected_application.replace(' ', '').replace('\n', ''), - yaml.replace(' ', '').replace('\n', '')) - - def test_translate_ui_with_nonexistent_template(self): - hot_package = self._get_hot_package(self.test_dirs[2]) - self.assertRaisesRegex( - exceptions.PackageClassLoadError, - 'File with class definition not found', - hot_package._translate_ui) - - def test_translate_class_with_nonexistent_template(self): - hot_package = self._get_hot_package(self.test_dirs[2]) - self.assertRaisesRegex( - exceptions.PackageClassLoadError, - 'File with class definition not found', - hot_package._translate_class) diff --git a/murano/tests/unit/packages/mpl_package/Classes/test.class1 b/murano/tests/unit/packages/mpl_package/Classes/test.class1 deleted file mode 100644 index d4472833c..000000000 --- a/murano/tests/unit/packages/mpl_package/Classes/test.class1 +++ /dev/null @@ -1 +0,0 @@ -test.class1 diff --git a/murano/tests/unit/packages/mpl_package/UI/ui.yaml b/murano/tests/unit/packages/mpl_package/UI/ui.yaml deleted file mode 100644 index 6f1cda4a7..000000000 --- a/murano/tests/unit/packages/mpl_package/UI/ui.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed 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. - -Version: 2.2 - -Forms: - - appConfiguration: - fields: - - name: license - type: string - description: Apache License, Version 2.0 - hidden: false - required: false diff --git a/murano/tests/unit/packages/mpl_package/__init__.py b/murano/tests/unit/packages/mpl_package/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/packages/mpl_package/manifest.yaml b/murano/tests/unit/packages/mpl_package/manifest.yaml deleted file mode 100644 index 30e23db7d..000000000 --- a/murano/tests/unit/packages/mpl_package/manifest.yaml +++ /dev/null @@ -1,12 +0,0 @@ -Type: Application -FullName: test.pl.v1.app -Name: Test PL v1 App -Description: Test PL v1 Application -Author: Test Runner -Tags: [Linux] -Classes: - Class1: test.class1 - Class2: test.class2 -UI: ui.yaml -Meta: test.meta - diff --git a/murano/tests/unit/packages/mpl_package/test_mpl_package.py b/murano/tests/unit/packages/mpl_package/test_mpl_package.py deleted file mode 100644 index 317b0d20f..000000000 --- a/murano/tests/unit/packages/mpl_package/test_mpl_package.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 os -import yaml - -from murano.packages import exceptions -import murano.packages.mpl_package as mpl_package -import murano.tests.unit.base as test_base - - -class TestMPLPackage(test_base.MuranoTestCase): - - def setUp(cls): - super(TestMPLPackage, cls).setUp() - cls.source_directory = os.path.dirname(os.path.realpath(__file__)) - manifest_path = os.path.join(cls.source_directory, 'manifest.yaml') - cls.manifest = {} - with open(manifest_path) as manifest_file: - for key, value in yaml.safe_load(manifest_file).items(): - cls.manifest[key] = value - - def test_classes_property(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - classes = package.classes - self.assertIn('Class1', classes) - self.assertIn('Class2', classes) - - def test_ui_property(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - ui = package.ui - self.assertIsNotNone(ui) - self.assertIn(b'name: license', ui) - self.assertIn(b'type: string', ui) - self.assertIn(b'description: Apache License, Version 2.0', ui) - self.assertIn(b'hidden: false', ui) - self.assertIn(b'required: false', ui) - - def test_requirements_property(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - requirements = package.requirements - self.assertEqual(requirements, {}) - - # Override the Require property in cls.manifest and check new value. - self.manifest['Require'] = {'murano.plugins.example': 0} - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - requirements = package.requirements - self.assertEqual(requirements, {'murano.plugins.example': 0}) - del self.manifest['Require'] - - def test_meta_property(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - meta = package.meta - self.assertEqual(meta, 'test.meta') - - def test_get_class(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - stream, path = package.get_class('Class1') - expected_path = os.path.join(self.source_directory, 'Classes', - 'test.class1') - self.assertIn(b'test.class1', stream) - self.assertEqual(path, expected_path) - - def test_get_class_with_inappropriate_name(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - self.assertRaises(exceptions.PackageClassLoadError, - package.get_class, - 'Invalid name') - - def test_get_class_with_nonexistent_class(self): - package = mpl_package.MuranoPlPackage(None, None, - self.source_directory, - self.manifest) - self.assertRaises(exceptions.PackageClassLoadError, - package.get_class, - 'Class2') diff --git a/murano/tests/unit/packages/test_exceptions.py b/murano/tests/unit/packages/test_exceptions.py deleted file mode 100644 index c31633bcf..000000000 --- a/murano/tests/unit/packages/test_exceptions.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 murano.packages import exceptions -import murano.tests.unit.base as test_base - - -class TestExceptions(test_base.MuranoTestCase): - - def test_package_class_load_error(self): - class_name = 'test class name' - message = 'test message' - error = exceptions.PackageClassLoadError(class_name=class_name, - message=message) - expected = 'Unable to load class "{0}" from package: {1}'\ - .format(class_name, message) - self.assertEqual(expected, error.args[0]) - - def test_package_ui_load_error(self): - messages = ['', 'test_message'] - for message in messages: - error = exceptions.PackageUILoadError(message=message) - expected = 'Unable to load ui definition from package' - if message: - expected += ': {0}'.format(message) - self.assertEqual(expected, error.args[0]) - - def test_package_format_error(self): - messages = ['', 'test_message'] - for message in messages: - error = exceptions.PackageFormatError(message=message) - expected = 'Incorrect package format' - if message: - expected += ': {0}'.format(message) - self.assertEqual(expected, error.args[0]) diff --git a/murano/tests/unit/packages/test_load_utils.py b/murano/tests/unit/packages/test_load_utils.py deleted file mode 100644 index 069fd98ac..000000000 --- a/murano/tests/unit/packages/test_load_utils.py +++ /dev/null @@ -1,210 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 os -import random -import shutil -import string -import tempfile -from unittest import mock -import yaml -import zipfile - -from murano.packages import exceptions -from murano.packages import load_utils -import murano.tests.unit.base as test_base - - -class TestLoadUtils(test_base.MuranoTestCase): - - def setUp(cls): - super(TestLoadUtils, cls).setUp() - cls.temp_directories = [] - cls.temp_files = [] - - def _create_temp_dir(self): - temp_directory = tempfile.mkdtemp() - self.temp_directories.append(temp_directory) - return temp_directory - - def _create_temp_file(self): - temp_file = tempfile.NamedTemporaryFile(delete=True) - self.temp_files.append(temp_file) - return temp_file - - def _create_temp_zip_file(self, zip_path, manifest_path, - arcname='manifest.yaml'): - zip_ = zipfile.ZipFile(zip_path, 'w') - zip_.write(manifest_path, arcname=arcname) - zip_.close() - self.temp_files.append(zip_) - return zip_ - - def tearDown(cls): - super(TestLoadUtils, cls).tearDown() - for directory in cls.temp_directories: - if os.path.isdir(directory): - shutil.rmtree(directory) - for file in cls.temp_files: - if isinstance(file, zipfile.ZipFile): - if zipfile.is_zipfile(file.filename): - os.remove(file) - else: - if os.path.isfile(file.name): - os.remove(file.name) - - def _test_load_from_file(self, target_dir=None, drop_dir=True): - manifest_file_contents = dict( - Format='MuranoPL/1.1', - FullName='test_full_name', - Type='Application', - Description='test_description', - Author='test_author', - Supplier='test_supplier', - Tags=[] - ) - test_directory = self._create_temp_dir() - manifest_path = os.path.join(test_directory, 'manifest.yaml') - zip_path = os.path.join(test_directory, 'test_zip_load_utils.zip') - - with open(manifest_path, 'w') as manifest_file: - yaml.dump(manifest_file_contents, manifest_file, - default_flow_style=True) - self._create_temp_zip_file(zip_path, manifest_path) - - with load_utils.load_from_file(archive_path=zip_path, - target_dir=target_dir, - drop_dir=drop_dir) as plugin: - self.assertEqual('MuranoPL', plugin.format_name) - self.assertEqual('1.1.0', str(plugin.runtime_version)) - self.assertEqual(manifest_file_contents['FullName'], - plugin.full_name) - self.assertEqual(manifest_file_contents['Description'], - plugin.description) - self.assertEqual(manifest_file_contents['Author'], - plugin.author) - self.assertEqual(manifest_file_contents['Supplier'], - plugin.supplier) - self.assertEqual(manifest_file_contents['Tags'], - plugin.tags) - - def test_load_from_file(self): - self._test_load_from_file(target_dir=None, drop_dir=True) - - def test_load_from_file_with_custom_target_directory(self): - target_dir = self._create_temp_dir() - self._test_load_from_file(target_dir=target_dir, drop_dir=True) - - @mock.patch('murano.packages.load_utils.get_plugin_loader') - def test_load_from_file_with_invalid_handler(self, mock_plugin_loader): - mock_plugin_loader().get_package_handler = mock.MagicMock( - return_value=None) - test_format = 'Invalid Format' - manifest_file_contents = dict( - Format=test_format, - FullName='test_full_name', - Type='Application', - Description='test_description', - Author='test_author', - Supplier='test_supplier', - Tags=[] - ) - test_directory = self._create_temp_dir() - target_dir = self._create_temp_dir() - manifest_path = os.path.join(test_directory, 'manifest.yaml') - zip_path = os.path.join(test_directory, 'test_zip_load_utils.zip') - - with open(manifest_path, 'w') as manifest_file: - yaml.dump(manifest_file_contents, manifest_file, - default_flow_style=True) - self._create_temp_zip_file(zip_path, manifest_path) - - expected_error_msg = "Unsupported format {0}".format(test_format) - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - with load_utils.load_from_file(archive_path=zip_path, - target_dir=target_dir, - drop_dir=True): - pass - mock_plugin_loader().get_package_handler.assert_called_once_with( - test_format) - - def test_load_from_file_with_invalid_archive_path(self): - expected_error_msg = "Unable to find package file" - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - with load_utils.load_from_file('invalid file path'): - pass - - @mock.patch('murano.packages.load_utils.os') - def test_load_from_file_with_nonempty_target_directory(self, mock_os): - mock_os.listdir = mock.MagicMock(return_value=True) - temp_file = self._create_temp_file() - expected_error_msg = "Target directory is not empty" - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - this_dir = os.path.dirname(os.path.realpath(__file__)) - with load_utils.load_from_file(temp_file.name, - target_dir=this_dir): - pass - - def test_load_from_file_without_zip_file(self): - temp_file = self._create_temp_file() - expected_error_msg = "Uploaded file {0} is not a zip archive".\ - format(temp_file.name) - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - with load_utils.load_from_file(temp_file.name): - pass - - @mock.patch('murano.packages.load_utils.zipfile') - def test_load_from_file_handle_value_error(self, mock_zipfile): - test_error_msg = 'Random error message.' - expected_error_msg = "Couldn't load package from file: {0}".\ - format(test_error_msg) - mock_zipfile.is_zipfile = mock.MagicMock( - side_effect=ValueError(test_error_msg)) - temp_file = self._create_temp_file() - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - with load_utils.load_from_file(temp_file.name): - pass - mock_zipfile.is_zipfile.assert_called_once_with( - temp_file.name) - - def test_load_from_dir_without_source_directory(self): - expected_error_msg = 'Invalid package directory' - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - load_utils.load_from_dir('random_test_directory') - - def test_load_from_dir_with_invalid_source_directory(self): - source_directory = self._create_temp_dir() - expected_error_msg = 'Unable to find package manifest' - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - load_utils.load_from_dir(source_directory) - - @mock.patch('murano.packages.load_utils.os.path.isfile') - def test_load_from_dir_open_file_negative(self, mock_isfile): - mock_isfile.return_value = True - source_directory = self._create_temp_dir() - random_filename = ''.join(random.choice(string.ascii_lowercase) - for i in range(20)) - expected_error_msg = 'Unable to load due to' - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_msg): - load_utils.load_from_dir(source_directory, - filename=random_filename) diff --git a/murano/tests/unit/packages/test_package_base.py b/murano/tests/unit/packages/test_package_base.py deleted file mode 100644 index 3bab53c45..000000000 --- a/murano/tests/unit/packages/test_package_base.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 os -import random -import semantic_version -import shutil -import string -import tempfile -from unittest import mock - -from murano.packages import exceptions -from murano.packages import package_base -import murano.tests.unit.base as test_base - - -class TestPackageBase(test_base.MuranoTestCase): - - @classmethod - def setUpClass(cls): - super(TestPackageBase, cls).setUpClass() - package_base.PackageBase.__abstractmethods__ = set() - cls.source_directory = tempfile.mkdtemp(dir=tempfile.tempdir) - cls.version = semantic_version.Version.coerce('1.2.3') - cls.mock_manifest = { - 'Name': 'mock_display_name', - 'FullName': 'mock_full_name', - 'Type': 'Application', - 'Version': '1.2.3', - 'Description': 'test_description', - 'Author': 'test_author', - 'Supplier': 'test_supplier', - 'Tags': ['tag1', 'tag2', 'tag3'], - 'Logo': None - } - cls.package_base = package_base.PackageBase('test_format', - 'test_runtime_version', - cls.source_directory, - cls.mock_manifest) - - @classmethod - def tearDownClass(cls): - if os.path.isdir(cls.source_directory): - shutil.rmtree(cls.source_directory) - - def test_create_package_base_without_full_name(self): - with self.assertRaisesRegex(exceptions.PackageFormatError, - 'FullName is not specified'): - package_base.PackageBase('test_format', - 'test_runtime_version', - 'test_source_directory', - manifest={'FullName': None}) - - def test_create_package_base_with_invalid_full_name(self): - full_names = ['.invalid_name_1', 'invalid..name..2', 'invalid name 3'] - for full_name in full_names: - expected_error_message = 'Invalid FullName {0}'.format(full_name) - with self.assertRaisesRegex(exceptions.PackageFormatError, - expected_error_message): - package_base.PackageBase('test_format', - 'test_runtime_version', - 'test_source_directory', - manifest={'FullName': full_name}) - - def test_create_package_base_with_invalid_type(self): - package_type = 'Invalid' - with self.assertRaisesRegex(exceptions.PackageFormatError, - 'Invalid package Type {0}' - .format(package_type)): - package_base.PackageBase('test_format', - 'test_runtime_version', - 'test_source_directory', - manifest={'FullName': 'mock_full_name', - 'Type': package_type}) - - def test_requirements_negative(self): - with self.assertRaisesRegex(NotImplementedError, None): - self.package_base.requirements - - def test_classes_negative(self): - with self.assertRaisesRegex(NotImplementedError, None): - self.package_base.classes - - def test_get_class_negative(self): - with self.assertRaisesRegex(NotImplementedError, None): - self.package_base.get_class(None) - - def test_ui_negative(self): - with self.assertRaisesRegex(NotImplementedError, None): - self.package_base.ui - - def test_full_name(self): - self.assertEqual(self.mock_manifest['FullName'], - self.package_base.full_name) - - def test_source_directory(self): - self.assertEqual(self.source_directory, - self.package_base.source_directory) - - def test_version(self): - self.assertEqual(self.version, - self.package_base.version) - - def test_package_type(self): - self.assertEqual(self.mock_manifest['Type'], - self.package_base.package_type) - - def test_display_name(self): - self.assertEqual(self.mock_manifest['Name'], - self.package_base.display_name) - - def test_description(self): - self.assertEqual(self.mock_manifest['Description'], - self.package_base.description) - - def test_author(self): - self.assertEqual(self.mock_manifest['Author'], - self.package_base.author) - - def test_supplier(self): - self.assertEqual(self.mock_manifest['Supplier'], - self.package_base.supplier) - - def test_tags(self): - self.assertEqual(self.mock_manifest['Tags'], - self.package_base.tags) - - def test_logo_without_file_name(self): - self.assertIsNone(self.package_base.logo) - - def test_logo_with_invalid_logo_path(self): - expected_error_message = 'Unable to load logo' - self.package_base._logo = ''.join(random.choice(string.ascii_letters) - for _ in range(10)) - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_message): - self.package_base.logo - self.package_base._logo = self.mock_manifest['Logo'] - - @mock.patch('murano.packages.package_base.imghdr', - what=mock.MagicMock(return_value='xyz')) - def test_load_image_with_invalid_extension(self, mock_imghdr): - expected_error_message = 'Unsupported Format.' - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_message): - self.package_base._load_image('logo.xyz', 'logo.xyz', 'logo') - full_path = os.path.join(self.package_base._source_directory, - 'logo.xyz') - mock_imghdr.what.assert_called_once_with(full_path) - - @mock.patch('murano.packages.package_base.imghdr', - what=mock.MagicMock(return_value='png')) - @mock.patch('murano.packages.package_base.os') - def test_load_image_with_oversized_image(self, mock_os, mock_imghdr): - mock_os.stat.return_value = mock.MagicMock(st_size=5000 * 1024) - mock_os.isfile = mock.MagicMock(return_value=True) - expected_error_message = 'Max allowed size is {0}'.format(500 * 1024) - with self.assertRaisesRegex(exceptions.PackageLoadError, - expected_error_message): - self.package_base._load_image('logo.xyz', 'logo.xyz', 'logo') - - def test_meta(self): - self.assertIsNone(self.package_base.meta) - - def test_get_resource(self): - test_name = 'test_resource_name' - expected_dir = os.path.join(self.source_directory, 'Resources', - test_name) - self.assertEqual(expected_dir, self.package_base.get_resource( - test_name)) diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app/manifest.yaml b/murano/tests/unit/packages/test_packages/test.hot.v1.app/manifest.yaml deleted file mode 100644 index 96a423a2f..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app/manifest.yaml +++ /dev/null @@ -1,17 +0,0 @@ -Format: Heat.HOT/1.0 -Type: Application -FullName: test.hot.v1.app -Name: Test HOT v1 App -Description: Test HOT v1 Application -Author: Test Runner -Tags: [Linux] -Logo: test_logo.png -Supplier: - Name: Supplier Name - CompanyUrl: - Text: Example Company - Link: http://example.com - Logo: test_supplier_logo.png - Summary: Company summary goes here - Description: Marked up company description goes here - diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app/template.yaml b/murano/tests/unit/packages/test_packages/test.hot.v1.app/template.yaml deleted file mode 100644 index cb19d9b4a..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app/template.yaml +++ /dev/null @@ -1,8 +0,0 @@ -heat_template_version: '2013-05-23' -resources: - test-server: - type: OS::Nova::Server - properties: - flavor: test.flavor - image: Some image name - key_name: default diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_logo.png b/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_logo.png deleted file mode 100644 index e43885f14..000000000 Binary files a/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_logo.png and /dev/null differ diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_supplier_logo.png b/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_supplier_logo.png deleted file mode 100644 index e43885f14..000000000 Binary files a/murano/tests/unit/packages/test_packages/test.hot.v1.app/test_supplier_logo.png and /dev/null differ diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file/testHeatFile b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file/testHeatFile deleted file mode 100644 index d89a1e06b..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file/testHeatFile +++ /dev/null @@ -1 +0,0 @@ -inner file \ No newline at end of file diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file2/testHeatFile b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file2/testHeatFile deleted file mode 100644 index c4829b038..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/inner_file2/testHeatFile +++ /dev/null @@ -1 +0,0 @@ -inner file 2 \ No newline at end of file diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/testHeatFile b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/testHeatFile deleted file mode 100644 index 04a77da0f..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/middle_file/testHeatFile +++ /dev/null @@ -1 +0,0 @@ -middle file \ No newline at end of file diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/testHeatFile b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/testHeatFile deleted file mode 100644 index 1a010b1c0..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/Resources/HotFiles/testHeatFile +++ /dev/null @@ -1 +0,0 @@ -file \ No newline at end of file diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/manifest.yaml b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/manifest.yaml deleted file mode 100644 index 96a423a2f..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/manifest.yaml +++ /dev/null @@ -1,17 +0,0 @@ -Format: Heat.HOT/1.0 -Type: Application -FullName: test.hot.v1.app -Name: Test HOT v1 App -Description: Test HOT v1 Application -Author: Test Runner -Tags: [Linux] -Logo: test_logo.png -Supplier: - Name: Supplier Name - CompanyUrl: - Text: Example Company - Link: http://example.com - Logo: test_supplier_logo.png - Summary: Company summary goes here - Description: Marked up company description goes here - diff --git a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/template.yaml b/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/template.yaml deleted file mode 100644 index cb19d9b4a..000000000 --- a/murano/tests/unit/packages/test_packages/test.hot.v1.app_with_files/template.yaml +++ /dev/null @@ -1,8 +0,0 @@ -heat_template_version: '2013-05-23' -resources: - test-server: - type: OS::Nova::Server - properties: - flavor: test.flavor - image: Some image name - key_name: default diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/Classes/Thing.yaml b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/Classes/Thing.yaml deleted file mode 100644 index 216c10098..000000000 --- a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/Classes/Thing.yaml +++ /dev/null @@ -1,5 +0,0 @@ -Namespaces: - =: test.mpl.v1.app - -Name: Thing - diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest.yaml b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest.yaml deleted file mode 100644 index 9ebddd3d9..000000000 --- a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest.yaml +++ /dev/null @@ -1,19 +0,0 @@ -Format: 1.0 -Type: Application -FullName: test.mpl.v1.app -Description: Test V1 Application -Author: Test runner -Tags: [Linux] -Classes: - test.mpl.v1.app.Thing: Thing.yaml -Logo: test_logo.png -UI: ui.yaml -Supplier: - Name: Supplier Name - CompanyUrl: - Text: Example Company - Link: http://example.com - Logo: test_supplier_logo.png - Summary: Company summary goes here - Description: Marked up company description goes here - diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest_with_broken_logo.yaml b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest_with_broken_logo.yaml deleted file mode 100644 index 05d61fdc6..000000000 --- a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/manifest_with_broken_logo.yaml +++ /dev/null @@ -1,19 +0,0 @@ -Format: 1.0 -Type: Application -FullName: test.mpl.v1.app -Description: Test V1 Application -Author: Test runner -Tags: [Linux] -Classes: - test.mpl.v1.app.Thing: Thing.yaml -Logo: test_logo.png.not_valid -UI: ui.yaml -Supplier: - Name: Supplier Name - CompanyUrl: - Text: Example Company - Link: http://example.com - Logo: test_supplier_logo.png - Summary: Company summary goes here - Description: Marked up company description goes here - diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png deleted file mode 100644 index e43885f14..000000000 Binary files a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png and /dev/null differ diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png.not_valid b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png.not_valid deleted file mode 100644 index a3dfad3da..000000000 Binary files a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_logo.png.not_valid and /dev/null differ diff --git a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_supplier_logo.png b/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_supplier_logo.png deleted file mode 100644 index e43885f14..000000000 Binary files a/murano/tests/unit/packages/test_packages/test.mpl.v1.app/test_supplier_logo.png and /dev/null differ diff --git a/murano/tests/unit/packages/versions/__init__.py b/murano/tests/unit/packages/versions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/packages/versions/test_hot_v1.py b/murano/tests/unit/packages/versions/test_hot_v1.py deleted file mode 100644 index e11acb20b..000000000 --- a/murano/tests/unit/packages/versions/test_hot_v1.py +++ /dev/null @@ -1,45 +0,0 @@ -# -# Licensed 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 imghdr -import os - -import murano.packages.load_utils as load_utils -import murano.tests.unit.base as test_base - - -class TestHotV1(test_base.MuranoTestCase): - - def test_supplier_info_load(self): - package_dir = os.path.abspath( - os.path.join(__file__, '../../test_packages/test.hot.v1.app') - ) - package = load_utils.load_from_dir(package_dir) - - self.assertIsNotNone(package.supplier) - self.assertEqual('Supplier Name', package.supplier['Name']) - self.assertEqual({'Link': 'http://example.com', - 'Text': 'Example Company'}, - package.supplier['CompanyUrl']) - self.assertEqual( - 'Company summary goes here', - package.supplier['Summary'] - ) - self.assertEqual( - 'Marked up company description goes here', - package.supplier['Description'] - ) - self.assertEqual('test_supplier_logo.png', package.supplier['Logo']) - - self.assertEqual('png', imghdr.what('', package.supplier_logo)) diff --git a/murano/tests/unit/packages/versions/test_mpl_v1.py b/murano/tests/unit/packages/versions/test_mpl_v1.py deleted file mode 100644 index 865ab4e68..000000000 --- a/murano/tests/unit/packages/versions/test_mpl_v1.py +++ /dev/null @@ -1,43 +0,0 @@ -# -# Licensed 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 imghdr -import os - -import murano.packages.load_utils as load_utils -import murano.tests.unit.base as test_base - - -class TestMplV1(test_base.MuranoTestCase): - - def test_supplier_info_load(self): - package_dir = os.path.abspath( - os.path.join(__file__, '../../test_packages/test.mpl.v1.app') - ) - package = load_utils.load_from_dir(package_dir) - - self.assertIsNotNone(package.supplier) - self.assertEqual('Supplier Name', package.supplier['Name']) - self.assertEqual({'Link': 'http://example.com', - 'Text': 'Example Company'}, - package.supplier['CompanyUrl']) - self.assertEqual( - 'Company summary goes here', - package.supplier['Summary'] - ) - self.assertEqual( - 'Marked up company description goes here', - package.supplier['Description'] - ) - self.assertEqual('test_supplier_logo.png', package.supplier['Logo']) - - self.assertEqual('png', imghdr.what('', package.supplier_logo)) diff --git a/murano/tests/unit/policy/__init__.py b/murano/tests/unit/policy/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/policy/expected_rules_model.txt b/murano/tests/unit/policy/expected_rules_model.txt deleted file mode 100644 index 7d2e4261c..000000000 --- a/murano/tests/unit/policy/expected_rules_model.txt +++ /dev/null @@ -1,4 +0,0 @@ -murano:objects+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "c86104748a0c4907b4c5981e6d3bce9f", "io.murano.apps.linux.Git") -murano:properties+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "name", "git1") -murano:objects+("b840b71e-1805-46c5-9e6f-5a3d2c8d773e", "0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("b840b71e-1805-46c5-9e6f-5a3d2c8d773e", "name", "whjiyi3uzhxes6") \ No newline at end of file diff --git a/murano/tests/unit/policy/expected_rules_model_complex.txt b/murano/tests/unit/policy/expected_rules_model_complex.txt deleted file mode 100644 index c7b71818f..000000000 --- a/murano/tests/unit/policy/expected_rules_model_complex.txt +++ /dev/null @@ -1,5 +0,0 @@ -murano:properties+("ade378ce-00d4-4a33-99eb-7b4b6ea3ab97", "ipAddresses", "10.0.1.13") -murano:properties+("ade378ce-00d4-4a33-99eb-7b4b6ea3ab97", "ipAddresses", "16.60.90.90") -murano:properties+("ade378ce-00d4-4a33-99eb-7b4b6ea3ab97", "networks.customNetworks", "10.0.1.0") -murano:properties+("ade378ce-00d4-4a33-99eb-7b4b6ea3ab97", "networks.customNetworks", "10.0.2.0") -murano:properties+("ade378ce-00d4-4a33-99eb-7b4b6ea3ab97", "networks.customProp1.prop", "val") \ No newline at end of file diff --git a/murano/tests/unit/policy/expected_rules_model_renamed.txt b/murano/tests/unit/policy/expected_rules_model_renamed.txt deleted file mode 100644 index 7d2e4261c..000000000 --- a/murano/tests/unit/policy/expected_rules_model_renamed.txt +++ /dev/null @@ -1,4 +0,0 @@ -murano:objects+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "c86104748a0c4907b4c5981e6d3bce9f", "io.murano.apps.linux.Git") -murano:properties+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "name", "git1") -murano:objects+("b840b71e-1805-46c5-9e6f-5a3d2c8d773e", "0c810278-7282-4e4a-9d69-7b4c36b6ce6f", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("b840b71e-1805-46c5-9e6f-5a3d2c8d773e", "name", "whjiyi3uzhxes6") \ No newline at end of file diff --git a/murano/tests/unit/policy/expected_rules_model_two_instances.txt b/murano/tests/unit/policy/expected_rules_model_two_instances.txt deleted file mode 100644 index 54b8da0d0..000000000 --- a/murano/tests/unit/policy/expected_rules_model_two_instances.txt +++ /dev/null @@ -1,2 +0,0 @@ -murano:properties+("824b1718-09d8-4dd3-be32-9886f0d146d7", "flavor", "m1.medium") -murano:properties+("afa3266c-e2a7-4822-a176-11a48cdd7949", "flavor", "m1.medium") \ No newline at end of file diff --git a/murano/tests/unit/policy/expected_rules_wordpress.txt b/murano/tests/unit/policy/expected_rules_wordpress.txt deleted file mode 100644 index 0b9b93ea6..000000000 --- a/murano/tests/unit/policy/expected_rules_wordpress.txt +++ /dev/null @@ -1,145 +0,0 @@ -murano:objects+("83bff5acf8354816b08cf9b4917c898d", "de305d5475b4431badb2eb6b9e546013", "io.murano.Environment") -murano:properties+("83bff5acf8354816b08cf9b4917c898d", "name", "wordpress-env") -murano:parent_types+("83bff5acf8354816b08cf9b4917c898d", "io.murano.Object") -murano:objects+("c46770dec1db483ca2322914b842e50f", "83bff5acf8354816b08cf9b4917c898d", "io.murano.resources.NeutronNetwork") -murano:properties+("c46770dec1db483ca2322914b842e50f", "name", "wordpress-env-network") -murano:properties+("c46770dec1db483ca2322914b842e50f", "autogenerateSubnet", "True") -murano:properties+("c46770dec1db483ca2322914b842e50f", "autoUplink", "True") -murano:parent_types+("c46770dec1db483ca2322914b842e50f", "io.murano.resources.Network") -murano:parent_types+("c46770dec1db483ca2322914b842e50f", "io.murano.Object") -murano:objects+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "83bff5acf8354816b08cf9b4917c898d", "io.murano.databases.MySql") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "username", "admin") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "name", "MySqlDB") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "database", "wordpress") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "password", "Adminadmin#1") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.databases.SqlDatabase") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.Object") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.Application") -murano:objects+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "name", "qgijhi4uwe5wd8") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "assignFloatingIp", "False") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "networks.useFlatNetwork", "False") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "networks.useEnvironmentNetwork", "True") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "flavor", "m1.small") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "image", "murano-ubuntu") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.resources.LinuxInstance") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.Object") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.resources.Instance") -murano:objects+("d224db7d-081d-47a4-9333-9d2677b90b1f", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.apache.ApacheHttpServer") -murano:properties+("d224db7d-081d-47a4-9333-9d2677b90b1f", "name", "ApacheHttpServer") -murano:properties+("d224db7d-081d-47a4-9333-9d2677b90b1f", "enablePHP", "True") -murano:parent_types+("d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.Object") -murano:parent_types+("d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.Application") -murano:objects+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "name", "yeqsbi4uwejfg7") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "assignFloatingIp", "False") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "networks.useFlatNetwork", "False") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "networks.useEnvironmentNetwork", "True") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "flavor", "m1.small") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "image", "murano-ubuntu") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.resources.LinuxInstance") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.Object") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.resources.Instance") -murano:objects+("33e91790-5c44-40ce-9292-9dd4856325a0", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "username", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "name", "ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "database", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "password", "Adminadmin#1") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Object") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Application") -murano:objects+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "name", "gzxgdi4uwfjt57") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "assignFloatingIp", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useFlatNetwork", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useEnvironmentNetwork", "True") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "flavor", "m1.small") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "image", "murano-ubuntu") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.LinuxInstance") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.Object") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.Instance") -murano:objects+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.ZabbixAgent") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "probe", "ICMP") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "hostname", "zabbix") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "name", "ZabbixAgent") -murano:parent_types+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "io.murano.Object") -murano:parent_types+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "io.murano.Application") -murano:objects+("33e91790-5c44-40ce-9292-9dd4856325a0", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "username", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "name", "ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "database", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "password", "Adminadmin#1") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Object") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Application") -murano:objects+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "name", "gzxgdi4uwfjt57") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "assignFloatingIp", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useFlatNetwork", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useEnvironmentNetwork", "True") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "flavor", "m1.small") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "image", "murano-ubuntu") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.LinuxInstance") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.Object") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.Instance") -murano:objects+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.WordPress") -murano:properties+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "name", "WordPress") -murano:properties+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "dbPassword", "Adminadmin#1") -murano:properties+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "dbUser", "admin") -murano:properties+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "dbName", "wordpress") -murano:parent_types+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "io.murano.Object") -murano:parent_types+("fec71a35-8abc-4a8f-a5e4-91e77854d761", "io.murano.Application") -murano:objects+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.ZabbixAgent") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "probe", "ICMP") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "hostname", "zabbix") -murano:properties+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "name", "ZabbixAgent") -murano:parent_types+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "io.murano.Object") -murano:parent_types+("19a87de5-41ce-4e63-bf43-0e25dd409a1e", "io.murano.Application") -murano:objects+("33e91790-5c44-40ce-9292-9dd4856325a0", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "username", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "name", "ZabbixServer") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "database", "zabbix") -murano:properties+("33e91790-5c44-40ce-9292-9dd4856325a0", "password", "Adminadmin#1") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Object") -murano:parent_types+("33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.Application") -murano:objects+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "33e91790-5c44-40ce-9292-9dd4856325a0", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "name", "gzxgdi4uwfjt57") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "assignFloatingIp", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useFlatNetwork", "False") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "networks.useEnvironmentNetwork", "True") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "flavor", "m1.small") -murano:properties+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "image", "murano-ubuntu") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.LinuxInstance") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.Object") -murano:parent_types+("0b568a74-66c9-4e73-84d8-7dd1b96066ec", "io.murano.resources.Instance") -murano:objects+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "83bff5acf8354816b08cf9b4917c898d", "io.murano.databases.MySql") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "username", "admin") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "name", "MySqlDB") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "database", "wordpress") -murano:properties+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "password", "Adminadmin#1") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.databases.SqlDatabase") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.Object") -murano:parent_types+("e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.Application") -murano:objects+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "e7a13d3c-b3c9-42fa-975d-a47b142fd233", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "name", "qgijhi4uwe5wd8") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "assignFloatingIp", "False") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "networks.useFlatNetwork", "False") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "networks.useEnvironmentNetwork", "True") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "flavor", "m1.small") -murano:properties+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "image", "murano-ubuntu") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.resources.LinuxInstance") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.Object") -murano:parent_types+("825dc61d-217a-4fd8-80fc-43807f8d6fa2", "io.murano.resources.Instance") -murano:objects+("d224db7d-081d-47a4-9333-9d2677b90b1f", "83bff5acf8354816b08cf9b4917c898d", "io.murano.apps.apache.ApacheHttpServer") -murano:properties+("d224db7d-081d-47a4-9333-9d2677b90b1f", "name", "ApacheHttpServer") -murano:properties+("d224db7d-081d-47a4-9333-9d2677b90b1f", "enablePHP", "True") -murano:parent_types+("d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.Object") -murano:parent_types+("d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.Application") -murano:objects+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "d224db7d-081d-47a4-9333-9d2677b90b1f", "io.murano.resources.LinuxMuranoInstance") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "name", "yeqsbi4uwejfg7") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "assignFloatingIp", "False") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "networks.useFlatNetwork", "False") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "networks.useEnvironmentNetwork", "True") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "flavor", "m1.small") -murano:properties+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "image", "murano-ubuntu") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.resources.LinuxInstance") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.Object") -murano:parent_types+("3ddd4945-e4b8-4dac-9f85-537fc0957151", "io.murano.resources.Instance") \ No newline at end of file diff --git a/murano/tests/unit/policy/model.yaml b/murano/tests/unit/policy/model.yaml deleted file mode 100644 index baffa81ff..000000000 --- a/murano/tests/unit/policy/model.yaml +++ /dev/null @@ -1,21 +0,0 @@ -'?': {id: c86104748a0c4907b4c5981e6d3bce9f, type: io.murano.Environment} -applications: -- '?': - _26411a1861294160833743e45d0eaad9: {name: Git} - id: 0c810278-7282-4e4a-9d69-7b4c36b6ce6f - type: io.murano.apps.linux.Git - instance: - '?': {id: b840b71e-1805-46c5-9e6f-5a3d2c8d773e, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - image: murano-ubuntu - keyname: '' - name: whjiyi3uzhxes6 - name: git1 - repo: default.git -defaultNetworks: - environment: - '?': {id: 9d89844f20a642e0a1148e1fb8fd9e2e, type: io.murano.resources.NeutronNetwork} - name: quick-env-1-network - flat: null -name: quick-env-1 \ No newline at end of file diff --git a/murano/tests/unit/policy/model_complex.yaml b/murano/tests/unit/policy/model_complex.yaml deleted file mode 100644 index 88d7c1108..000000000 --- a/murano/tests/unit/policy/model_complex.yaml +++ /dev/null @@ -1,52 +0,0 @@ -'?': {id: 75a7423a67384ca3a6ee9f7350f3552b, type: io.murano.Environment} -applications: -- '?': {id: 7cb96c80-1cd7-40bc-88fe-47b905069890, type: io.murano.apps.apache.TomcatCluster} - balancer_ip: null - clustered: null - instances: - - '?': {id: be3c5155-6670-4cf6-9a28-a4574ff70b71, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: gcthmi4jn23l68 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - - '?': {id: ade378ce-00d4-4a33-99eb-7b4b6ea3ab97, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [10.0.1.13, 16.60.90.90] - keyname: '' - name: bmtjbi4jn23l89 - networks: - customNetworks: [10.0.1.0, 10.0.2.0] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - customProp1: - prop: val - securityGroupName: null - sharedIps: [] - name: Tomcat - port: 8080 - public_ip: null -defaultNetworks: - environment: - '?': {id: 7bac482a4c194b6e8dea81e6ce606db6, type: io.murano.resources.NeutronNetwork} - autoUplink: true - autogenerateSubnet: true - dnsNameservers: null - externalRouterId: null - name: quick-env-12-network - subnetCidr: null - flat: null -name: quick-env-12 \ No newline at end of file diff --git a/murano/tests/unit/policy/model_renamed.yaml b/murano/tests/unit/policy/model_renamed.yaml deleted file mode 100644 index d9f8d08f7..000000000 --- a/murano/tests/unit/policy/model_renamed.yaml +++ /dev/null @@ -1,21 +0,0 @@ -'?': {id: c86104748a0c4907b4c5981e6d3bce9f, type: io.murano.Environment} -apps: -- '?': - _26411a1861294160833743e45d0eaad9: {name: Git} - id: 0c810278-7282-4e4a-9d69-7b4c36b6ce6f - type: io.murano.apps.linux.Git - hostedOn: - '?': {id: b840b71e-1805-46c5-9e6f-5a3d2c8d773e, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - image: murano-ubuntu - keyname: '' - name: whjiyi3uzhxes6 - name: git1 - repo: default.git -defaultNetworks: - environment: - '?': {id: 9d89844f20a642e0a1148e1fb8fd9e2e, type: io.murano.resources.NeutronNetwork} - name: quick-env-1-network - flat: null -name: quick-env-1 \ No newline at end of file diff --git a/murano/tests/unit/policy/model_two_instances.yaml b/murano/tests/unit/policy/model_two_instances.yaml deleted file mode 100644 index f042c9e8f..000000000 --- a/murano/tests/unit/policy/model_two_instances.yaml +++ /dev/null @@ -1,26 +0,0 @@ -'?': {id: 00cc8fdf4cc54addb29133234d485edd, type: io.murano.Environment} -applications: -- '?': - _26411a1861294160833743e45d0eaad9: {name: Apache Tomcat Cluster} - id: 222256b6-07ef-4a69-a836-a6fe0df219e6 - type: io.murano.apps.apache.TomcatCluster - instances: - - '?': {id: afa3266c-e2a7-4822-a176-11a48cdd7949, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - image: murano-ubuntu - keyname: '' - name: qvamli4jgrkjn3 - - '?': {id: 824b1718-09d8-4dd3-be32-9886f0d146d7, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - image: murano-ubuntu - keyname: '' - name: krqlei4jgrkjo4 - name: Tomcat-cluster -defaultNetworks: - environment: - '?': {id: 854caae929e94290a2671281aef7ca9f, type: io.murano.resources.NeutronNetwork} - name: quick-env-3-network - flat: null -name: quick-env-3 \ No newline at end of file diff --git a/murano/tests/unit/policy/model_with_relations.yaml b/murano/tests/unit/policy/model_with_relations.yaml deleted file mode 100644 index 8fa9e0150..000000000 --- a/murano/tests/unit/policy/model_with_relations.yaml +++ /dev/null @@ -1,46 +0,0 @@ -'?': {id: 3409bdd0590e4c60b70fda5e6777ff96, type: io.murano.Environment} -applications: -- '?': - _26411a1861294160833743e45d0eaad9: {name: MySQL} - id: 0aafd67e-72e9-4ae0-bb62-fe724f77df2a - type: io.murano.databases.MySql - database: wordpress - instance: - '?': {id: ed8df2b0-ddd2-4009-b3c9-2e7a368f3cb8, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - image: murano-ubuntu - keyname: '' - name: cgnvki4ji4tzo2 - name: MySqlDB - password: admin - username: admin -- '?': - _26411a1861294160833743e45d0eaad9: {name: Apache HTTP Server} - id: 8ce94f23-f16a-40a1-9d9d-a877266c315d - type: io.murano.apps.apache.ApacheHttpServer - enablePHP: true - instance: - '?': {id: fc6b8c41-166f-4fc9-a640-d82009e0a03d, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.medium - image: murano-ubuntu - keyname: '' - name: ntxoai4ji58e53 - name: ApacheHttpServer -- '?': - _26411a1861294160833743e45d0eaad9: {name: WordPress} - id: 50fa68ff-cd9a-4845-b573-2c80879d158d - type: io.murano.apps.WordPress - database: 0aafd67e-72e9-4ae0-bb62-fe724f77df2a - dbName: wordpress - dbPassword: admin - dbUser: admin - monitoring: null - server: 8ce94f23-f16a-40a1-9d9d-a877266c315d -defaultNetworks: - environment: - '?': {id: 119de5707de34fca92fd2d6cd48bc26c, type: io.murano.resources.NeutronNetwork} - name: wordpress-env-network - flat: null -name: wordpress-env \ No newline at end of file diff --git a/murano/tests/unit/policy/modify/__init__.py b/murano/tests/unit/policy/modify/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/policy/modify/actions/__init__.py b/murano/tests/unit/policy/modify/actions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/policy/modify/actions/meta/ModelExamples.yaml b/murano/tests/unit/policy/modify/actions/meta/ModelExamples.yaml deleted file mode 100644 index e7e0328f3..000000000 --- a/murano/tests/unit/policy/modify/actions/meta/ModelExamples.yaml +++ /dev/null @@ -1,11 +0,0 @@ -Name: ModelExamples - -Properties: - sampleClass: - Contract: $.class(SampleClass1) - anotherSampleClass: - Contract: $.class(SampleClass1) - uninitialized: - Contract: $.class(SampleClass1) - Usage: Runtime - diff --git a/murano/tests/unit/policy/modify/actions/meta/SampleClass1.yaml b/murano/tests/unit/policy/modify/actions/meta/SampleClass1.yaml deleted file mode 100644 index 02132bbb3..000000000 --- a/murano/tests/unit/policy/modify/actions/meta/SampleClass1.yaml +++ /dev/null @@ -1,19 +0,0 @@ -Name: SampleClass1 - -Properties: - stringProperty: - Contract: $.string().notNull() - numberProperty: - Contract: $.int().notNull() - numberProperty: - Contract: $.int() - boolProperty: - Contract: $.bool() - classProperty: - Contract: [$.class(SampleClass2).notNull()] - dictProperty: - Contract: {$.int(): $.string()} - dictClassProperty: - Contract: {$.string(): $.class(SampleClass2)} - - diff --git a/murano/tests/unit/policy/modify/actions/meta/SampleClass2.yaml b/murano/tests/unit/policy/modify/actions/meta/SampleClass2.yaml deleted file mode 100644 index 884f4fcc1..000000000 --- a/murano/tests/unit/policy/modify/actions/meta/SampleClass2.yaml +++ /dev/null @@ -1,6 +0,0 @@ -Name: SampleClass2 - -Properties: - class2Property: - Contract: $.string().notNull() - diff --git a/murano/tests/unit/policy/modify/actions/test_action_manager.py b/murano/tests/unit/policy/modify/actions/test_action_manager.py deleted file mode 100644 index f69a17a9b..000000000 --- a/murano/tests/unit/policy/modify/actions/test_action_manager.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -import murano.policy.modify.actions.action_manager as am -from murano.policy.modify.actions import default_actions as da -import murano.tests.unit.policy.modify.actions.test_default_actions as tda - - -class TestActionManager(tda.ModifyActionTestCase): - - def test_loading(self): - self.skipTest('skipped until proper fix') - manager = am.ModifyActionManager() - self.assertEqual(da.RemoveObjectAction, - manager.load_action('remove-object')) - self.assertEqual(da.SetPropertyAction, - manager.load_action('set-property')) - self.assertEqual(da.AddObjectAction, - manager.load_action('add-object')) - self.assertEqual(da.RemoveRelationAction, - manager.load_action('remove-relation')) - - def test_caching(self): - self.skipTest('skipped until proper fix') - manager = am.ModifyActionManager() - manager._load_action = mock.MagicMock(wraps=manager._load_action) - manager.load_action('remove-object') - # second call is expected to get cached value - manager.load_action('remove-object') - manager._load_action.assert_called_once_with('remove-object') - - def test_no_such_action(self): - manager = am.ModifyActionManager() - self.assertRaises(ValueError, manager.load_action, 'no-such-action') - - def test_action_apply(self): - self.skipTest('skipped until proper fix') - with self._runner.session(): - manager = am.ModifyActionManager() - obj_id = self._dict_member.id - action_spec = 'remove-object: {object_id: %s}' % obj_id - manager.apply_action(self._obj, - action_spec) - - def test_action_apply_invalid_spec(self): - manager = am.ModifyActionManager() - self.assertRaises(ValueError, - manager.apply_action, self._obj, 'remove-object') diff --git a/murano/tests/unit/policy/modify/actions/test_default_actions.py b/murano/tests/unit/policy/modify/actions/test_default_actions.py deleted file mode 100644 index f40007446..000000000 --- a/murano/tests/unit/policy/modify/actions/test_default_actions.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 murano.policy.modify.actions.default_actions as da -import murano.tests.unit.dsl.foundation.object_model as om -import murano.tests.unit.dsl.foundation.test_case as tc - - -class ModifyActionTestCase(tc.DslTestCase): - def setUp(self): - super(ModifyActionTestCase, self).setUp() - self._list_member = om.Object('SampleClass2', class2Property='string2') - self._dict_member = om.Object('SampleClass2', class2Property='string2') - self._runner = self.new_runner( - om.Object( - 'ModelExamples', - sampleClass=om.Object( - 'SampleClass1', - stringProperty='string1', - dictProperty={1: 'a', 2: 'b'}, - dictClassProperty={ - 'key': self._dict_member}, - classProperty=[self._list_member]))) - self._obj = self._runner.root - - -class TestRemoveObjectAction(ModifyActionTestCase): - def test_remove(self): - with self._runner.session(): - self.assertIsNotNone(self._obj.get_property('sampleClass')) - object_id = self._obj.get_property('sampleClass').object_id - da.RemoveObjectAction(object_id=object_id).modify(self._obj) - self.assertIsNone(self._obj.get_property('sampleClass')) - - def test_remove_from_list(self): - with self._runner.session(): - self.assertEqual(1, len( - self._obj.get_property('sampleClass') - .get_property('classProperty'))) - da.RemoveObjectAction(object_id=self._list_member.id).modify( - self._obj) - self.assertEqual(0, len( - self._obj.get_property('sampleClass') - .get_property('classProperty'))) - self.assertNotIn(self._list_member.id, repr(self._obj)) - - def test_remove_from_dict(self): - with self._runner.session(): - self.assertEqual(1, len( - self._obj.get_property('sampleClass') - .get_property('dictClassProperty'))) - da.RemoveObjectAction(object_id=self._dict_member.id).modify( - self._obj) - self.assertEqual(0, len( - self._obj.get_property('sampleClass') - .get_property('dictClassProperty'))) - self.assertNotIn(self._dict_member.id, repr(self._obj)) - - def test_remove_not_exists(self): - with self._runner.session(): - action = da.RemoveObjectAction(object_id='not_exists') - self.assertRaises(ValueError, action.modify, self._obj) - - -class TestSetPropertyAction(ModifyActionTestCase): - def test_set_str(self): - self._test_set_value('stringProperty', 'test_string', 'test_string') - - def test_set_number(self): - self._test_set_value('numberProperty', '15', 15) - self._test_set_value('numberProperty', '50', 50) - self._test_set_value('numberProperty', 40, 40) - self._test_set_value('numberProperty', '-5', -5) - - def test_set_boolean(self): - self._test_set_value('boolProperty', True, True) - self._test_set_value('boolProperty', False, False) - - def test_set_dict(self): - self._test_set_value('dictProperty', {1: 'a'}, {1: 'a'}) - self._test_set_value('dictProperty', {1: 'b', 2: 'c'}, - {1: 'b', 2: 'c'}) - - def _test_set_value(self, property, raw_input, expected): - with self._runner.session(): - sample = self._obj.get_property('sampleClass') - da.SetPropertyAction(sample.object_id, prop_name=property, - value=raw_input).modify(self._obj) - self.assertEqual(expected, sample.get_property(property)) - - -class TestRemoveRelationAction(ModifyActionTestCase): - def test_remove(self): - with self._runner.session(): - self.assertIsNotNone(self._obj.get_property('sampleClass')) - da.RemoveRelationAction(self._obj.object_id, - prop_name='sampleClass').modify(self._obj) - self.assertIsNone(self._obj.get_property('sampleClass')) - - -class TestAddRelationAction(ModifyActionTestCase): - def test_add(self): - with self._runner.session(): - sample = self._obj.get_property('sampleClass') - self.assertIsNone(self._obj.get_property('anotherSampleClass')) - da.AddRelationAction(source_id=self._obj.object_id, - relation='anotherSampleClass', - target_id=sample.object_id).modify(self._obj) - self.assertIsNotNone(self._obj.get_property('anotherSampleClass')) - rel_target = self._obj.get_property('anotherSampleClass').object_id - self.assertEqual(sample.object_id, rel_target) - - -class TestAddObjectAction(ModifyActionTestCase): - def test_add_object(self): - with self._runner.session(): - self._obj.set_property('sampleClass', None) - self.assertIsNone(self._obj.get_property('sampleClass')) - da.AddObjectAction( - self._obj.object_id, - 'sampleClass', - 'SampleClass1', - {'stringProperty': 'test_add_obj'}).modify(self._obj) - self.assertIsNotNone(self._obj.get_property('sampleClass')) - self.assertEqual('test_add_obj', - self._obj.get_property('sampleClass') - .get_property('stringProperty')) diff --git a/murano/tests/unit/policy/test_congress_rules.py b/murano/tests/unit/policy/test_congress_rules.py deleted file mode 100644 index 8f4748c67..000000000 --- a/murano/tests/unit/policy/test_congress_rules.py +++ /dev/null @@ -1,299 +0,0 @@ -# Copyright (c) 2014 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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 inspect -import os.path -import unittest -import yaml - -from murano.common import uuidutils -from murano.dsl import helpers -from murano.dsl import package_loader -import murano.policy.congress_rules as congress - -TENANT_ID = 'de305d5475b4431badb2eb6b9e546013' - - -class MockPackageLoader(package_loader.MuranoPackageLoader): - def __init__(self, rules): - """Create rules like this: ['child->parent', 'child->parent2'].""" - - self._classes = {} - rules_dict = {} - for rule in rules: - split = rule.split('->') - rules_dict.setdefault(split[0], []).append(split[1]) - classes = (self.get_class(cls, rules_dict) for cls in rules_dict) - self._package = MockPackage(classes) - - def get_class(self, name, rules_dict): - if name in self._classes: - return self._classes[name] - parents = [self.get_class(parent, rules_dict) - for parent in rules_dict.get(name, [])] - result = MockClass({'name': name, 'declared_parents': parents}) - self._classes[name] = result - return result - - def register_package(self, package): - pass - - def load_class_package(self, class_name, version_spec): - return self._package - - def load_package(self, package_name, version_spec): - return self._package - - def export_fixation_table(self): - pass - - def import_fixation_table(self, fixations): - pass - - def compact_fixation_table(self): - pass - - -class MockPackage(object): - def __init__(self, classes): - self._classes = {} - for cls in classes: - self._classes[cls.name] = cls - - @property - def classes(self): - return self._classes.keys() - - def find_class(self, name, *args, **kwargs): - return self._classes.get(name) - - -class MockClass(object): - def __init__(self, entries): - self.__dict__.update(entries) - - def ancestors(self): - return helpers.traverse(self, lambda t: t.declared_parents) - - -class TestCongressRules(unittest.TestCase): - - def _load_file(self, file_name): - model_file = os.path.join( - os.path.dirname(inspect.getfile(self.__class__)), file_name) - - with open(model_file) as stream: - return yaml.safe_load(stream) - - def _create_rules_str(self, model_file, package_loader=None): - model = self._load_file(model_file) - - congress_rules = congress.CongressRulesManager() - rules = congress_rules.convert(model, package_loader, - tenant_id=TENANT_ID) - rules_str = ", \n".join(map(str, rules)) - - return rules_str - - def test_transitive_closure(self): - closure = congress.CongressRulesManager.transitive_closure( - [(1, 2), (2, 3), (3, 4)]) - self.assertIn((1, 4), closure) - self.assertIn((2, 4), closure) - - def test_empty_model(self): - congress_rules = congress.CongressRulesManager() - rules = congress_rules.convert(None) - self.assertEqual(0, len(rules)) - - def test_convert_simple_app(self): - rules_str = self._create_and_check_rules_str('model') - - self.assertNotIn("instance.", rules_str) - - def test_convert_model_two_instances(self): - rules_str = self._create_and_check_rules_str('model_two_instances') - - self.assertNotIn("\"instances\"", rules_str) - - def test_convert_model_with_relations(self): - rules_str = self._create_rules_str('model_with_relations.yaml') - - self.assertNotIn( - 'murano:properties+("50fa68ff-cd9a-4845-b573-2c80879d158d", ' - '"server", "8ce94f23-f16a-40a1-9d9d-a877266c315d")', rules_str) - - self.assertIn( - 'murano:relationships+("50fa68ff-cd9a-4845-b573-2c80879d158d", ' - '"8ce94f23-f16a-40a1-9d9d-a877266c315d", "server")', rules_str) - - self.assertIn( - 'murano:relationships+("0aafd67e-72e9-4ae0-bb62-fe724f77df2a", ' - '"ed8df2b0-ddd2-4009-b3c9-2e7a368f3cb8", "instance")', rules_str) - - def test_convert_model_transitive_relationships(self): - rules_str = self._create_rules_str('model_with_relations.yaml') - - self.assertIn( - 'murano:connected+("50fa68ff-cd9a-4845-b573-2c80879d158d", ' - '"8ce94f23-f16a-40a1-9d9d-a877266c315d")', rules_str) - - self.assertIn( - 'murano:connected+("8ce94f23-f16a-40a1-9d9d-a877266c315d", ' - '"fc6b8c41-166f-4fc9-a640-d82009e0a03d")', rules_str) - - def test_convert_model_services_relationship(self): - rules_str = self._create_rules_str('model_with_relations.yaml') - - self.assertIn( - 'murano:relationships+("3409bdd0590e4c60b70fda5e6777ff96", ' - '"8ce94f23-f16a-40a1-9d9d-a877266c315d", "services")', rules_str) - - self.assertIn( - 'murano:relationships+("3409bdd0590e4c60b70fda5e6777ff96", ' - '"50fa68ff-cd9a-4845-b573-2c80879d158d", "services")', rules_str) - - def test_convert_model_complex(self): - self._create_and_check_rules_str('model_complex') - - def test_convert_renamed_app(self): - self._create_and_check_rules_str('model_renamed') - - def test_parent_types(self): - - # grand-parent - # / \ - # parent1 parent2 - # \ / - # io.murano.apps.linux.Git - - package_loader = MockPackageLoader([ - 'io.murano.apps.linux.Git->parent1', - 'io.murano.apps.linux.Git->parent2', - 'parent1->grand-parent', - 'parent2->grand-parent' - ]) - - rules_str = self._create_rules_str('model.yaml', package_loader) - - self.assertIn( - 'murano:parent_types+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f",' - ' "parent1")', rules_str) - - self.assertIn( - 'murano:parent_types+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f",' - ' "parent2")', rules_str) - - self.assertIn( - 'murano:parent_types+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f",' - ' "grand-parent")', rules_str) - - self.assertIn( - 'murano:parent_types+("0c810278-7282-4e4a-9d69-7b4c36b6ce6f",' - ' "io.murano.apps.linux.Git")', rules_str) - - def test_to_dictionary(self): - """test to_dictionary - - If model contains object entry (not dict) - we try to convert to dict using 'to_dictionary' method. - """ - - class Struct(object): - def __init__(self, d): - self.__dict__ = d - - def to_dictionary(self): - return self.__dict__ - - def __getitem__(self, item): - return self.__dict__[item] - - d = {'?': {'id': '1', 'type': 't1'}, - 'apps': [Struct({'?': {'id': '2', 'type': 't2'}, - 'instances': [Struct( - {'?': {'id': '3', 'type': 't3'}})]})] - } - - model = Struct(d) - - congress_rules = congress.CongressRulesManager() - tenant_id = uuidutils.generate_uuid() - rules = congress_rules.convert(model, tenant_id=tenant_id) - rules_str = ", \n".join(map(str, rules)) - - self.assertIn('murano:objects+("1", "{0}", "t1")'.format(tenant_id), - rules_str) - self.assertIn('murano:objects+("2", "1", "t2")', rules_str) - self.assertIn('murano:objects+("3", "2", "t3")', rules_str) - - def test_environment_owner(self): - model = self._load_file("model.yaml") - congress_rules = congress.CongressRulesManager() - rules = congress_rules.convert(model, tenant_id='tenant1') - rules_str = ", \n".join(map(str, rules)) - self.assertIn('murano:objects+("c86104748a0c4907b4c5981e6d3bce9f", ' - '"tenant1", "io.murano.Environment")', rules_str) - - def test_wordpress(self): - package_loader = MockPackageLoader([ - 'io.murano.Environment->io.murano.Object', - 'io.murano.resources.NeutronNetwork->io.murano.resources.Network', - 'io.murano.resources.Network->io.murano.Object', - 'io.murano.databases.MySql->io.murano.databases.SqlDatabase', - 'io.murano.databases.MySql->io.murano.Application', - 'io.murano.databases.SqlDatabase->io.murano.Object', - 'io.murano.resources.LinuxInstance->io.murano.resources.Instance', - 'io.murano.resources.Instance->io.murano.Object', - 'io.murano.Application->io.murano.Object', - 'io.murano.apps.apache.ApacheHttpServer->io.murano.Application', - 'io.murano.apps.ZabbixServer->io.murano.Application', - 'io.murano.apps.ZabbixAgent->io.murano.Application', - 'io.murano.apps.WordPress->io.murano.Application', - - 'io.murano.resources.LinuxMuranoInstance->' - 'io.murano.resources.LinuxInstance' - ]) - - self._create_and_check_rules_str('wordpress', package_loader) - - def _create_and_check_rules_str(self, model_name, package_loader=None): - rules_str = self._create_rules_str( - '{0}.yaml'.format(model_name), package_loader) - self._check_expected_rules(rules_str, - 'expected_rules_{0}.txt'.format(model_name)) - return rules_str - - def _check_expected_rules(self, rules_str, expected_rules_file_name): - expected_rules_file = os.path.join( - os.path.dirname(inspect.getfile(self.__class__)), - expected_rules_file_name) - - s = '' - with open(expected_rules_file) as f: - for line in f: - line = line.rstrip('\n') - if line not in rules_str: - s += 'Expected rule not found:\n\t' + line + '\n' - - if len(s) > 0: - self.fail(s) - - def test_state_rule(self): - rules_str = self._create_rules_str('model.yaml') - - self.assertIn( - 'murano:states+("c86104748a0c4907b4c5981e6d3bce9f", "pending")', - rules_str) diff --git a/murano/tests/unit/policy/test_model_policy_enforcer.py b/murano/tests/unit/policy/test_model_policy_enforcer.py deleted file mode 100644 index e44db6c63..000000000 --- a/murano/tests/unit/policy/test_model_policy_enforcer.py +++ /dev/null @@ -1,145 +0,0 @@ -# Copyright (c) 2014 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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. - -try: - import congressclient -except ImportError: - congressclient = None -from unittest import mock - -from oslo_config import cfg - -from murano.common import engine -from murano.policy import model_policy_enforcer -from murano.tests.unit import base - -CONF = cfg.CONF - - -class TestModelPolicyEnforcer(base.MuranoTestCase): - def setUp(self): - if not congressclient: - self.skipTest('skipped ModelPolicyEnforcer tests') - super(TestModelPolicyEnforcer, self).setUp() - - self.obj = mock.Mock() - self.package_loader = mock.Mock() - - self.model_dict = mock.Mock() - self.obj.to_dictionary = mock.Mock(return_value=self.model_dict) - - self.task = { - 'action': {'method': 'deploy'}, - 'model': {'Objects': None, - 'project_id': 'tenant', - 'user_id': 'user' - }, - 'token': 'token', - 'project_id': 'tenant', - 'user_id': 'user', - 'id': 'environment.id' - } - - self.congress_client_mock = \ - mock.Mock(spec=congressclient.v1.client.Client) - model_policy_enforcer.ModelPolicyEnforcer._create_client = mock.Mock( - return_value=self.congress_client_mock) - - def test_enforcer_disabled(self): - executor = engine.TaskExecutor(self.task) - executor._model_policy_enforcer = mock.Mock() - - CONF.engine.enable_model_policy_enforcer = False - executor._validate_model(self.obj, self.package_loader, None) - - self.assertFalse(executor._model_policy_enforcer.validate.called) - - def test_enforcer_enabled(self): - executor = engine.TaskExecutor(self.task) - executor._model_policy_enforcer = mock.Mock() - - CONF.engine.enable_model_policy_enforcer = True - dsl_executor = mock.Mock() - executor._validate_model(self.obj, self.package_loader, dsl_executor) - - executor._model_policy_enforcer \ - .validate.assert_called_once_with(self.model_dict, - self.package_loader) - - def test_validation_pass(self): - self.congress_client_mock.execute_policy_action.return_value = \ - {"result": []} - model = {'?': {'id': '123', 'type': 'class'}} - enforcer = model_policy_enforcer.ModelPolicyEnforcer(mock.Mock()) - enforcer.validate(model) - - def test_validation_failure(self): - self.congress_client_mock.execute_policy_action.return_value = \ - {"result": ['predeploy_errors("123","instance1","failure")']} - - model = {'?': {'id': '123', 'type': 'class'}} - enforcer = model_policy_enforcer.ModelPolicyEnforcer(mock.Mock()) - self.assertRaises(model_policy_enforcer.ValidationError, - enforcer.validate, model) - - def test_modify(self): - model = {'?': {'id': '123', 'type': 'class'}} - obj = mock.MagicMock() - obj.to_dictionary = mock.Mock(return_value=model) - self.congress_client_mock.execute_policy_action.return_value = \ - {"result": [ - 'predeploy_modify("123","instance1",' - '"remove-object: {object_id: "12"}")']} - - action_manager = mock.MagicMock() - enforcer = model_policy_enforcer.ModelPolicyEnforcer( - mock.Mock(), action_manager) - - enforcer.modify(obj) - self.assertTrue(action_manager.apply_action.called) - - def test_parse_result(self): - congress_response = [ - 'unexpected response', - 'predeploy_errors("env1","instance1","Instance 1 has problem")', - 'predeploy_errors("env1","instance1","Instance 2 has problem")', - 'predeploy_errors("env2","instance1","Instance 3 has problem")' - ] - - enforcer = model_policy_enforcer.ModelPolicyEnforcer(None) - result = enforcer._parse_simulation_result( - 'predeploy_errors', 'env1', congress_response) - - self.assertNotIn("unexpected response", result) - self.assertIn("Instance 1 has problem", result) - self.assertIn("Instance 2 has problem", result) - self.assertNotIn("Instance 3 has problem", result) - - def test_none_model(self): - executor = engine.TaskExecutor(self.task) - executor._model_policy_enforcer = mock.Mock() - - CONF.engine.enable_model_policy_enforcer = True - dsl_executor = mock.Mock() - - executor._validate_model(None, self.package_loader, dsl_executor) - - self.assertFalse(executor._model_policy_enforcer.modify.called) - self.assertFalse(executor._model_policy_enforcer.validate.called) - - executor._validate_model(self.obj, self.package_loader, dsl_executor) - - self.assertTrue(executor._model_policy_enforcer.modify.called) - self.assertTrue(executor._model_policy_enforcer.validate.called) diff --git a/murano/tests/unit/policy/wordpress.yaml b/murano/tests/unit/policy/wordpress.yaml deleted file mode 100644 index 166e33bfd..000000000 --- a/murano/tests/unit/policy/wordpress.yaml +++ /dev/null @@ -1,174 +0,0 @@ -defaultNetworks: - environment: - '?': {id: c46770dec1db483ca2322914b842e50f, type: io.murano.resources.NeutronNetwork} - autoUplink: true - autogenerateSubnet: true - dnsNameservers: null - externalRouterId: null - name: wordpress-env-network - subnetCidr: null - flat: null -name: wordpress-env -'?': {type: io.murano.Environment, id: 83bff5acf8354816b08cf9b4917c898d} -applications: -- '?': {id: e7a13d3c-b3c9-42fa-975d-a47b142fd233, type: io.murano.databases.MySql} - database: wordpress - instance: - '?': {id: 825dc61d-217a-4fd8-80fc-43807f8d6fa2, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: qgijhi4uwe5wd8 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: MySqlDB - password: Adminadmin#1 - username: admin -- '?': {id: d224db7d-081d-47a4-9333-9d2677b90b1f, type: io.murano.apps.apache.ApacheHttpServer} - enablePHP: true - instance: - '?': {id: 3ddd4945-e4b8-4dac-9f85-537fc0957151, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: yeqsbi4uwejfg7 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: ApacheHttpServer -- '?': {id: 33e91790-5c44-40ce-9292-9dd4856325a0, type: io.murano.apps.ZabbixServer} - database: zabbix - instance: - '?': {id: 0b568a74-66c9-4e73-84d8-7dd1b96066ec, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: gzxgdi4uwfjt57 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: ZabbixServer - password: Adminadmin#1 - username: zabbix -- '?': {id: 19a87de5-41ce-4e63-bf43-0e25dd409a1e, type: io.murano.apps.ZabbixAgent} - hostname: zabbix - name: ZabbixAgent - probe: ICMP - server: - '?': {id: 33e91790-5c44-40ce-9292-9dd4856325a0, type: io.murano.apps.ZabbixServer} - database: zabbix - instance: - '?': {id: 0b568a74-66c9-4e73-84d8-7dd1b96066ec, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: gzxgdi4uwfjt57 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: ZabbixServer - password: Adminadmin#1 - username: zabbix -- '?': {id: fec71a35-8abc-4a8f-a5e4-91e77854d761, type: io.murano.apps.WordPress} - database: - '?': {id: e7a13d3c-b3c9-42fa-975d-a47b142fd233, type: io.murano.databases.MySql} - database: wordpress - instance: - '?': {id: 825dc61d-217a-4fd8-80fc-43807f8d6fa2, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: qgijhi4uwe5wd8 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: MySqlDB - password: Adminadmin#1 - username: admin - dbName: wordpress - dbPassword: Adminadmin#1 - dbUser: admin - monitoring: - '?': {id: 19a87de5-41ce-4e63-bf43-0e25dd409a1e, type: io.murano.apps.ZabbixAgent} - hostname: zabbix - name: ZabbixAgent - probe: ICMP - server: - '?': {id: 33e91790-5c44-40ce-9292-9dd4856325a0, type: io.murano.apps.ZabbixServer} - database: zabbix - instance: - '?': {id: 0b568a74-66c9-4e73-84d8-7dd1b96066ec, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: gzxgdi4uwfjt57 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: ZabbixServer - password: Adminadmin#1 - username: zabbix - name: WordPress - server: - '?': {id: d224db7d-081d-47a4-9333-9d2677b90b1f, type: io.murano.apps.apache.ApacheHttpServer} - enablePHP: true - instance: - '?': {id: 3ddd4945-e4b8-4dac-9f85-537fc0957151, type: io.murano.resources.LinuxMuranoInstance} - assignFloatingIp: false - flavor: m1.small - floatingIpAddress: null - image: murano-ubuntu - ipAddresses: [] - keyname: '' - name: yeqsbi4uwejfg7 - networks: - customNetworks: [] - primaryNetwork: null - useEnvironmentNetwork: true - useFlatNetwork: false - securityGroupName: null - sharedIps: [] - name: ApacheHttpServer \ No newline at end of file diff --git a/murano/tests/unit/services/__init__.py b/murano/tests/unit/services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/murano/tests/unit/services/test_actions.py b/murano/tests/unit/services/test_actions.py deleted file mode 100644 index 3857cfa10..000000000 --- a/murano/tests/unit/services/test_actions.py +++ /dev/null @@ -1,229 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from murano.services import actions -from murano.services import states -import murano.tests.unit.base as test_base - - -class TestActions(test_base.MuranoTestCase): - - def test_create_action_task(self): - mock_action_name = 'test_action_name' - mock_target_obj = '123' - mock_args = {'param1': 'something', 'param2': 'something else'} - mock_environment = mock.MagicMock(id='456', tenant_id='789') - mock_description = { - 'Objects': { - '?': { - 'id': '456' - }, - 'applications': [], - 'services': ['service1', 'service2'] - }, - 'project_id': 'XXX', - 'user_id': 'YYY' - } - mock_session = mock.MagicMock(description=mock_description) - mock_context = mock.Mock(auth_token='test_token', - project_id='test_tenant', - user='test_user') - expected_task = { - 'action': { - 'object_id': mock_target_obj, - 'method': mock_action_name, - 'args': mock_args - }, - 'model': { - 'Objects': { - '?': { - 'id': mock_environment.id - }, - 'applications': - mock_session.description['Objects']['services'] - }, - 'project_id': 'XXX', - 'user_id': 'YYY' - }, - 'token': 'test_token', - 'project_id': 'test_tenant', - 'user_id': 'test_user', - 'id': mock_environment.id - } - - task = actions.ActionServices.create_action_task(mock_action_name, - mock_target_obj, - mock_args, - mock_environment, - mock_session, - mock_context) - - self.assertEqual(expected_task, task) - - @mock.patch('murano.services.actions.models') - def test_update_task(self, mock_models): - mock_models.Task = mock.MagicMock() - mock_models.Status = mock.MagicMock() - mock_action = [{}, {'name': 'test_action_name'}] - mock_session = mock.MagicMock(environment_id='123', - state=states.SessionState.OPENED, - description={'Objects': ['o1', 'o2']}) - mock_task = {'action': 'test_action_name'} - mock_unit = mock.MagicMock(__enter__=mock.MagicMock(), - __exit__=mock.MagicMock()) - - actions.ActionServices.update_task(mock_action, mock_session, - mock_task, mock_unit) - - self.assertEqual(mock_session.environment_id, - mock_models.Task().environment_id) - self.assertEqual(dict(mock_session.description['Objects']), - mock_models.Task().description) - self.assertEqual(mock_task['action'], - mock_models.Task().action) - self.assertIn(mock_action[1]['name'], - mock_models.Status().text) - self.assertEqual('info', mock_models.Status().level) - mock_models.Task().statuses.append.assert_called_once_with( - mock_models.Status()) - self.assertEqual(2, mock_unit.add.call_count) - expected_session = mock_session - expected_session.state = states.SessionState.DEPLOYED - mock_unit.add.assert_any_call(expected_session) - mock_unit.add.assert_called_with(mock_models.Task()) - - @mock.patch('murano.services.actions.rpc') - @mock.patch('murano.services.actions.actions_db.update_task') - @mock.patch('murano.services.actions.ActionServices.create_action_task') - def test_submit_task(self, mock_create_action_task, mock_update_task, - mock_rpc): - mock_task = mock.MagicMock() - mock_create_action_task.return_value = mock_task - mock_update_task.return_value = '123' - mock_rpc.engine().handle_task = mock.MagicMock() - - test_action_name = 'test_action_name' - test_target_obj = 'test_target_obj' - test_args = 'test_args' - test_environment = 'test_environment' - test_session = 'test_session' - context = mock.Mock() - context.auth_token = 'test_token' - context.project_id = 'test_tenant' - context.user = 'test_user' - test_unit = 'test_unit' - - task_id = actions.ActionServices.submit_task(test_action_name, - test_target_obj, - test_args, - test_environment, - test_session, - context, - test_unit) - - self.assertEqual('123', task_id) - mock_create_action_task.assert_called_once_with(test_action_name, - test_target_obj, - test_args, - test_environment, - test_session, - context) - mock_update_task.assert_called_once_with(test_action_name, - test_session, mock_task, - test_unit) - mock_rpc.engine().handle_task.assert_called_once_with(mock_task) - - @mock.patch('murano.services.actions.actions_db.get_environment') - @mock.patch('murano.services.actions.ActionServices.submit_task') - def test_execute(self, mock_submit_task, mock_get_environment): - mock_environment = mock.MagicMock() - mock_task_id = 'test_task_id' - mock_get_environment.return_value = mock_environment - mock_submit_task.return_value = mock_task_id - - test_action_id = 'test_action_id' - test_description = [{ - '?': { - 'id': 'test_obj_id', - '_actions': { - test_action_id: { - 'name': 'test_action_1', - 'enabled': True - } - } - } - }] - test_session = mock.MagicMock(description=test_description) - test_unit = 'test_unit' - test_token = 'test_token' - test_args = None - expected_action_name = 'test_action_1' - expected_target_obj = 'test_obj_id' - - task_id = actions.ActionServices.execute(test_action_id, test_session, - test_unit, test_token, - test_args) - - self.assertEqual(mock_task_id, task_id) - mock_submit_task.assert_called_once_with(expected_action_name, - expected_target_obj, - {}, mock_environment, - test_session, test_token, - test_unit) - - @mock.patch('murano.services.actions.actions_db.get_environment') - def test_execute_with_invalid_action_id(self, mock_get_environment): - test_action_id = 'test_action_id' - test_session = mock.MagicMock(description=[]) - - with self.assertRaisesRegex(LookupError, 'Action is not found'): - actions.ActionServices.execute(test_action_id, test_session, - None, None, None) - - @mock.patch('murano.services.actions.actions_db.get_environment') - def test_execute_with_disabled_action(self, mock_get_environment): - test_action_id = 'test_action_id' - test_description = [{ - '?': { - 'id': 'test_obj_id', - '_actions': { - test_action_id: { - 'name': 'test_action_1', - 'enabled': False - } - } - } - }] - test_session = mock.MagicMock(description=test_description) - - with self.assertRaisesRegex(ValueError, - 'Cannot execute disabled action'): - actions.ActionServices.execute(test_action_id, test_session, - None, None, None) - - def test_get_result(self): - mock_task = mock.MagicMock(result='test_result') - mock_unit = mock.MagicMock() - mock_unit.query().filter_by().first.return_value = mock_task - result = actions.ActionServices.get_result('eid', 'tid', mock_unit) - self.assertEqual(mock_task.result, result) - - mock_unit.query().filter_by().first.return_value = None - task = actions.ActionServices.get_result('eid', 'tid', mock_unit) - self.assertIsNone(task) - mock_unit.query().filter_by.assert_any_call(id='tid', - environment_id='eid') diff --git a/murano/tests/unit/test_actions.py b/murano/tests/unit/test_actions.py deleted file mode 100644 index 586c5b075..000000000 --- a/murano/tests/unit/test_actions.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 murano.services import actions -from murano.tests.unit import base - - -class TestActionFinder(base.MuranoTestCase): - def test_simple_root_level_search(self): - model = { - '?': { - 'id': 'id1', - '_actions': { - 'ad_deploy': { - 'enabled': True, - 'name': 'deploy' - } - } - } - } - action = actions.ActionServices.find_action(model, 'ad_deploy') - self.assertEqual('deploy', action[1]['name']) - - def test_recursive_action_search(self): - model = { - '?': { - 'id': 'id1', - '_actions': {'ad_deploy': {'enabled': True, 'name': 'deploy'}} - }, - 'property': { - '?': { - 'id': 'id2', - '_actions': { - 'ad_scale': {'enabled': True, 'name': 'scale'} - } - }, - } - } - action = actions.ActionServices.find_action(model, 'ad_scale') - self.assertEqual('scale', action[1]['name']) diff --git a/murano/tests/unit/test_engine.py b/murano/tests/unit/test_engine.py deleted file mode 100644 index ac77a3b8f..000000000 --- a/murano/tests/unit/test_engine.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) 2014 Mirantis Inc. -# -# Licensed 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 re -from unittest import mock - -import semantic_version -import yaql -from yaql.language import exceptions -from yaql.language import utils - -import murano.dsl.helpers as helpers -import murano.dsl.namespace_resolver as ns_resolver -import murano.dsl.yaql_expression as yaql_expression -from murano.tests.unit import base - -ROOT_CLASS = 'io.murano.Object' - - -class TestNamespaceResolving(base.MuranoTestCase): - def test_fails_w_empty_name(self): - resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'}) - - self.assertRaises(ValueError, resolver.resolve_name, None) - - def test_fails_w_unknown_prefix(self): - resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'}) - name = 'unknown_prefix:example.murano' - - self.assertRaises(KeyError, resolver.resolve_name, name) - - def test_fails_w_prefix_wo_name(self): - resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'}) - name = 'sys:' - - self.assertRaises(ValueError, resolver.resolve_name, name) - - def test_fails_w_excessive_prefix(self): - ns = {'sys': 'com.example.murano.system'} - resolver = ns_resolver.NamespaceResolver(ns) - invalid_name = 'sys:excessive_ns:muranoResource' - - self.assertRaises(ValueError, resolver.resolve_name, invalid_name) - - def test_empty_prefix_is_default(self): - resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'}) - # name without prefix delimiter - name = 'some.arbitrary.name' - - resolved_name = resolver.resolve_name(':' + name) - - self.assertEqual( - 'com.example.murano.some.arbitrary.name', resolved_name) - - def test_resolves_specified_ns_prefix(self): - ns = {'sys': 'com.example.murano.system'} - resolver = ns_resolver.NamespaceResolver(ns) - short_name, full_name = 'sys:File', 'com.example.murano.system.File' - - resolved_name = resolver.resolve_name(short_name) - - self.assertEqual(full_name, resolved_name) - - def test_resolves_current_ns(self): - resolver = ns_resolver.NamespaceResolver({'=': 'com.example.murano'}) - short_name, full_name = 'Resource', 'com.example.murano.Resource' - - resolved_name = resolver.resolve_name(short_name) - - self.assertEqual(full_name, resolved_name) - - def test_resolves_w_empty_namespaces(self): - resolver = ns_resolver.NamespaceResolver({}) - - resolved_name = resolver.resolve_name('Resource') - - self.assertEqual('Resource', resolved_name) - - -class TestHelperFunctions(base.MuranoTestCase): - def test_generate_id(self): - generated_id = helpers.generate_id() - - self.assertTrue(re.match(r'[a-z0-9]{32}', generated_id)) - - def test_evaluate(self): - yaql_value = mock.Mock(yaql_expression.YaqlExpression, - return_value='atom') - complex_value = {yaql_value: ['some', (1, yaql_value), 'hi!'], - 'sample': [yaql_value, range(5)]} - complex_literal = utils.FrozenDict({ - 'atom': ('some', (1, 'atom'), 'hi!'), - 'sample': ('atom', (0, 1, 2, 3, 4)) - }) - context = yaql.create_context() - evaluated_value = helpers.evaluate(yaql_value, context) - evaluated_complex_value = helpers.evaluate(complex_value, context) - - self.assertEqual('atom', evaluated_value) - self.assertEqual(complex_literal, evaluated_complex_value) - - -class TestYaqlExpression(base.MuranoTestCase): - def setUp(self): - self._version = semantic_version.Version.coerce('1.0') - super(TestYaqlExpression, self).setUp() - - def test_expression(self): - yaql_expr = yaql_expression.YaqlExpression('string', self._version) - - self.assertEqual('string', yaql_expr.expression) - - def test_evaluate_calls(self): - string = 'string' - expected_calls = [mock.call(string, self._version), - mock.call().evaluate(context=None)] - - with mock.patch('murano.dsl.yaql_integration.parse') as mock_parse: - yaql_expr = yaql_expression.YaqlExpression(string, self._version) - yaql_expr(None) - - self.assertEqual(expected_calls, mock_parse.mock_calls) - - def test_is_expression_returns(self): - expr = yaql_expression.YaqlExpression('string', self._version) - - with mock.patch('murano.dsl.yaql_integration.parse'): - self.assertTrue(expr.is_expression('$some', self._version)) - self.assertTrue(expr.is_expression('$.someMore', self._version)) - - with mock.patch('murano.dsl.yaql_integration.parse') as parse_mock: - parse_mock.side_effect = exceptions.YaqlGrammarException - self.assertFalse(expr.is_expression('', self._version)) - - with mock.patch('murano.dsl.yaql_integration.parse') as parse_mock: - parse_mock.side_effect = exceptions.YaqlLexicalException - self.assertFalse(expr.is_expression('', self._version)) - - def test_property(self): - self.assertRaises(TypeError, - yaql_expression.YaqlExpression, - None, self._version) - - expr = yaql_expression.YaqlExpression('string', self._version) - self.assertEqual(expr._version, expr.version) - self.assertIsNone(expr._file_position) - - yaql_rep = expr.__repr__() - self.assertEqual('YAQL(%s)' % expr._expression, yaql_rep) diff --git a/murano/tests/unit/test_hacking.py b/murano/tests/unit/test_hacking.py deleted file mode 100644 index 5021824d0..000000000 --- a/murano/tests/unit/test_hacking.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2015 Intel, Inc. -# -# Licensed 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 pycodestyle -import textwrap -from unittest import mock - -from murano.hacking import checks -from murano.tests.unit import base - - -class HackingTestCase(base.MuranoTestCase): - """Tests the hacking checks in murano.hacking.checks - - This class tests the hacking checks in murano.hacking.checks by passing - strings to the check methods like the pycodestyle/flake8 parser would. - The parser loops over each line in the file and then passes the - parameters to the check method. The parameter names in the check method - dictate what type of object is passed to the check method. - The parameter types are:: - logical_line: A processed line with the following modifications: - - Multi-line statements converted to a single line. - - Stripped left and right. - - Contents of strings replaced with "xxx" of same length. - - Comments removed. - physical_line: Raw line of text from the input file. - lines: a list of the raw lines from the input file - tokens: the tokens that contribute to this logical line - line_number: line number in the input file - total_lines: number of lines in the input file - blank_lines: blank lines before this one - indent_char: indentation character in this file (" " or "\t") - indent_level: indentation (with tabs expanded to multiples of 8) - previous_indent_level: indentation on previous line - previous_logical: previous logical line - filename: Path of the file being run through pycodestyle - When running a test on a check method the return will be False/None if - there is no violation in the sample input. If there is an error a tuple is - returned with a position in the line, and a message. So to check the result - just assertTrue if the check is expected to fail and assertFalse if it - should pass. - """ - # We are patching pycodestyle so that only the check under test is actually - # installed. - - @mock.patch('pycodestyle._checks', - {'physical_line': {}, 'logical_line': {}, 'tree': {}}) - def _run_check(self, code, checker, filename=None): - pycodestyle.register_check(checker) - - lines = textwrap.dedent(code).strip().splitlines(True) - - checker = pycodestyle.Checker(filename=filename, lines=lines) - checker.check_all() - checker.report._deferred_print.sort() - return checker.report._deferred_print - - def _assert_has_errors(self, code, checker, expected_errors=None, - filename=None): - actual_errors = [e[:3] for e in - self._run_check(code, checker, filename)] - self.assertEqual(expected_errors or [], actual_errors) - - def _assert_has_no_errors(self, code, checker, filename=None): - self._assert_has_errors(code, checker, filename=filename) - - def test_assert_equal_none(self): - errors = [(1, 0, "M318")] - check = checks.assert_equal_none - - code = "self.assertEqual(A, None)" - self._assert_has_errors(code, check, errors) - - code = "self.assertEqual(None, A)" - self._assert_has_errors(code, check, errors) - - code = "self.assertIsNone()" - self._assert_has_no_errors(code, check) - - def test_no_mutable_default_args(self): - self.assertEqual(1, len(list(checks.no_mutable_default_args( - "def get_info_from_bdm(virt_type, bdm, mapping=[])")))) - - self.assertEqual(0, len(list(checks.no_mutable_default_args( - "defined = []")))) - - self.assertEqual(0, len(list(checks.no_mutable_default_args( - "defined, undefined = [], {}")))) - - def test_check_no_basestring(self): - self.assertEqual(1, len(list(checks.check_no_basestring( - "isinstance('foo', basestring)")))) - - self.assertEqual(0, len(list(checks.check_no_basestring( - "isinstance('foo', str)")))) diff --git a/murano/tests/unit/test_heat_stack.py b/murano/tests/unit/test_heat_stack.py deleted file mode 100644 index d9d651013..000000000 --- a/murano/tests/unit/test_heat_stack.py +++ /dev/null @@ -1,731 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# Copyright (c) 2016 AT&T Corp -# -# Licensed 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 heatclient.v1 import stacks -from unittest import mock - -from oslo_config import cfg - -from murano.engine.system import heat_stack -from murano.tests.unit import base - -CLS_NAME = 'murano.engine.system.heat_stack.HeatStack' -CONF = cfg.CONF - - -class TestHeatStack(base.MuranoTestCase): - def setUp(self): - super(TestHeatStack, self).setUp() - self.heat_client_mock = mock.Mock() - self.heat_client_mock.stacks = mock.MagicMock(spec=stacks.StackManager) - self.override_config('stack_tags', ['test-murano'], 'heat') - self.mock_tag = ','.join(CONF.heat.stack_tags) - self._patch_get_client() - - def tearDown(self): - super(TestHeatStack, self).tearDown() - self.addCleanup(mock.patch.stopall) - - def _patch_get_client(self): - self.get_client_patcher = mock.patch( - 'murano.engine.system.heat_stack.HeatStack._get_client', - return_value=self.heat_client_mock) - self.get_token_client_patcher = mock.patch.object( - heat_stack.HeatStack, '_get_token_client', - return_value=self.heat_client_mock) - self.get_client_patcher.start() - self.get_token_client_patcher.start() - - def _unpatch_get_client(self): - self.get_client_patcher.stop() - self.get_token_client_patcher.stop() - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_push_adds_version(self, status_get, wait_st): - """Assert that if heat_template_version is omitted, it's added.""" - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', 'Generated by TestHeatStack') - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = {} - hs._parameters = {} - hs._applied = False - hs.push() - - hs = heat_stack.HeatStack( - 'test-stack', 'Generated by TestHeatStack') - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._parameters = {} - hs._applied = False - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'description': 'Generated by TestHeatStack', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment={}, - tags=self.mock_tag - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_description_is_optional(self, status_get, wait_st): - """Assert that if heat_template_version is omitted, it's added.""" - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', None) - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=self.mock_tag - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_heat_files_are_sent(self, status_get, wait_st): - """Assert that if heat_template_version is omitted, it's added.""" - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {"heatFile": "file"} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={"heatFile": "file"}, - environment='', - tags=self.mock_tag - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_heat_environments_are_sent(self, status_get, wait_st): - """Assert that if heat_template_version is omitted, it's added.""" - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {"heatFile": "file"} - hs._hot_environment = 'environments' - hs._parameters = {} - hs._applied = False - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={"heatFile": "file"}, - environment='environments', - tags=self.mock_tag - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_heat_async_push(self, status_get, wait_st): - """Assert that if heat_template_version is omitted, it's added.""" - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {"heatFile": "file"} - hs._hot_environment = 'environments' - hs._parameters = {} - hs._applied = False - with mock.patch('murano.dsl.dsl.get_execution_session'): - hs.push(is_async=True) - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_not_called() - hs.output() - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={"heatFile": "file"}, - environment='environments', - tags=self.mock_tag - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - @mock.patch.object(heat_stack, 'LOG') - def test_push_except_http_conflict(self, mock_log, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {"heatFile": "file"} - hs._hot_environment = 'environments' - hs._parameters = {} - hs._applied = False - hs._get_token_client().stacks.create.side_effect = [ - heat_stack.heat_exc.HTTPConflict('test_error_msg'), - None - ] - - hs.push() - - mock_log.warning.assert_called_with( - 'Conflicting operation: ERROR: test_error_msg') - - @mock.patch(CLS_NAME + '.current') - def test_update_wrong_template_version(self, current): - """Template version other than expected should cause error.""" - - hs = heat_stack.HeatStack( - 'test-stack', 'Generated by TestHeatStack') - hs._template = {'resources': {'test': 1}} - - invalid_template = { - 'heat_template_version': 'something else' - } - - current.return_value = {} - - e = self.assertRaises(heat_stack.HeatStackError, - hs.update_template, - invalid_template) - err_msg = "Currently only heat_template_version 2013-05-23 "\ - "is supported." - self.assertEqual(err_msg, str(e)) - - # Check it's ok without a version - hs.update_template({}) - expected = {'resources': {'test': 1}} - self.assertEqual(expected, hs._template) - - # .. or with a good version - hs.update_template({'heat_template_version': '2013-05-23'}) - expected['heat_template_version'] = '2013-05-23' - self.assertEqual(expected, hs._template) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_heat_stack_tags_are_sent(self, status_get, wait_st): - """Assert heat_stack tags are sent - - Assert that heat_stack `tags` parameter get push & with - value from config parameter `stack_tags`. - """ - - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_parameters(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - - self.assertEqual(hs.parameters(), hs._parameters) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_reload(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - - hs.reload() - stack_info = self.heat_client_mock.stacks.get(stack_id=hs._name) - self.assertEqual(hs._template, hs._client.stacks.template( - stack_id='{0}/{1}'.format( - stack_info.stack_name, - stack_info.id))) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_delete(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - - hs.delete() - self.assertEqual({}, hs._template) - self.assertTrue(hs._applied) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - @mock.patch.object(heat_stack, 'LOG') - def test_delete_except_not_found(self, mock_log, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - - hs = heat_stack.HeatStack('test-stack', None) - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs.push() - - hs._client.stacks.delete.side_effect =\ - heat_stack.heat_exc.NotFound - hs.delete() - - self.assertTrue(hs._applied) - self.assertEqual({}, hs._template) - mock_log.warning.assert_called_with( - 'Stack test-stack already deleted?') - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - @mock.patch.object(heat_stack, 'LOG') - def test_delete_except_http_conflict(self, mock_log, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - - hs = heat_stack.HeatStack('test-stack', None) - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs.push() - - hs._client.stacks.delete.side_effect = [ - heat_stack.heat_exc.HTTPConflict('test_error_msg'), - None - ] - hs.delete() - - self.assertTrue(hs._applied) - self.assertEqual({}, hs._template) - mock_log.warning.assert_called_with('Conflicting operation: ' - 'ERROR: test_error_msg') - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_set_template_and_params(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - - new_template = {'resources': {'test': 2}} - new_parameters = {'parameters': {'test': 1}} - hs.set_template(new_template) - self.assertEqual(new_template, hs._template) - hs.set_parameters(new_parameters) - self.assertEqual(new_parameters, hs._parameters) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_set_hot_env_and_files(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - expected_template = { - 'heat_template_version': '2013-05-23', - 'resources': {'test': 1} - } - self.heat_client_mock.stacks.create.assert_called_with( - stack_name='test-stack', - disable_rollback=True, - parameters={}, - template=expected_template, - files={}, - environment='', - tags=','.join(CONF.heat.stack_tags) - ) - - new_hot_env = 'test' - new_files = {'files': {'test': 1}} - hs.set_hot_environment(new_hot_env) - self.assertEqual(new_hot_env, hs._hot_environment) - hs.set_files(new_files) - self.assertEqual(new_files, hs._files) - - @mock.patch(CLS_NAME + '._wait_state') - @mock.patch(CLS_NAME + '._get_status') - def test_none_template(self, status_get, wait_st): - status_get.return_value = 'NOT_FOUND' - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = None - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = True - hs._tags = ','.join(CONF.heat.stack_tags) - self.assertIsNone(hs.push()) - - @mock.patch(CLS_NAME + '._wait_state') - def test_get_hot_status(self, wait_st): - wait_st.return_value = {} - self.override_config('stack_tags', ['test-murano', 'murano-tag'], - 'heat') - hs = heat_stack.HeatStack('test-stack', None) - hs._description = None - hs._template = {'resources': {'test': 1}} - hs._files = {} - hs._hot_environment = '' - hs._parameters = {} - hs._applied = False - hs._tags = ','.join(CONF.heat.stack_tags) - hs.push() - - self.assertIsNone(hs._get_status()) - self.assertTrue(wait_st.called) - self.assertEqual({}, hs.output()) - - def test_current_except_http_notfound(self): - hs = heat_stack.HeatStack( - 'test-stack', 'Generated by TestHeatStack') - hs._template = None - hs._applied = False - hs._parameters = {'param1': 'val1', 'param2': 'val2'} - hs._client.stacks.get.side_effect = heat_stack.heat_exc.HTTPNotFound - - current = hs.current() - - self.assertEqual({}, current) - self.assertEqual(True, hs._applied) - self.assertEqual({}, hs._template) - self.assertEqual({}, hs._parameters) - - @mock.patch.object(heat_stack, 'auth_utils') - def test_get_client(self, mock_auth_utils): - self._unpatch_get_client() - - mock_auth_utils.get_session_client_parameters.return_value =\ - {'endpoint': 'test_endpoint/v1'} - - client = heat_stack.HeatStack._get_client('test_region_name') - - self.assertIsNotNone(client) - self.assertEqual("", - str(client.__class__)) - mock_auth_utils.get_client_session.assert_called_with( - conf='heat') - - @mock.patch.object(heat_stack, 'auth_utils') - def test_get_token_client(self, mock_auth_utils): - self._unpatch_get_client() - - mock_auth_utils.get_session_client_parameters.return_value =\ - {'endpoint': 'test_endpoint/v1'} - - hs = heat_stack.HeatStack('test-stack', 'Generated by TestHeatStack') - - token_client = hs._get_token_client() - - self.assertIsNotNone(token_client) - self.assertEqual("", - str(token_client.__class__)) - mock_auth_utils.get_token_client_session.assert_called_with( - conf='heat') - - def test_wait_state(self): - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.return_value =\ - mock.Mock(stack_status='CREATE_COMPLETE') - - result = hs._wait_state(lambda status: status == 'CREATE_COMPLETE') - - self.assertEqual({}, result) - - def test_wait_state_with_outputs(self): - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.side_effect = [ - mock.Mock(stack_status='IN_PROGRESS'), - mock.Mock(stack_status='CREATE_COMPLETE', - outputs=[{'output_key': 'key1', 'output_value': 'val1'}, - {'output_key': 'key2', 'output_value': 'val2'}]) - ] - - result = hs._wait_state(lambda status: status == 'CREATE_COMPLETE') - - self.assertNotEqual({}, result) - self.assertEqual({'key1': 'val1', 'key2': 'val2'}, result) - - def test_wait_state_with_multiple_states(self): - """Test that only the first state is checked.""" - - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.side_effect = [ - mock.Mock(stack_status=['IN_PROGRESS', 'NOT_FOUND']), - mock.Mock(stack_status='CREATE_COMPLETE') - ] - - result = hs._wait_state(lambda status: status == 'CREATE_COMPLETE') - - self.assertEqual({}, result) - - @mock.patch.object(heat_stack, 'eventlet') - def test_wait_state_with_wait_progress_true(self, mock_eventlet): - hs = heat_stack.HeatStack('test-stack', None) - hs._last_stack_timestamps = ('creation_time', 'updated_time') - hs._client.stacks.get.side_effect = [ - mock.Mock(stack_status='TEST_STATUS', - creation_time='creation_time', - updated_time='updated_time'), - mock.Mock(stack_status='TEST_STATUS', - creation_time='creation_time', - updated_time='updated_time'), - mock.Mock(stack_status='CREATE_COMPLETE') - ] - - result = hs._wait_state(lambda status: status == 'CREATE_COMPLETE', - wait_progress=True) - - self.assertEqual({}, result) - self.assertEqual(3, hs._client.stacks.get.call_count) - self.assertEqual(2, mock_eventlet.sleep.call_count) - expected_calls = [mock.call.sleep(2), mock.call.sleep(2)] - self.assertEqual(expected_calls, mock_eventlet.sleep.mock_calls) - - def test_wait_state_except_http_not_found(self): - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.side_effect = heat_stack.heat_exc.HTTPNotFound - - # If NOT FOUND is the expected status, then should run successfully. - result = hs._wait_state(lambda status: status == 'NOT_FOUND') - self.assertEqual({}, result) - - # Else EnvironmentError should be thrown. - expected_error_msg = "Unexpected stack state {0}"\ - .format('NOT_FOUND') - with self.assertRaisesRegex(EnvironmentError, - expected_error_msg): - hs._wait_state(lambda status: status == 'CREATE_COMPLETE') - - @mock.patch.object(heat_stack, 'eventlet') - def test_wait_state_except_general_exception(self, mock_eventlet): - """Test whether 4 tries are executed before exception raised.""" - - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.side_effect = Exception('test_exception_msg') - - with self.assertRaisesRegex(Exception, - 'test_exception_msg'): - hs._wait_state(lambda status: status == 'CREATE_COMPLETE') - - expected_calls = [mock.call.sleep(2), mock.call.sleep(4), - mock.call.sleep(8)] - self.assertEqual(4, hs._client.stacks.get.call_count) - self.assertEqual(3, mock_eventlet.sleep.call_count) - self.assertEqual(expected_calls, mock_eventlet.sleep.mock_calls) - - def test_wait_state_except_environment_error(self): - hs = heat_stack.HeatStack('test-stack', None) - hs._client.stacks.get.side_effect = [ - mock.Mock(stack_status='IN_PROGRESS'), - mock.Mock(stack_status='UNEXPECTED_STATUS', - stack_status_reason='test_reason') - ] - - expected_error_msg = "Unexpected stack state {0}: {1}"\ - .format('UNEXPECTED_STATUS', 'test_reason') - with self.assertRaisesRegex(EnvironmentError, - expected_error_msg): - hs._wait_state(lambda status: status == 'CREATE_COMPLETE') diff --git a/murano/tests/unit/test_utils.py b/murano/tests/unit/test_utils.py deleted file mode 100644 index 69e8befd5..000000000 --- a/murano/tests/unit/test_utils.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright (c) 2016 AT&T Corp -# All Rights Reserved. -# -# Licensed 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 unittest import mock - -from webob import exc - -from murano.services import states -import murano.tests.unit.base as test_base -from murano.tests.unit import utils as test_utils -from murano import utils - - -class TestUtils(test_base.MuranoTestCase): - - @mock.patch('murano.utils.db_session') - def test_check_env(self, mock_db_session): - """Test check env.""" - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_env = mock.MagicMock(environment_id='test_env_id', - tenant_id=mock_request.context.project_id) - mock_db_session.get_session().query().get.return_value = mock_env - - env = utils.check_env(mock_request, mock_env.environment_id) - self.assertEqual(mock_env, env) - - @mock.patch('murano.utils.db_session') - def test_check_env_with_null_environment_id(self, mock_db_session): - """Test check env with null environment id throws exception.""" - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_db_session.get_session().query().get.return_value = None - - test_env_id = 'test_env_id' - expected_error_message = 'Environment with id {env_id} not found'\ - .format(env_id=test_env_id) - - with self.assertRaisesRegex(exc.HTTPNotFound, - expected_error_message): - utils.check_env(mock_request, test_env_id) - - @mock.patch('murano.utils.db_session') - def test_check_env_with_mismatching_tenant_id(self, mock_db_session): - """Test check env without matching tenant ids throws exception.""" - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_env = mock.MagicMock(environment_id='test_env_id', - tenant_id='another_test_tenant_id') - mock_db_session.get_session().query().get.return_value = mock_env - - expected_error_message = 'User is not authorized to access these '\ - 'tenant resources' - - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_message): - utils.check_env(mock_request, mock_env.environment_id) - - def test_check_session_with_null_session(self): - """Test check session with null session throws exception.""" - expected_error_message = 'Session is not found'\ - .format(id=None) - with self.assertRaisesRegex(exc.HTTPNotFound, - expected_error_message): - utils.check_session(None, None, None, None) - - @mock.patch('murano.utils.check_env') - def test_check_session_with_mismatching_environment_id(self, _): - """Test check session without matching env ids throws exception.""" - mock_session = mock.MagicMock(session_id='session_id', - environment_id='environment_id') - environment_id = 'another_environment_id' - expected_error_msg = 'Session is not tied '\ - 'with Environment '\ - .format(session_id=mock_session.session_id, - environment_id=environment_id) - with self.assertRaisesRegex(exc.HTTPBadRequest, - expected_error_msg): - utils.check_session(None, environment_id, mock_session, - mock_session.session_id) - - def test_verify_session_with_invalid_request_header(self): - """Test session with invalid request header throws exception.""" - dummy_context = test_utils.dummy_context() - if dummy_context.session: - dummy_context.session = None - mock_request = mock.MagicMock(context=dummy_context) - expected_error_message = 'X-Configuration-Session header which '\ - 'indicates to the session is missed' - with self.assertRaisesRegex(exc.HTTPBadRequest, - expected_error_message): - self._test_verify_session(mock_request) - - @mock.patch('murano.utils.db_session') - def test_verify_session_with_null_session(self, mock_db_session): - """Test null session throws expected exception.""" - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.session = mock.MagicMock( - return_value='test_sess_id') - mock_db_session.get_session().query().get.return_value = None - expected_error_message = 'Session is not found'\ - .format(mock_request.context.session) - with self.assertRaisesRegex(exc.HTTPNotFound, - expected_error_message): - self._test_verify_session(mock_request) - - @mock.patch('murano.utils.db_session') - def test_verify_env_template_with_invalid_tenant(self, mock_db_session): - """Test session validation failure throws expected exception.""" - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.project_id = mock.MagicMock( - return_value='test_tenant_id') - mock_env_template = mock.MagicMock(tenant_id='another_test_tenant_id') - mock_db_session.get_session().query().get.return_value =\ - mock_env_template - expected_error_message = 'User is not authorized to access this'\ - ' tenant resources' - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_message): - self._test_verify_env_template(mock_request, None) - - @mock.patch('murano.utils.db_session') - def test_verify_env_template_with_null_template(self, mock_db_session): - """Test null env template throws expected exception.""" - mock_db_session.get_session().query().get.return_value = None - expected_error_message = 'Environment Template with id {id} not found'\ - .format(id='test_env_template_id') - with self.assertRaisesRegex(exc.HTTPNotFound, - expected_error_message): - self._test_verify_env_template(None, 'test_env_template_id') - - @utils.verify_env_template - def _test_verify_env_template(self, request, env_template_id): - """Helper function for testing above decorator.""" - pass - - @mock.patch('murano.utils.sessions.SessionServices.validate') - @mock.patch('murano.utils.db_session') - def test_verify_session_with_invalid_session(self, mock_db_session, - mock_validate): - """Test session validation failure throws expected exception.""" - mock_validate.return_value = False - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.session = mock.MagicMock( - return_value='test_sess_id') - mock_db_session.get_session().query().get.return_value =\ - mock.MagicMock() - expected_error_message = 'Session is invalid: '\ - 'environment has been updated or '\ - 'updating right now with other session'\ - .format(mock_request.context.session) - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_message): - self._test_verify_session(mock_request) - - @mock.patch('murano.utils.sessions.SessionServices.validate') - @mock.patch('murano.utils.db_session') - def test_verify_session_in_deploying_state(self, - mock_db_session, - mock_validate): - """Test deploying session throws expected exception.""" - mock_validate.return_value = True - mock_request = mock.MagicMock(context=test_utils.dummy_context()) - mock_request.context.session = mock.MagicMock( - return_value='test_sess_id') - mock_db_session.get_session().query().get.return_value =\ - mock.MagicMock(state=states.SessionState.DEPLOYING) - expected_error_message = 'Session is already in '\ - 'deployment state'\ - .format(mock_request.context.session) - with self.assertRaisesRegex(exc.HTTPForbidden, - expected_error_message): - self._test_verify_session(mock_request) - - @utils.verify_session - def _test_verify_session(self, request): - """Helper function for testing above decorator.""" - pass diff --git a/murano/tests/unit/utils.py b/murano/tests/unit/utils.py deleted file mode 100644 index f57ca34a7..000000000 --- a/murano/tests/unit/utils.py +++ /dev/null @@ -1,119 +0,0 @@ -# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 os -import shutil -import yaml -import zipfile - -from murano import context -from murano.db import session - - -MANIFEST = {'Format': 'MuranoPL/1.0', - 'Type': 'Application', - 'Description': 'MockApp for API tests', - 'Author': 'Mirantis, Inc'} - - -def dummy_context(user='test_username', tenant_id='test_tenant_id', - request_id='dummy-request', **kwargs): - - # NOTE(kzaitsev) we need to pass non-False value to request_id, to - # prevent it being generated by oslo during tests. - params = { - 'request_id': request_id, - 'project_id': tenant_id, - 'user': user, - } - params.update(kwargs) - return context.RequestContext.from_dict(params) - - -def save_models(*models): - s = session.get_session() - for m in models: - m.save(s) - - -def compose_package(app_name, package_dir, - require=None, archive_dir=None, add_class_name=False, - manifest_required=True, version=None): - """Composes a murano package - - Composes package `app_name` manifest and files from `package_dir`. - Includes `require` section if any in the manifest file. - Puts the resulting .zip file into `archive_dir` if present or in the - `package_dir`. - """ - tmp_package_dir = os.path.join(archive_dir, os.path.basename(package_dir)) - shutil.copytree(package_dir, tmp_package_dir) - package_dir = tmp_package_dir - - if manifest_required: - manifest = os.path.join(package_dir, "manifest.yaml") - with open(manifest, 'w') as f: - fqn = 'io.murano.apps.' + app_name - mfest_copy = MANIFEST.copy() - mfest_copy['FullName'] = fqn - mfest_copy['Name'] = app_name - mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'} - if require: - mfest_copy['Require'] = {str(name): version - for name, version in require} - if version: - mfest_copy['Version'] = version - f.write(yaml.dump(mfest_copy, default_flow_style=False)) - - if add_class_name: - class_file = os.path.join(package_dir, 'Classes', 'mock_muranopl.yaml') - with open(class_file, 'r') as f: - contents = f.read() - - index = contents.index('Extends') - contents = "{0}Name: {1}\n\n{2}".format(contents[:index], app_name, - contents[index:]) - with open(class_file, 'w') as f: - f.write(contents) - - if require: - class_file = os.path.join(package_dir, 'Classes', 'mock_muranopl.yaml') - with open(class_file, 'r') as f: - content = f.read() - - index_string = 'deploy:\n Body:\n ' - index = content.index(index_string) + len(index_string) - class_names = [req[0][req[0].rfind('.') + 1:] for req in require] - addition = "".join(["- new({})\n".format(name) + ' ' * 6 - for name in class_names]) - content = content[:index] + addition + content[index:] - with open(class_file, 'w') as f: - f.write(content) - - name = app_name + '.zip' - - if not archive_dir: - archive_dir = os.path.dirname(os.path.abspath(__file__)) - archive_path = os.path.join(archive_dir, name) - - with zipfile.ZipFile(archive_path, 'w') as zip_file: - for root, dirs, files in os.walk(package_dir): - for f in files: - zip_file.write( - os.path.join(root, f), - arcname=os.path.join(os.path.relpath(root, package_dir), f) - ) - - return archive_path, name diff --git a/murano/utils.py b/murano/utils.py deleted file mode 100644 index 3c10c36b1..000000000 --- a/murano/utils.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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 contextlib -import functools -import os - -from oslo_concurrency import lockutils -from oslo_log import log as logging -from oslo_utils import fileutils -from webob import exc - -from murano.common.i18n import _ -from murano.db import models -from murano.db.services import sessions -from murano.db import session as db_session -from murano.services import states - -LOG = logging.getLogger(__name__) - - -def check_env(request, environment_id): - unit = db_session.get_session() - environment = unit.query(models.Environment).get(environment_id) - if environment is None: - msg = _('Environment with id {env_id}' - ' not found').format(env_id=environment_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if hasattr(request, 'context'): - if (environment.tenant_id != request.context.project_id and not - request.context.is_admin): - msg = _('User is not authorized to access' - ' these tenant resources') - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - return environment - - -def check_session(request, environment_id, session, session_id): - """Validate, that a session is ok.""" - if session is None: - msg = _('Session is not found').format(id=session_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if session.environment_id != environment_id: - msg = _('Session is not tied ' - 'with Environment ').format( - session_id=session_id, - environment_id=environment_id) - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - check_env(request, environment_id) - - -def verify_env(func): - @functools.wraps(func) - def __inner(self, request, environment_id, *args, **kwargs): - check_env(request, environment_id) - return func(self, request, environment_id, *args, **kwargs) - return __inner - - -def verify_env_template(func): - @functools.wraps(func) - def __inner(self, request, env_template_id, *args, **kwargs): - unit = db_session.get_session() - template = unit.query(models.EnvironmentTemplate).get(env_template_id) - if template is None: - msg = _('Environment Template with id {id} not found' - ).format(id=env_template_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if hasattr(request, 'context'): - if template.tenant_id != request.context.project_id: - msg = _('User is not authorized to access' - ' this tenant resources') - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - return func(self, request, env_template_id, *args, **kwargs) - return __inner - - -def verify_session(func): - @functools.wraps(func) - def __inner(self, request, *args, **kwargs): - if hasattr(request, 'context') and not request.context.session: - msg = _('X-Configuration-Session header which indicates' - ' to the session is missed') - LOG.error(msg) - raise exc.HTTPBadRequest(explanation=msg) - - session_id = request.context.session - - unit = db_session.get_session() - session = unit.query(models.Session).get(session_id) - - if session is None: - msg = _('Session is not found').format(session_id) - LOG.error(msg) - raise exc.HTTPNotFound(explanation=msg) - - if not sessions.SessionServices.validate(session): - msg = _('Session ' - 'is invalid: environment has been updated or ' - 'updating right now with other session').format(session_id) - LOG.error(msg) - raise exc.HTTPForbidden(explanation=msg) - - if session.state == states.SessionState.DEPLOYING: - msg = _('Session is already in deployment state' - ).format(session_id) - raise exc.HTTPForbidden(explanation=msg) - return func(self, request, *args, **kwargs) - return __inner - - -ExclusiveInterProcessLock = lockutils.InterProcessLock -if os.name == 'nt': - # no shared locks on windows - SharedInterProcessLock = lockutils.InterProcessLock -else: - import fcntl - - class SharedInterProcessLock(lockutils.InterProcessLock): - def trylock(self): - # LOCK_EX instead of LOCK_EX - fcntl.lockf(self.lockfile, fcntl.LOCK_SH | fcntl.LOCK_NB) - - def _do_open(self): - # the file has to be open in read mode, therefore this method has - # to be overridden - basedir = os.path.dirname(self.path) - if basedir: - made_basedir = fileutils.ensure_tree(basedir) - if made_basedir: - self.logger.debug( - 'Created lock base path `%s`', basedir) - # The code here is mostly copy-pasted from oslo_concurrency and - # fasteners. The file has to be open with read permissions to be - # suitable for shared locking - if self.lockfile is None or self.lockfile.closed: - try: - # ensure the file is there, but do not obtain an extra file - # descriptor, as closing it would release fcntl lock - fd = os.open(self.path, os.O_CREAT | os.O_EXCL) - os.close(fd) - except OSError: - pass - self.lockfile = open(self.path, 'r') - - -class ReaderWriterLock(lockutils.ReaderWriterLock): - - @contextlib.contextmanager - def write_lock(self, blocking=True): - """Context manager that grants a write lock. - - Will wait until no active readers. Blocks readers after acquiring. - Raises a ``RuntimeError`` if an active reader attempts to acquire - a lock. - """ - timeout = None if blocking else 0.00001 - me = self._current_thread() - i_am_writer = self.is_writer(check_pending=False) - if self.is_reader() and not i_am_writer: - raise RuntimeError("Reader %s to writer privilege" - " escalation not allowed" % me) - if i_am_writer: - # Already the writer; this allows for basic reentrancy. - yield self - else: - with self._cond: - self._pending_writers.append(me) - while True: - # No readers, and no active writer, am I next?? - if len(self._readers) == 0 and self._writer is None: - if self._pending_writers[0] == me: - self._writer = self._pending_writers.popleft() - break - - # NOTE(kzaitsev): this actually means, that we can wait - # more than timeout times, since if we get True value we - # would get another spin inside of the while loop - # Should we do anything about it? - acquired = self._cond.wait(timeout) - if not acquired: - yield False - return - try: - yield True - finally: - with self._cond: - self._writer = None - self._cond.notify_all() diff --git a/murano/version.py b/murano/version.py deleted file mode 100644 index f9e2aa934..000000000 --- a/murano/version.py +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2014 Mirantis, Inc. -# -# Licensed 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 pbr import version - -version_info = version.VersionInfo('murano') -version_string = version_info.cached_version_string() diff --git a/murano_tempest_tests/README.rst b/murano_tempest_tests/README.rst deleted file mode 100644 index 249a8f6dc..000000000 --- a/murano_tempest_tests/README.rst +++ /dev/null @@ -1,5 +0,0 @@ -===== -MOVED -===== - -The murano tempest plugin has moved to http://opendev.org/openstack/murano-tempest-plugin diff --git a/rally-jobs/README.rst b/rally-jobs/README.rst deleted file mode 100644 index 8b9bedf9c..000000000 --- a/rally-jobs/README.rst +++ /dev/null @@ -1,30 +0,0 @@ -Rally job related files -======================= - -This directory contains rally tasks and plugins that are run by OpenStack CI. - -Structure ---------- - -* **task-murano.yaml** is a task that will be run in gates against OpenStack deployed - by DevStack with installed Rally & Murano. - -* **plugins** - directory where you can add rally plugins. Almost everything in - Rally is plugin. Benchmark context, Benchmark scenario, SLA checks, Generic - cleanup resources, .... - -* **extra** - all files from this directory will be copy-pasted to gates, which - makes it possible to use absolute paths in rally tasks. - Files will be in ~/.rally/extra/* - - -Useful links ------------- - -* More about rally: https://rally.readthedocs.org/en/latest/ - -* How to add rally-gates: https://rally.readthedocs.org/en/latest/gates.html - -* About plugins: https://rally.readthedocs.org/en/latest/plugins.html - -* Plugin samples: https://github.com/openstack/rally/tree/master/samples/plugins diff --git a/rally-jobs/extra/README.rst b/rally-jobs/extra/README.rst deleted file mode 100644 index 0aa38eb09..000000000 --- a/rally-jobs/extra/README.rst +++ /dev/null @@ -1,5 +0,0 @@ -Extra files -=========== - -All files from this directory will be copy-pasted to gates, which makes it -possible to use absolute paths in rally tasks. Files will be in ~/.rally/extra/* diff --git a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/Classes/HelloReporter.yaml b/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/Classes/HelloReporter.yaml deleted file mode 100644 index 2eca9d0df..000000000 --- a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/Classes/HelloReporter.yaml +++ /dev/null @@ -1,25 +0,0 @@ -Namespaces: - =: io.murano.apps - std: io.murano - sys: io.murano.system - - -Name: HelloReporter - -Extends: std:Application - -Properties: - name: - Contract: $.string().notNull() - -Workflow: - initialize: - Body: - - $.environment: $.find(std:Environment).require() - - deploy: - Body: - - If: not $.getAttr(deployed, false) - Then: - - $.environment.reporter.report($this, 'Starting deployment! Hello!') - - $.setAttr(deployed, True) diff --git a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/UI/ui.yaml b/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/UI/ui.yaml deleted file mode 100644 index 3836655a4..000000000 --- a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/UI/ui.yaml +++ /dev/null @@ -1,19 +0,0 @@ -Version: 2 - -Application: - ?: - type: io.murano.apps.HelloReporter - name: $.appConfiguration.name - -Forms: - - appConfiguration: - fields: - - name: name - type: string - label: Application Name - description: >- - Enter a desired name for the application. Just A-Z, a-z, 0-9, dash and - underline are allowed - - name: unitNamingPattern - type: string - required: false diff --git a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/manifest.yaml b/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/manifest.yaml deleted file mode 100644 index 47e4d510f..000000000 --- a/rally-jobs/extra/applications/HelloReporter/io.murano.apps.HelloReporter/manifest.yaml +++ /dev/null @@ -1,10 +0,0 @@ -Format: 1.0 -Type: Application -FullName: io.murano.apps.HelloReporter -Name: HelloReporter -Description: | - HelloReporter test app. -Author: 'Mirantis, Inc' -Tags: [App, Test, HelloWorld] -Classes: - io.murano.apps.HelloReporter: HelloReporter.yaml diff --git a/rally-jobs/extra/applications/README.rst b/rally-jobs/extra/applications/README.rst deleted file mode 100644 index 65bb8d4e0..000000000 --- a/rally-jobs/extra/applications/README.rst +++ /dev/null @@ -1,17 +0,0 @@ -Murano applications -=================== - -Files for Murano benchmarking - -Structure ---------- - -* / directories. Each directory stores a simple Murano package - that is used to prepare the Murano context that is used to deploy an environment - with a package. Other files needed for applications can be placed here as well. - - -Useful links ------------- - -* More about Murano: http://murano.readthedocs.org/ diff --git a/rally-jobs/plugins/README.rst b/rally-jobs/plugins/README.rst deleted file mode 100644 index 34a3fc957..000000000 --- a/rally-jobs/plugins/README.rst +++ /dev/null @@ -1,9 +0,0 @@ -Rally plugins -============= - -All *.py modules from this directory will be auto-loaded by Rally and all -plugins will be discoverable. There is no need of any extra configuration -and there is no difference between writing them here and in the Rally code base. - -Note, however, that it is better to push all interesting and useful benchmarks -to the Rally code base: this simplifies administration for Operators. diff --git a/rally-jobs/plugins/__init__.py b/rally-jobs/plugins/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/rally-jobs/task-murano.yaml b/rally-jobs/task-murano.yaml deleted file mode 100644 index c4cf8dc9f..000000000 --- a/rally-jobs/task-murano.yaml +++ /dev/null @@ -1,48 +0,0 @@ ---- - MuranoEnvironments.list_environments: - - - runner: - type: "constant" - times: 30 - concurrency: 4 - context: - users: - tenants: 2 - users_per_tenant: 2 - sla: - failure_rate: - max: 0 - - MuranoEnvironments.create_and_delete_environment: - - - runner: - type: "constant" - times: 20 - concurrency: 2 - context: - users: - tenants: 2 - users_per_tenant: 2 - sla: - failure_rate: - max: 0 - - MuranoEnvironments.create_and_deploy_environment: - - - args: - packages_per_env: 2 - runner: - type: "constant" - times: 8 - concurrency: 2 - context: - users: - tenants: 2 - users_per_tenant: 2 - murano_packages: - app_package: "~/.rally/extra/applications/HelloReporter/io.murano.apps.HelloReporter/" - roles: - - "admin" - sla: - failure_rate: - max: 0 diff --git a/releasenotes/notes/.placeholder b/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml b/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml deleted file mode 100644 index 75dfb8ad1..000000000 --- a/releasenotes/notes/action-syntax-3f2cbe843801f80d.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - Added a new manifest format 1.4.0. - Introduced the 'Scope' keyword for class methods to declare a method's - accessibility from outside through the API call. -deprecations: - - Deprecated the 'Usage Action' keyword. For format versions >= 1.4.0, use 'Scope Public' instead. diff --git a/releasenotes/notes/add-default-security-group-78855a66b960840a.yaml b/releasenotes/notes/add-default-security-group-78855a66b960840a.yaml deleted file mode 100644 index 3e143d237..000000000 --- a/releasenotes/notes/add-default-security-group-78855a66b960840a.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - added default rules to NeutronSecurityGroupManager to avoid - error if `createDefaultInstanceSecurityGroupRules()` method isn't - extended in inheritor and SecurityGroups isn't created in application - with call of `addGroupIngress()` method. \ No newline at end of file diff --git a/releasenotes/notes/add-upgrade-check-framework-1c069e5a54125d1b.yaml b/releasenotes/notes/add-upgrade-check-framework-1c069e5a54125d1b.yaml deleted file mode 100644 index 64d87ae34..000000000 --- a/releasenotes/notes/add-upgrade-check-framework-1c069e5a54125d1b.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -prelude: > - Added new tool ``murano-status upgrade check``. -features: - - | - New framework for ``murano-status upgrade check`` command is added. - This framework allows adding various checks which can be run before a - Murano upgrade to ensure if the upgrade can be performed safely. -upgrade: - - | - Operator can now use new CLI tool ``murano-status upgrade check`` - to check if Murano deployment can be safely upgraded from - N-1 to N release. diff --git a/releasenotes/notes/add_api_in_operator-371e3a1d2aec6421.yaml b/releasenotes/notes/add_api_in_operator-371e3a1d2aec6421.yaml deleted file mode 100644 index 18882d631..000000000 --- a/releasenotes/notes/add_api_in_operator-371e3a1d2aec6421.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - Implemented the capability for API endpoint ``/catalog/packages`` - to filter 'id', 'category', 'tag' properties using the 'in' operator. - An example of using the 'in' operator for 'id' is - 'id=in:id1,id2,id3'. - This filter is added using syntax that conforms to the latest - guidelines from the OpenStack API-WG. diff --git a/releasenotes/notes/add_timeout_to_linux_class-05d1f573a883f3ce.yaml b/releasenotes/notes/add_timeout_to_linux_class-05d1f573a883f3ce.yaml deleted file mode 100644 index 7936351b2..000000000 --- a/releasenotes/notes/add_timeout_to_linux_class-05d1f573a883f3ce.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added the ``timeout`` parameter to ``runCommand`` and ``putFile`` - methods of the ``io.murano.configuration.Linux`` class. diff --git a/releasenotes/notes/agent-source-0d2b21262ed10d3e.yaml b/releasenotes/notes/agent-source-0d2b21262ed10d3e.yaml deleted file mode 100644 index e3d1b0b8d..000000000 --- a/releasenotes/notes/agent-source-0d2b21262ed10d3e.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - | - A configuration file setting `[engine]/agent_source` was added. - The value is then used for the `pip install` command to install the - murano-agent. Since pip accepts http and git URLs, this can be used - to install agent from the custom git repo or install version other than the - latest. diff --git a/releasenotes/notes/application_catalog-to-application-catalog-f61d12454a557f79.yaml b/releasenotes/notes/application_catalog-to-application-catalog-f61d12454a557f79.yaml deleted file mode 100644 index 72aa03a86..000000000 --- a/releasenotes/notes/application_catalog-to-application-catalog-f61d12454a557f79.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -fixes: - - Murano is now able to work with keystone configured to use a templated catalog. -upgrade: - - When updating to Mitaka, the operator should update service name and type - for endpoint in keystone from "application_catalog" to "application-catalog" - if SQL is used for catalog back-end driver. diff --git a/releasenotes/notes/attributes-owner-type-c321e82f99f96cf1.yaml b/releasenotes/notes/attributes-owner-type-c321e82f99f96cf1.yaml deleted file mode 100644 index 9109ebd99..000000000 --- a/releasenotes/notes/attributes-owner-type-c321e82f99f96cf1.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - Now all native MuranoPL methods (those that are written in Python) - have "?muranoMethod" metadata key referring to MuranoMethod instance for - the method. -fixes: - - It was impossible to explicitly provide attribute owner class to - getAttr/setAttr methods without using namespace prefix or if the type was - not from the core library. diff --git a/releasenotes/notes/better-detect-agent-9ef8892a4bfb72cd.yaml b/releasenotes/notes/better-detect-agent-9ef8892a4bfb72cd.yaml deleted file mode 100644 index b93500866..000000000 --- a/releasenotes/notes/better-detect-agent-9ef8892a4bfb72cd.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -fixes: - - Core Library's init scripts used to have various problems detecting - pre-installed (by DIB) murano-agent on non-ubuntu images. - Agent setup script now checks wider list of directories before attempting - to install murano-agnet and service script now does not impose strict script - location. diff --git a/releasenotes/notes/bug-1654103-f39ee721d1b90b68.yaml b/releasenotes/notes/bug-1654103-f39ee721d1b90b68.yaml deleted file mode 100644 index c4c0c1d4a..000000000 --- a/releasenotes/notes/bug-1654103-f39ee721d1b90b68.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - | - Now admin can delete user's environments. diff --git a/releasenotes/notes/bug-1690179-375599ff3e8f2cd9.yaml b/releasenotes/notes/bug-1690179-375599ff3e8f2cd9.yaml deleted file mode 100644 index 114bd632e..000000000 --- a/releasenotes/notes/bug-1690179-375599ff3e8f2cd9.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -fixes: - - | - Remove hardcoded constant called 'ITERATORS_LIMIT', that can be - exceeded (2000) having big amount of objects. - Introduce dsl_iterators_limit configuration option instead of - constant. - diff --git a/releasenotes/notes/cinder-volumes-0412875c1011f8eb.yaml b/releasenotes/notes/cinder-volumes-0412875c1011f8eb.yaml deleted file mode 100644 index 4c62753f2..000000000 --- a/releasenotes/notes/cinder-volumes-0412875c1011f8eb.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Classes to work with Cinder volumes were added to core library. - Now it is possible to create new volume from various sources or use - existing volume. Also it is possible to attach volumes to instances - and boot instances from volumes. diff --git a/releasenotes/notes/class-config-versioning-23f1d676a3d54c0c.yaml b/releasenotes/notes/class-config-versioning-23f1d676a3d54c0c.yaml deleted file mode 100644 index 9c8ac7d3d..000000000 --- a/releasenotes/notes/class-config-versioning-23f1d676a3d54c0c.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -features: - - "Class configs are now also versioned. For class foo.bar version 1.2.3 - the following file names will be examined: - foo.bar-1.2.3.json - foo.bar-1.2.3.yaml - foo.bar-1.2.json - foo.bar-1.2.yaml - foo.bar-1.json - foo.bar-1.yaml - In addition for classes of version 0.x.y file name without version suffix - are also examined as a last attempt so the backward compatibility is - retained" diff --git a/releasenotes/notes/config-network-driver-77c82d151dead620.yaml b/releasenotes/notes/config-network-driver-77c82d151dead620.yaml deleted file mode 100644 index 215dc414d..000000000 --- a/releasenotes/notes/config-network-driver-77c82d151dead620.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Added the ``driver`` configuration option to the ``networking`` group. - It allows to explicitly select the networking driver. It supports 'neutron' and 'nova' options. - If set to ``None`` (default), murano attempts to use 'neutron' - if available, 'nova' otherwise. The change is backward compatible. diff --git a/releasenotes/notes/configure-notifications-0c84a5085c25f6e7.yaml b/releasenotes/notes/configure-notifications-0c84a5085c25f6e7.yaml deleted file mode 100644 index d4c679266..000000000 --- a/releasenotes/notes/configure-notifications-0c84a5085c25f6e7.yaml +++ /dev/null @@ -1,6 +0,0 @@ -features: - - It is now possible to configure the notifications to use a different - transport URL than the RPCs. These could potentially be completely - different message broker hosts (though they doesn't need to be). If the - notification-specific configuration is not provided, the notifier will use - the same transport as the RPCs. \ No newline at end of file diff --git a/releasenotes/notes/csar-template-plugin-f1682bfee213ae37.yaml b/releasenotes/notes/csar-template-plugin-f1682bfee213ae37.yaml deleted file mode 100644 index 56ddd0241..000000000 --- a/releasenotes/notes/csar-template-plugin-f1682bfee213ae37.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - New plugin 'murano_heat-translator_plugin' was added. - Now it is possible to deploy applications from CSAR templates. diff --git a/releasenotes/notes/decrypt-yaql-function-6651d0f5d73bd58d.yaml b/releasenotes/notes/decrypt-yaql-function-6651d0f5d73bd58d.yaml deleted file mode 100644 index 7ace8158c..000000000 --- a/releasenotes/notes/decrypt-yaql-function-6651d0f5d73bd58d.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - | - Added a new yaql function 'decryptData' which pairs with 'encryptData' on - the dashboard side. Application authors can use these functions to secure - sensitive input to their Murano applications such as passwords. - - Requires a valid secret storage backend (e.g. Barbican) to be configured - via Castellan. diff --git a/releasenotes/notes/delete-app-in-env-template-d8e07d3b860f0441.yaml b/releasenotes/notes/delete-app-in-env-template-d8e07d3b860f0441.yaml deleted file mode 100644 index 4e4fbe240..000000000 --- a/releasenotes/notes/delete-app-in-env-template-d8e07d3b860f0441.yaml +++ /dev/null @@ -1,3 +0,0 @@ -fixes: - - API call for deleting a service from environment template did not return - result of its operation. The issue is fixed. diff --git a/releasenotes/notes/deployment-list-8c2da5a5efc6dbac.yaml b/releasenotes/notes/deployment-list-8c2da5a5efc6dbac.yaml deleted file mode 100644 index 00a32fed1..000000000 --- a/releasenotes/notes/deployment-list-8c2da5a5efc6dbac.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - It is now possible to make a GET request to '/deployments' endpoint. - This will result in deployments for all environments in a specific project - (tenant) being returned. diff --git a/releasenotes/notes/deprecate-json-formatted-policy-file-b41728d03ee008e8.yaml b/releasenotes/notes/deprecate-json-formatted-policy-file-b41728d03ee008e8.yaml deleted file mode 100644 index c9c530004..000000000 --- a/releasenotes/notes/deprecate-json-formatted-policy-file-b41728d03ee008e8.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -upgrade: - - | - The default value of ``[oslo_policy] policy_file`` config option has - been changed from ``policy.json`` to ``policy.yaml``. - Operators who are utilizing customized or previously generated - static policy JSON files (which are not needed by default), should - generate new policy files or convert them in YAML format. Use the - `oslopolicy-convert-json-to-yaml - `_ - tool to convert a JSON to YAML formatted policy file in - backward compatible way. -deprecations: - - | - Use of JSON policy files was deprecated by the ``oslo.policy`` library - during the Victoria development cycle. As a result, this deprecation is - being noted in the Wallaby cycle with an anticipated future removal of support - by ``oslo.policy``. As such operators will need to convert to YAML policy - files. Please see the upgrade notes for details on migration of any - custom policy files. diff --git a/releasenotes/notes/devstack_using_heat_plugin-3dc9feeed36f24ec.yaml b/releasenotes/notes/devstack_using_heat_plugin-3dc9feeed36f24ec.yaml deleted file mode 100644 index 22913b255..000000000 --- a/releasenotes/notes/devstack_using_heat_plugin-3dc9feeed36f24ec.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -other: - - Since Newton release, heat is available as a devstack plugin. - So we remove heat as enable_service in devstack. diff --git a/releasenotes/notes/drop-py-2-7-37d8f1a13e867edb.yaml b/releasenotes/notes/drop-py-2-7-37d8f1a13e867edb.yaml deleted file mode 100644 index c34611afa..000000000 --- a/releasenotes/notes/drop-py-2-7-37d8f1a13e867edb.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -upgrade: - - | - Python 2.7 support has been dropped. Last release of Murano - to support python 2.7 is OpenStack Train. The minimum version of Python now - supported by Murano is Python 3.6. diff --git a/releasenotes/notes/drop-python-3-6-and-3-7-77af6bd3473ea5ba.yaml b/releasenotes/notes/drop-python-3-6-and-3-7-77af6bd3473ea5ba.yaml deleted file mode 100644 index 3d5e4e3d3..000000000 --- a/releasenotes/notes/drop-python-3-6-and-3-7-77af6bd3473ea5ba.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -upgrade: - - | - Python 3.6 & 3.7 support has been dropped. The minimum version of Python now - supported is Python 3.8. \ No newline at end of file diff --git a/releasenotes/notes/enable-hot-for-glare-8026f2dccad1732e.yaml b/releasenotes/notes/enable-hot-for-glare-8026f2dccad1732e.yaml deleted file mode 100644 index 443cc8a73..000000000 --- a/releasenotes/notes/enable-hot-for-glare-8026f2dccad1732e.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - Fixed a bug when the UI dialog was not displayed in Murano Dashboard for - applications which don't have UI definitions bundled in the package but - generate them based on the package contents instead. This usually affected - HOT-based packages and other non-muranopl-based applications. diff --git a/releasenotes/notes/enable-mocks-a156e7cc1b1d5066.yaml b/releasenotes/notes/enable-mocks-a156e7cc1b1d5066.yaml deleted file mode 100644 index 76af244dd..000000000 --- a/releasenotes/notes/enable-mocks-a156e7cc1b1d5066.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - Enable mocks in MuranoPL tests cases. - - Added :command:`murano-test-tunner` command to run murano package tests. - - Introduced two YAQL *inject* functions to enable - mocks ``def inject(target, target_method, mock_object, mock_name)`` - and ``def inject(target, target_method, yaql_expr)``. - diff --git a/releasenotes/notes/environment-edit-213789159902d4c3.yaml b/releasenotes/notes/environment-edit-213789159902d4c3.yaml deleted file mode 100644 index 45de625e7..000000000 --- a/releasenotes/notes/environment-edit-213789159902d4c3.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - /environments/ENV_ID/model/PATH endpoint added. - GET request responds with the subsection of ENV_ID's object model - located in its PATH. - PATCH request applies json-patch from request body to ENV_ID's model. It - does not contain PATH in the URL. diff --git a/releasenotes/notes/existing-sec-group-522d58bb2fe689a4.yaml b/releasenotes/notes/existing-sec-group-522d58bb2fe689a4.yaml deleted file mode 100644 index 250b78b69..000000000 --- a/releasenotes/notes/existing-sec-group-522d58bb2fe689a4.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - | - Users can now assign an existing security group to an application as - an alternative to using the one created by Murano's ``SecurityGroupManager``. diff --git a/releasenotes/notes/extension-methods-f674c2d342670e95.yaml b/releasenotes/notes/extension-methods-f674c2d342670e95.yaml deleted file mode 100644 index 7a7beabbf..000000000 --- a/releasenotes/notes/extension-methods-f674c2d342670e95.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -features: - - > - New method type: extension methods. Extension methods enable you to "add" - methods to existing types without modifying the original type. - Extension methods are a special kind of static method, but they are called - as if they were instance methods on the extended type. - Extension methods are identified by "Usage: Extension" and the type - they extend is determined by their first argument contract. Thus - such methods must have at lease one parameter. - - > - New type-level keyword "Import" which can be either list or scalar - that specifies type names which extensions methods should be imported - into class context and thus become available to type members. diff --git a/releasenotes/notes/filter-in-package-definition-43edaf12rad81b88.yaml b/releasenotes/notes/filter-in-package-definition-43edaf12rad81b88.yaml deleted file mode 100644 index 8496731f9..000000000 --- a/releasenotes/notes/filter-in-package-definition-43edaf12rad81b88.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -features: - - Added filter by 'Name' which only matches package name. diff --git a/releasenotes/notes/fip-multiple-external-networks-a6f99103ba3b3015.yaml b/releasenotes/notes/fip-multiple-external-networks-a6f99103ba3b3015.yaml deleted file mode 100644 index 83f616ce4..000000000 --- a/releasenotes/notes/fip-multiple-external-networks-a6f99103ba3b3015.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Murano is now able to assign correct floating IPs when using multiple external networks. - It attempts to choose one that shares a router with internal network. diff --git a/releasenotes/notes/fix-1498097.yaml b/releasenotes/notes/fix-1498097.yaml deleted file mode 100644 index 51b94842f..000000000 --- a/releasenotes/notes/fix-1498097.yaml +++ /dev/null @@ -1,3 +0,0 @@ -fixes: - - Avoid race condition during parallel upload of packages, when - packages have same tags. diff --git a/releasenotes/notes/fix-1528452-0e3bcee9bba89ffa.yaml b/releasenotes/notes/fix-1528452-0e3bcee9bba89ffa.yaml deleted file mode 100644 index 2e13a5e01..000000000 --- a/releasenotes/notes/fix-1528452-0e3bcee9bba89ffa.yaml +++ /dev/null @@ -1,3 +0,0 @@ -fixes: - - Fixed incorrect murano behaviour if deployed on devstack with keystone v3 by - default. diff --git a/releasenotes/notes/fix-py3-yaql-function-error-e0b0f8547956f5a6.yaml b/releasenotes/notes/fix-py3-yaql-function-error-e0b0f8547956f5a6.yaml deleted file mode 100644 index b94753aa9..000000000 --- a/releasenotes/notes/fix-py3-yaql-function-error-e0b0f8547956f5a6.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Avoid the `'method' object has no attribute '__yaql_function__'` error - when calling some YAQL functions with Python 3 diff --git a/releasenotes/notes/fixed-adding_text_description-25bd77f36ee370ba.yaml b/releasenotes/notes/fixed-adding_text_description-25bd77f36ee370ba.yaml deleted file mode 100644 index aca6e86c5..000000000 --- a/releasenotes/notes/fixed-adding_text_description-25bd77f36ee370ba.yaml +++ /dev/null @@ -1,5 +0,0 @@ -features: - - Added the ``description_text`` field to environment and environment templates - database tables and respective API objects. -upgrade: - - New database migration 015 has to be applied. diff --git a/releasenotes/notes/garbage-collection-50e78c4c9d47eba6.yaml b/releasenotes/notes/garbage-collection-50e78c4c9d47eba6.yaml deleted file mode 100644 index 55c18b73f..000000000 --- a/releasenotes/notes/garbage-collection-50e78c4c9d47eba6.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -features: - - Introduced a new MuranoPL class ``io.murano.system.GC`` - Now MuranoPL garbage collector can be used to - set up destruction dependencies between murano objects. - If object Foo is subscribed to object Bar's destruction, - it will be notified through a specific handler. If both - Foo and Bar are going to be destroyed during one execution session, - Foo will be destroyed after Bar. - You can omit the handler, in this case destruction order will also - be preserved. Handler can be a static or a usual function. diff --git a/releasenotes/notes/gc-collect-165e73bbaf345d74.yaml b/releasenotes/notes/gc-collect-165e73bbaf345d74.yaml deleted file mode 100644 index 8ecb6de15..000000000 --- a/releasenotes/notes/gc-collect-165e73bbaf345d74.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -features: - - New on-request garbage collector for MuranoPL objects were implemented. - Garbage collection is triggered by io.murano.system.GC.collect() - static method. Garbage collector destroys all object that are not - reachable anymore. GC can handle objects with cross-references and - isolated object graphs. When portion of object model becomes not - reachable it destroyed in predictable order such that child objects get - destroyed before their parents and, when possible, before objects - that are subscribed to their destruction notifications. - - Internally, both pre-deployment garbage collection (that was done by - comparison of ``Objects`` and ``ObjectsCopy``) and post-deployment - orphan object collection are now done through the new GC. diff --git a/releasenotes/notes/gc-isdoomed-isdestroyed-9598a6e15dbf36a0.yaml b/releasenotes/notes/gc-isdoomed-isdestroyed-9598a6e15dbf36a0.yaml deleted file mode 100644 index de24a987f..000000000 --- a/releasenotes/notes/gc-isdoomed-isdestroyed-9598a6e15dbf36a0.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - io.murano.system.GC.isDoomed() static method was added. It can be used - within the ``.destroy`` method to test if other object is also going to be - destroyed. - - io.murano.system.GC.isDestroyed() static method was added. It checks if - the object is destroyed and thus no methods can be invoked on it. - diff --git a/releasenotes/notes/heat_push_async-da3f31b63284a0ea.yaml b/releasenotes/notes/heat_push_async-da3f31b63284a0ea.yaml deleted file mode 100644 index 635a297b0..000000000 --- a/releasenotes/notes/heat_push_async-da3f31b63284a0ea.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - io.murano.system.HeatStack.push can be called with - async => true flag for asynchronous push diff --git a/releasenotes/notes/hot-outputs-merge-eeb9d12356560b48.yaml b/releasenotes/notes/hot-outputs-merge-eeb9d12356560b48.yaml deleted file mode 100644 index fc89a0c2a..000000000 --- a/releasenotes/notes/hot-outputs-merge-eeb9d12356560b48.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - All HOT template outputs are put into a single dictionary property - 'templateOutputs' rather than in a generated property per each output. - As a result there are no more constraints on output names. diff --git a/releasenotes/notes/implement-environment-audit-reports-23bb8009d1dfaecc.yaml b/releasenotes/notes/implement-environment-audit-reports-23bb8009d1dfaecc.yaml deleted file mode 100644 index 72ff5ad58..000000000 --- a/releasenotes/notes/implement-environment-audit-reports-23bb8009d1dfaecc.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -features: - - | - - Add notifications about environment events that are required for - tracking. These are AMQP notifications and oslo.messaging library - is used for sending them. - The follow event types are provided: environment.deploy.end, - environment.delete.end, environment.exists - There are 2 new configuration options controlling these notifications: - stats.env_audit_period, env_audit_enabled. diff --git a/releasenotes/notes/keystone-v3-0e287679f7f40a2a.yaml b/releasenotes/notes/keystone-v3-0e287679f7f40a2a.yaml deleted file mode 100644 index b71cbb590..000000000 --- a/releasenotes/notes/keystone-v3-0e287679f7f40a2a.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -fixes: - - Removed the need for Keystone v2 options (admin_user, - admin_password, admin_tenant_name) when Keystone v3 - is in use. - - Previously murano assumed that the service user - and service project are in the 'Default' domain. These values can - now be set in ``keystone_authtoken`` config group. diff --git a/releasenotes/notes/linux-helpers-async-243fc1adbbe5c512.yaml b/releasenotes/notes/linux-helpers-async-243fc1adbbe5c512.yaml deleted file mode 100644 index dc704abf5..000000000 --- a/releasenotes/notes/linux-helpers-async-243fc1adbbe5c512.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Implemented the capability for the helper methods of Linux class to run concurrently - if executed for different VM agents. diff --git a/releasenotes/notes/list-environments-of-a-given-project-e45315561478c8a2.yaml b/releasenotes/notes/list-environments-of-a-given-project-e45315561478c8a2.yaml deleted file mode 100644 index f7424fa35..000000000 --- a/releasenotes/notes/list-environments-of-a-given-project-e45315561478c8a2.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - > - "List Environments" API call is now able to filter environments by an owner - project (tenant). diff --git a/releasenotes/notes/magnum-plugin-f372caac83d2cd78.yaml b/releasenotes/notes/magnum-plugin-f372caac83d2cd78.yaml deleted file mode 100644 index 1994f36c0..000000000 --- a/releasenotes/notes/magnum-plugin-f372caac83d2cd78.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added magnum plugin to murano, that allows creating/deleting - of magnum baymodels and bays from MuranoPL diff --git a/releasenotes/notes/message-signing-07b09e541c2d94d6.yaml b/releasenotes/notes/message-signing-07b09e541c2d94d6.yaml deleted file mode 100644 index e5ae8cbee..000000000 --- a/releasenotes/notes/message-signing-07b09e541c2d94d6.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - | - Murano engine can be configured to sign all the RabbitMQ messages sent - to the agents. When the RSA key is provided, engine will provide agents - with its public part and sign all the messages sent. Agents then will - ignore any command that was not sent by the engine. diff --git a/releasenotes/notes/meta-e76d5c747b0a0fb6.yaml b/releasenotes/notes/meta-e76d5c747b0a0fb6.yaml deleted file mode 100644 index 0a2d1249b..000000000 --- a/releasenotes/notes/meta-e76d5c747b0a0fb6.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -features: - - Added ability to extend MuranoPL entities with custom metadata. - - > - For MuranoPL classes new key "Usage" was added. By default it is "Class". - But it can also be "Meta" to define meta-class. Meta-class has all the - capabilities of regular classes and in addition has 3 new attributes: - Cardinality, Applies and Inherited. - - It is possible to attach meta-class instances to packages, - classes (including other meta-classes), properties, methods and - method arguments. Each of them got new "Meta" key containing - list (or single scalar) of meta-class instances. diff --git a/releasenotes/notes/meta-for-ui-72f5b58c6d17599f.yaml b/releasenotes/notes/meta-for-ui-72f5b58c6d17599f.yaml deleted file mode 100644 index 7ecc89ed7..000000000 --- a/releasenotes/notes/meta-for-ui-72f5b58c6d17599f.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -features: - - Added the following meta-classes to the core library - - ``Title`` - ``Description`` - ``HelpText`` - ``Hidden`` - ``Section`` - ``Position`` - ``ModelBuilder``. - These classes will later be used to implement dynamic object model - generation. diff --git a/releasenotes/notes/metadata-aware-mixin-41777dd8d1802908.yaml b/releasenotes/notes/metadata-aware-mixin-41777dd8d1802908.yaml deleted file mode 100644 index b8740c68c..000000000 --- a/releasenotes/notes/metadata-aware-mixin-41777dd8d1802908.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added a MetadataAware mixin class capable to retrieve the metadata - attributes from the implementing objects and all its parents. diff --git a/releasenotes/notes/metadata-getter-76907aa1f0325adc.yaml b/releasenotes/notes/metadata-getter-76907aa1f0325adc.yaml deleted file mode 100644 index 1b767e5ae..000000000 --- a/releasenotes/notes/metadata-getter-76907aa1f0325adc.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Added a ``metadata()`` yaql function to retrieve the meta information about the - object, stored in the "?/metadata" section of object model. -other: - - Bumped the RUNTIME_VERSION attribute to 1.5 diff --git a/releasenotes/notes/model-load-c1eb24843d30e414.yaml b/releasenotes/notes/model-load-c1eb24843d30e414.yaml deleted file mode 100644 index 08839188a..000000000 --- a/releasenotes/notes/model-load-c1eb24843d30e414.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - Added an overload of the new function - ``new($model, $owner)``. - It loads complete object graph in a single call. Objects in the model - can have cross references. In that case, this is the only way to instantiate - the graph. Objects might be specified either in object model format (with - '?' attribute or in MuranoPL format (used for Meta definitions). - - The contract ``class()`` now uses the same approach to load classes from - dictionaries. Thus the same two syntaxes apply there as well. diff --git a/releasenotes/notes/multi-class-yamls-cbb3ef1d8578f41a.yaml b/releasenotes/notes/multi-class-yamls-cbb3ef1d8578f41a.yaml deleted file mode 100644 index 8efe5f82a..000000000 --- a/releasenotes/notes/multi-class-yamls-cbb3ef1d8578f41a.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -features: - - Now it is possible to have several classes in one YAML file. - Classes are separated using YAML document separator (3 dashes). - Empty documents are skipped. If the class doesn't have Namespace - section corresponding section from the previous class in the same - file is used. Thus it is possible to declare namespace prefixes once - at the file header. Even if there are several classes in one file all of - them are still required to be declared in manifest file. - diff --git a/releasenotes/notes/multi-regional-apps-b64afbaeafd5b9c5.yaml b/releasenotes/notes/multi-regional-apps-b64afbaeafd5b9c5.yaml deleted file mode 100644 index ee5616009..000000000 --- a/releasenotes/notes/multi-regional-apps-b64afbaeafd5b9c5.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -features: - - Added Support for application deployment across OpenStack regions. - Now, all OpenStack resource classes inherit from ``io.murano.CloudResource`` - that provides ``.getRegion()`` method and ``regionName`` property. - This allows to assign resources - to different regions. ``.getRegion()`` returns ``io.murano.CloudRegion`` - instance that resource or its parent belongs to. ``CloudRegion`` has - interface similar to ``Environment`` class and is the correct way to - get ``HeatStack`` instance associated with the region, - default network configuration, security - group manager and agent listener instances. ``Environment`` now acts as - default region so backward compatibility is not broken. However new - applications should not use environment to set security group rules but - rather a region(s) of their instance(s) in order to work correctly when - their instances were configured to use region other than the default. diff --git a/releasenotes/notes/multiple-api-workers-60492ddc2e3ff0aa.yaml b/releasenotes/notes/multiple-api-workers-60492ddc2e3ff0aa.yaml deleted file mode 100644 index 6a4087cb3..000000000 --- a/releasenotes/notes/multiple-api-workers-60492ddc2e3ff0aa.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - Added the ``api_workers`` option to ``murano`` config group. It controls - the number of API workers launched by murano. If not set, it would default to - the number of CPUs available. - -deprecations: - - Renamed the ``workers`` option from the ``engine`` group - to ``engine_workers`` to reduce ambiguity with the ``api_workers`` option. diff --git a/releasenotes/notes/multiple-engine-workers-7fec79572a6a9d01.yaml b/releasenotes/notes/multiple-engine-workers-7fec79572a6a9d01.yaml deleted file mode 100644 index 4d391e9bb..000000000 --- a/releasenotes/notes/multiple-engine-workers-7fec79572a6a9d01.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Add multiple engine workers -issues: - - Enabling multiple workers might break workflows under BSD and Windows systems - diff --git a/releasenotes/notes/murano-object-interface-equality-9fc8048be61bd539.yaml b/releasenotes/notes/murano-object-interface-equality-9fc8048be61bd539.yaml deleted file mode 100644 index d092179e7..000000000 --- a/releasenotes/notes/murano-object-interface-equality-9fc8048be61bd539.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Equality check (assertEqual) in test-runner can now properly compare - two MuranoPl objects. diff --git a/releasenotes/notes/muranopl-forms-4a3fb8153f26bbcf.yaml b/releasenotes/notes/muranopl-forms-4a3fb8153f26bbcf.yaml deleted file mode 100644 index a870f5508..000000000 --- a/releasenotes/notes/muranopl-forms-4a3fb8153f26bbcf.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - Added a new engine RPC call to generate json-schema from MuranoPL class. - The schema may be generated either from the entire class or for specific - model builders - static actions that can be used to generate - object model from their input. Class schema is built by inspecting - class properties and method schema using the same algorithm but applied - to its arguments. diff --git a/releasenotes/notes/new-contract-framework-1dede2d16b2e9c71.yaml b/releasenotes/notes/new-contract-framework-1dede2d16b2e9c71.yaml deleted file mode 100644 index 57ac45a67..000000000 --- a/releasenotes/notes/new-contract-framework-1dede2d16b2e9c71.yaml +++ /dev/null @@ -1,38 +0,0 @@ ---- -features: - - Implemented a new framework for MuranoPL contracts. Now, instead of several - independent implementations of the same yaql methods (string(), class() - etc.) all implementations of the same method are combined into single - class. Therefore, we now have a class per contract method. This also simplifies - development of new contracts. Each such class can provide methods for - data transformation (default contract usage), validation that is used - to decide if the method can be considered an extension method for the - value, and json schema generation method that was moved from the schema - generator script. - - Previously, when a class overrode a property from its parent - class the value was stored separately for both of them, transformed - by each of the contracts. Thus each class saw the value of its contract. - In absolute majority of the cases, the observed value was the same. However, - if the contracts were compatible on the provided value (say int() and - string() contracts on the value "123") they were different. This is - considered to be a bad pattern. - Now, the value is stored only once per object and transformed by the - contract defined in the actual object type. All base contracts are used - to validate the transformed object thus this pattern will not work - anymore. - - The value that is stored in the object's properties is obtained by - executing special "finalize" contract implementation which by default - returns the input value unmodified. Because validation happens on - the transformed value before it gets finalized it is possible for - transformation to return a value that will pass the validation though - the final value won't. This is used to relax the template() contract - limitation that prevented child class from excluding additional properties - from the template. - - The ``string()`` contract no longer converts everything to string values. - Now it only converts scalar values to strings. - Previous behavior allowed ``string()`` property to - accept lists and convert them to their Python string representation which - is clearly not what developers expected. - - Due to refactoring, contracts work a little bit faster because there is - no more need to generate yaql function definition for each contract method - on each call. diff --git a/releasenotes/notes/new-objects-resource-leak-fix-33a2eca3a4ccb8af.yaml b/releasenotes/notes/new-objects-resource-leak-fix-33a2eca3a4ccb8af.yaml deleted file mode 100644 index 7505d93d6..000000000 --- a/releasenotes/notes/new-objects-resource-leak-fix-33a2eca3a4ccb8af.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Prevented the resource leak for objects created during deployment - with ``new()`` function call. diff --git a/releasenotes/notes/new-type-format-in-object-model-da6976291057ab31.yaml b/releasenotes/notes/new-type-format-in-object-model-da6976291057ab31.yaml deleted file mode 100644 index 390759981..000000000 --- a/releasenotes/notes/new-type-format-in-object-model-da6976291057ab31.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -features: - - Changed the type representation in object model. Previous format was - to have three attributes in "?" section of the object - type, classVersion - and package where only the "type" is mandatory. Now they are merged into - single attribute "type" that has a format ``typeName/version@package``. - Version and package parts are still optional. -upgrades: - - Any of the tools, that inspected object model should be updated to expect - new object format representation ``typeName/version@package`` diff --git a/releasenotes/notes/no-neutron-sec-group-support-2d69082b7226d6c0.yaml b/releasenotes/notes/no-neutron-sec-group-support-2d69082b7226d6c0.yaml deleted file mode 100644 index 578ccd3f8..000000000 --- a/releasenotes/notes/no-neutron-sec-group-support-2d69082b7226d6c0.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -fixes: - - Murano is now able to deploy applications in the environments with disabled - Neutron Security Groups. Detection is based on the presence of - 'security-group' Neutron extension. diff --git a/releasenotes/notes/objects-copy-objects-merge-8f2752b1a1a18af0.yaml b/releasenotes/notes/objects-copy-objects-merge-8f2752b1a1a18af0.yaml deleted file mode 100644 index b93850cff..000000000 --- a/releasenotes/notes/objects-copy-objects-merge-8f2752b1a1a18af0.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -features: - - Previously, when pre-deployment garbage collection occurred it executed ``.destroy`` - method for objects that were present in the ``ObjectsCopy`` section of the - object model (which is the snapshot of the model after last deployment) - and not present in the current model anymore (because they were deleted - through the API between deployments). If the destroyed objects were to - access another object that was not deleted it was accessing its copy from - the ``ObjectsCopy``. Thus any changes to the internal state made by that - object were lost after the garbage collection finished (that is, before the - ``.deploy`` method call) and could not affect the deployment. - Now, if the object is present in both ``Objects`` and ``ObjectsCopy``, a - single instance (the one from ``Objects``) is used for both garbage - collection and deployment. - As a consequence, instances (in their ``.destroy`` method) now may observe - changes made to other objects they refer if they were not deleted, but - modified through the API. In some rare cases, it may break existing - applications. diff --git a/releasenotes/notes/operator-is-9b2b554d3487924d.yaml b/releasenotes/notes/operator-is-9b2b554d3487924d.yaml deleted file mode 100644 index 48eb4aa5e..000000000 --- a/releasenotes/notes/operator-is-9b2b554d3487924d.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - New operator *is* was added to MuranoPL. - Now it is possible to test if MuranoPL object is of particular type. diff --git a/releasenotes/notes/package_cache-68495dcde223c167.yaml b/releasenotes/notes/package_cache-68495dcde223c167.yaml deleted file mode 100644 index 3129c06bc..000000000 --- a/releasenotes/notes/package_cache-68495dcde223c167.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - Murano engine is now capable of caching packages on disk for reuse. - This is controlled by `packages_cache` directory path and - `enable_packages_cache` boolean parameter (true by default). - The packages are cached in an eventlet/inter-process safe manner - and are cleaned up as soon as newer - version of the package becomes available - (unless it's used by ongoing deployment) diff --git a/releasenotes/notes/public-template-a8853ac02dcf9396.yaml b/releasenotes/notes/public-template-a8853ac02dcf9396.yaml deleted file mode 100644 index b8d83e58a..000000000 --- a/releasenotes/notes/public-template-a8853ac02dcf9396.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - Added public field to environment templates. GET method - for api now displays public templates from other projects(tenants). - - Added public filter to environment templates api. - - Added clone action to environment templates. diff --git a/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml b/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml deleted file mode 100644 index efc452c3c..000000000 --- a/releasenotes/notes/put-empty-body-d605c2083b239f76.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - It is now possible to make a PUT request with body equal to '[]' to - '/environments//services' endpoint. This will result in removing - all apps from current session. This allows deleting the last application - from environment from CLI. diff --git a/releasenotes/notes/reflection-2fc43b990ea6b980.yaml b/releasenotes/notes/reflection-2fc43b990ea6b980.yaml deleted file mode 100644 index ec5f1c7cf..000000000 --- a/releasenotes/notes/reflection-2fc43b990ea6b980.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - Basic reflection capabilities were added to MuranoPL. Now it is possible - to get type info with typeinfo() function and using it as a starting point - obtain information about the class, its methods and properties as well as - the package of the class. Reflected properties can be used to obtain or - set its value in a given object or invoke its method. diff --git a/releasenotes/notes/region-aware-shared-ip-4441113c7cdd3c62.yaml b/releasenotes/notes/region-aware-shared-ip-4441113c7cdd3c62.yaml deleted file mode 100644 index 91f404c39..000000000 --- a/releasenotes/notes/region-aware-shared-ip-4441113c7cdd3c62.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Fixed 'io.murano.SharedIp' class to properly wotk in muti-region - environments. diff --git a/releasenotes/notes/release-cinder-volumes-01c29d28031a94dd.yaml b/releasenotes/notes/release-cinder-volumes-01c29d28031a94dd.yaml deleted file mode 100644 index bfbf82bb3..000000000 --- a/releasenotes/notes/release-cinder-volumes-01c29d28031a94dd.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Previously Cinder Volumes created in MuranoPL were not released correctly - on object destruction. The issue is now fixed. diff --git a/releasenotes/notes/remove-show-categories-42636e9c24c33105.yaml b/releasenotes/notes/remove-show-categories-42636e9c24c33105.yaml deleted file mode 100644 index 7dd097e3b..000000000 --- a/releasenotes/notes/remove-show-categories-42636e9c24c33105.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -deprecations: - - | - Removed `show_categories` endpoint from the application catalog API which - has been deprecated since the Liberty cycle. Use `list_categories` instead. diff --git a/releasenotes/notes/roles-for-requestcontext-43d32d88c3eaaa95.yaml b/releasenotes/notes/roles-for-requestcontext-43d32d88c3eaaa95.yaml deleted file mode 100644 index d8d897437..000000000 --- a/releasenotes/notes/roles-for-requestcontext-43d32d88c3eaaa95.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -fixes: - - RequestContext now serialises it's roles. This should allow murano to - work correctly (and allow rules like "role:xxx" in policy.json) - when using oslo.context prior to 2.2.0 and oslo.policy diff --git a/releasenotes/notes/safeloader-cve-2016-4972-19035a2a091ec30a.yaml b/releasenotes/notes/safeloader-cve-2016-4972-19035a2a091ec30a.yaml deleted file mode 100644 index f022c5c7e..000000000 --- a/releasenotes/notes/safeloader-cve-2016-4972-19035a2a091ec30a.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -security: - - cve-2016-4972 has been addressed. In ceveral places - Murano used loaders inherited directly from yaml.Loader - when parsing MuranoPL and UI files from packages. - This is unsafe, because this loader is capable of creating - custom python objects from specifically constructed - yaml files. With this change all yaml loading operations are done - using safe loaders instead. diff --git a/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml b/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml deleted file mode 100644 index 56ed5d64c..000000000 --- a/releasenotes/notes/script-line-endings-db632db9e24237a3.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - Whenever murano-engine accesses script files, - text script files are opened in 'rU' mode which recognizes all types of - newlines, and binary files are opened in 'rb' mode to prevent their - corruption. diff --git a/releasenotes/notes/separate-service-broker-from-murano-f6ee48576f51d893.yaml b/releasenotes/notes/separate-service-broker-from-murano-f6ee48576f51d893.yaml deleted file mode 100644 index bf7d92e89..000000000 --- a/releasenotes/notes/separate-service-broker-from-murano-f6ee48576f51d893.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Separated murano service broker from murano-api into a murano-cfapi service. - Created a separate database and ``paste.ini`` for service broker. diff --git a/releasenotes/notes/shared-net-port-creation-0eda66be4444cf2f.yaml b/releasenotes/notes/shared-net-port-creation-0eda66be4444cf2f.yaml deleted file mode 100644 index 73c5038cd..000000000 --- a/releasenotes/notes/shared-net-port-creation-0eda66be4444cf2f.yaml +++ /dev/null @@ -1,10 +0,0 @@ ---- -issues: - - If a VM being a part of some shared-ip group is attached to the network - which is not owned by the current tenant (shared network) a policy - violation may occur thus failing the deployment. - -fixes: - - Murano no longer specifies fixed-ip parameter for ports when creating VMs - attached to networks owned and shared by other tenants. Specifying this - parameter for non-owned networks could cause violation of neutron policies. diff --git a/releasenotes/notes/spec-semver-library-436b0db35fbd4c37.yaml b/releasenotes/notes/spec-semver-library-436b0db35fbd4c37.yaml deleted file mode 100644 index fd823ae6d..000000000 --- a/releasenotes/notes/spec-semver-library-436b0db35fbd4c37.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -fixes: - - It is now possible to use version specifications like '=0.0.0' when - ``semantic_version`` library version '2.3.1' is installed. Previously - such specifications caused an error and '==0.0.0' had to be used. diff --git a/releasenotes/notes/static-actions-61759be796299039.yaml b/releasenotes/notes/static-actions-61759be796299039.yaml deleted file mode 100644 index 64962bdf5..000000000 --- a/releasenotes/notes/static-actions-61759be796299039.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -features: - - Added a new API endpoint ``v1/actions`` to call static public methods. - It accepts class name, method name, - method arguments, and optionally package name and class version in the - request body. This call does not create an environment, - object instances or database records. diff --git a/releasenotes/notes/statics-9943fe9873138dac.yaml b/releasenotes/notes/statics-9943fe9873138dac.yaml deleted file mode 100644 index 761b69e76..000000000 --- a/releasenotes/notes/statics-9943fe9873138dac.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - "Static methods and properties were introduced. - Both properties and methods can be marked as Usage: Static - Statics can be accessed using ns:Class.property / ns:Class.method(), - :Class.property / :Class.method() to access class from current namespace - or type('full.name').property / type('full.name').method() to use full - type name." - - io.murano.configuration.Linux methods are now static diff --git a/releasenotes/notes/string-logging-20b8e60a957ba6b7.yaml b/releasenotes/notes/string-logging-20b8e60a957ba6b7.yaml deleted file mode 100644 index 7cc3c232f..000000000 --- a/releasenotes/notes/string-logging-20b8e60a957ba6b7.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -fixes: - - Murano engine no longer logs methods ``string()``, ``json()``, and ``yaml()`` - of the 'io.murano.system.Resources' class. This is done to prevent UnicodeDecodeError's - when transferring binary files to murano agent. diff --git a/releasenotes/notes/tag-heat-stacks-3345eb1bda531a6f.yaml b/releasenotes/notes/tag-heat-stacks-3345eb1bda531a6f.yaml deleted file mode 100644 index fa0dac729..000000000 --- a/releasenotes/notes/tag-heat-stacks-3345eb1bda531a6f.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - Heat stacks created by murano during environment deployment now - have 'murano' tag by default. This is controlled by ``stack_tags`` - config parameter. \ No newline at end of file diff --git a/releasenotes/notes/template-contract-b71840cbc35eb478.yaml b/releasenotes/notes/template-contract-b71840cbc35eb478.yaml deleted file mode 100644 index ac7f1520e..000000000 --- a/releasenotes/notes/template-contract-b71840cbc35eb478.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -features: - - Implemented a new contract function ``template()``. ``template()`` works - similar to the ``class()`` in regards to the data validation but does not - instantiate objects. Instead, the data is left in the object model - in dictionary format so that it could be instantiated later with the ``new()`` - function. Additionally, the function allows excluding specified properties from - validation and from the resulting template so that they could be provided later. - Objects that are assigned to the property or argument with ``template()`` - contract will be automatically converted to their object model - representation. diff --git a/releasenotes/notes/test-runner-output-fix-e942e221be189424.yaml b/releasenotes/notes/test-runner-output-fix-e942e221be189424.yaml deleted file mode 100644 index 9768a027a..000000000 --- a/releasenotes/notes/test-runner-output-fix-e942e221be189424.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -fixes: - - The test-runner now outputs the tests it runs and their results to stdout - directly, instead of the logging system. - - The test-runner now does not output logs to stderr by default unless a - 'use_stderr' parameter is specified in the configuration file. diff --git a/releasenotes/notes/test-runner-set-up-tear-down-a269a31734544a3a.yaml b/releasenotes/notes/test-runner-set-up-tear-down-a269a31734544a3a.yaml deleted file mode 100644 index bfe596559..000000000 --- a/releasenotes/notes/test-runner-set-up-tear-down-a269a31734544a3a.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -fixes: - - Fixed the issue that prevented the test-runner from properly - invoking ``setUp`` and ``tearDown`` methods of fixtures in some cases. diff --git a/releasenotes/notes/two-phase-instance-deploy-81d37e7987abc792.yaml b/releasenotes/notes/two-phase-instance-deploy-81d37e7987abc792.yaml deleted file mode 100644 index bfc598735..000000000 --- a/releasenotes/notes/two-phase-instance-deploy-81d37e7987abc792.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - Split ``Instance``'s ``.deploy()`` method into two phases - ``beginDeploy()`` - and ``endDeploy()``. This allows the application developer to provision multiple - instances at once without the need to push the stack for each instance. diff --git a/releasenotes/notes/update-app-in-env-template-08d92b22bd1355f5.yaml b/releasenotes/notes/update-app-in-env-template-08d92b22bd1355f5.yaml deleted file mode 100644 index c94aee717..000000000 --- a/releasenotes/notes/update-app-in-env-template-08d92b22bd1355f5.yaml +++ /dev/null @@ -1,4 +0,0 @@ ---- -features: - - Added API endpoint ``/templates/{env_template_id}/services/{path:.*?}`` - for environment template application update operation. diff --git a/releasenotes/notes/use_http_proxy_to_wsgi-9b22d3e60c045689.yaml b/releasenotes/notes/use_http_proxy_to_wsgi-9b22d3e60c045689.yaml deleted file mode 100644 index d78b0a173..000000000 --- a/releasenotes/notes/use_http_proxy_to_wsgi-9b22d3e60c045689.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- - -features: - - | - Murano switched to using standard oslo middleware HTTPProxyToWSGI instead - of custom implementation. This middleware parses the X-Forwarded-Proto - HTTP header or the Proxy protocol in order to help murano respond with - the correct URL refs when it's put behind a TLS proxy (such as HAProxy). - This middleware is disabled by default, but can be enabled via a - configuration option in the oslo_middleware group. -upgrade: - - | - File ``murano-paste.ini has been updated to use oslo HTTPProxyToWSGI middleware. - Config option ``secure_proxy_ssl_header`` has been removed. Please refer to - oslo_middleware configuration options if you wish deploy murano behind TLS proxy. - Most notably you would need to set ``enable_proxy_headers_parsing`` under group - ``oslo_middleware`` to True, to enable header parsing. - diff --git a/releasenotes/notes/user-project-6173d7282765b5ca.yaml b/releasenotes/notes/user-project-6173d7282765b5ca.yaml deleted file mode 100644 index d9ecedb19..000000000 --- a/releasenotes/notes/user-project-6173d7282765b5ca.yaml +++ /dev/null @@ -1,5 +0,0 @@ ---- -features: - - Added classes that represent OpenStack user and project - - Added ability to retrieve current user and project info - - Added ability to retrieve environment owner user and project info diff --git a/releasenotes/notes/var-kw-args-c42c31678d8bc747.yaml b/releasenotes/notes/var-kw-args-c42c31678d8bc747.yaml deleted file mode 100644 index 624976523..000000000 --- a/releasenotes/notes/var-kw-args-c42c31678d8bc747.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -features: - - > - Added the capability to declare MuranoPL YAML methods with variable length - positional and keyword arguments. This is done using argument ``Usage`` - attribute. Regular arguments have Standard usage which is the default. - Variable length args (args in Python) should have "Usage: VarArgs" and - keyword args (kwargs) are declared with "Usage: KwArgs". Inside the - method they are seen as a list and a dictionary correspondingly. For such - arguments contracts are written for individual argument values thus - no need to write them as lists/dicts. diff --git a/releasenotes/notes/yaql11-822b503f13992890.yaml b/releasenotes/notes/yaql11-822b503f13992890.yaml deleted file mode 100644 index 43300707c..000000000 --- a/releasenotes/notes/yaql11-822b503f13992890.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -features: - - Murano was migrated to yaql 1.1 - - New format MuranoPL/1.3 can be specified in manifest files. - MuranoPL/1.3 is identical to MuranoPL/1.2 but except for the fact - that MuranoPL/1.3 packages cannot be imported to earlier Murano versions. - Thus applications that use new features of yaql 1.1 should use this format - version. diff --git a/releasenotes/source/2023.1.rst b/releasenotes/source/2023.1.rst deleted file mode 100644 index d1238479b..000000000 --- a/releasenotes/source/2023.1.rst +++ /dev/null @@ -1,6 +0,0 @@ -=========================== -2023.1 Series Release Notes -=========================== - -.. release-notes:: - :branch: stable/2023.1 diff --git a/releasenotes/source/_static/.placeholder b/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/releasenotes/source/_templates/.placeholder b/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29bb..000000000 diff --git a/releasenotes/source/conf.py b/releasenotes/source/conf.py deleted file mode 100644 index c7b44dbec..000000000 --- a/releasenotes/source/conf.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -# Licensed 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. - -# Murano Release Notes documentation build configuration file, created by -# sphinx-quickstart on Tue Nov 3 17:40:50 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'openstackdocstheme', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -# source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -copyright = u'2015, Murano Developers' - -# openstackdocstheme options -openstackdocs_repo_name = 'openstack/murano' -openstackdocs_bug_project = 'murano' -openstackdocs_bug_tag = '' - -# Release notes are version independent. -# The short X.Y version. - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -# today = '' -# Else, today_fmt is used as the format for a strftime call. -# today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -# default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -# add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -# add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -# show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'native' - -# A list of ignored prefixes for module index sorting. -# modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -# keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'openstackdocs' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -# html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -# html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -# html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -# html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -# html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -# html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -# html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -# html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -# html_additional_pages = {} - -# If false, no module index is generated. -# html_domain_indices = True - -# If false, no index is generated. -# html_use_index = True - -# If true, the index is split into individual pages for each letter. -# html_split_index = False - -# If true, links to the reST sources are added to the pages. -# html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -# html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -# html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -# html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -# html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'MuranoReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'MuranoReleaseNotes.tex', u'Murano Release Notes Documentation', - u'Murano Developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -# latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -# latex_use_parts = False - -# If true, show page references after internal links. -# latex_show_pagerefs = False - -# If true, show URL addresses after external links. -# latex_show_urls = False - -# Documents to append as an appendix to all manuals. -# latex_appendices = [] - -# If false, no module index is generated. -# latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'muranoreleasenotes', u'Murano Release Notes Documentation', - [u'Murano Developers'], 1) -] - -# If true, show URL addresses after external links. -# man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'MuranoReleaseNotes', u'Murano Release Notes Documentation', - u'Murano Developers', 'MuranoReleaseNotes', - 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -# texinfo_appendices = [] - -# If false, no module index is generated. -# texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -# texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -# texinfo_no_detailmenu = False - -# -- Options for Internationalization output ------------------------------ -locale_dirs = ['locale/'] diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst deleted file mode 100644 index 3edc40f6b..000000000 --- a/releasenotes/source/index.rst +++ /dev/null @@ -1,37 +0,0 @@ -.. - Licensed 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. - -====================== - Murano Release Notes -====================== - -.. toctree:: - :maxdepth: 2 - - unreleased - 2023.1 - zed - yoga - xena - wallaby - victoria - ussuri - train - stein - rocky - queens - pike - ocata - newton - mitaka - liberty diff --git a/releasenotes/source/liberty.rst b/releasenotes/source/liberty.rst deleted file mode 100644 index 36217be84..000000000 --- a/releasenotes/source/liberty.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================== - Liberty Series Release Notes -============================== - -.. release-notes:: - :branch: origin/stable/liberty diff --git a/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po deleted file mode 100644 index c1d79408c..000000000 --- a/releasenotes/source/locale/de/LC_MESSAGES/releasenotes.po +++ /dev/null @@ -1,87 +0,0 @@ -# Andreas Jaeger , 2019. #zanata -msgid "" -msgstr "" -"Project-Id-Version: murano\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-09-25 11:20+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2019-09-26 12:52+0000\n" -"Last-Translator: Andreas Jaeger \n" -"Language-Team: German\n" -"Language: de\n" -"X-Generator: Zanata 4.3.3\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" - -msgid "1.0.2" -msgstr "1.0.2" - -msgid "1.0.3" -msgstr "1.0.3" - -msgid "2.0.0" -msgstr "2.0.0" - -msgid "2.0.1" -msgstr "2.0.1" - -msgid "2.0.2" -msgstr "2.0.2" - -msgid "2.0.2-12" -msgstr "2.0.2-12" - -msgid "3.0.0" -msgstr "3.0.0" - -msgid "3.0.0-15" -msgstr "3.0.0-15" - -msgid "3.1.0" -msgstr "3.1.0" - -msgid "3.2.0" -msgstr "3.2.0" - -msgid "4.0.0" -msgstr "4.0.0" - -msgid "5.0.0" -msgstr "5.0.0" - -msgid "7.0.0" -msgstr "7.0.0" - -msgid "Current Series Release Notes" -msgstr "Aktuelle Serie Releasenotes" - -msgid "Liberty Series Release Notes" -msgstr "Liberty Serie Releasenotes" - -msgid "Mitaka Series Release Notes" -msgstr "Mitaka Serie Releasenotes" - -msgid "Murano Release Notes" -msgstr "Murano Releasenotes" - -msgid "Newton Series Release Notes" -msgstr "Newton Serie Releasenotes" - -msgid "Ocata Series Release Notes" -msgstr "Ocata Serie Releasenotes" - -msgid "Pike Series Release Notes" -msgstr "Pike Serie Releasenotes" - -msgid "Queens Series Release Notes" -msgstr "Queens Serie Releasenotes" - -msgid "Rocky Series Release Notes" -msgstr "Rocky Serie Releasenotes" - -msgid "Stein Series Release Notes" -msgstr "Stein Serie Releasenotes" - -msgid "Train Series Release Notes" -msgstr "Train Serie Releasenotes" diff --git a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po deleted file mode 100644 index 94dca8cd8..000000000 --- a/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po +++ /dev/null @@ -1,1299 +0,0 @@ -# Andi Chandler , 2017. #zanata -# Andi Chandler , 2018. #zanata -# Andi Chandler , 2019. #zanata -# Andi Chandler , 2020. #zanata -# Andi Chandler , 2022. #zanata -# Andi Chandler , 2023. #zanata -msgid "" -msgstr "" -"Project-Id-Version: murano\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-26 08:25+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2023-05-08 11:46+0000\n" -"Last-Translator: Andi Chandler \n" -"Language-Team: English (United Kingdom)\n" -"Language: en_GB\n" -"X-Generator: Zanata 4.3.3\n" -"Plural-Forms: nplurals=2; plural=(n != 1)\n" - -msgid "" -"\"List Environments\" API call is now able to filter environments by an " -"owner project (tenant)." -msgstr "" -"\"List Environments\" API call is now able to filter environments by an " -"owner project (tenant)." - -msgid "" -"/environments/ENV_ID/model/PATH endpoint added. GET request responds with " -"the subsection of ENV_ID's object model located in its PATH. PATCH request " -"applies json-patch from request body to ENV_ID's model. It does not contain " -"PATH in the URL." -msgstr "" -"/environments/ENV_ID/model/PATH endpoint added. GET request responds with " -"the subsection of ENV_ID's object model located in its PATH. PATCH request " -"applies json-patch from request body to ENV_ID's model. It does not contain " -"PATH in the URL." - -msgid "1.0.2" -msgstr "1.0.2" - -msgid "1.0.3" -msgstr "1.0.3" - -msgid "11.0.0" -msgstr "11.0.0" - -msgid "15.0.0" -msgstr "15.0.0" - -msgid "2.0.0" -msgstr "2.0.0" - -msgid "2.0.1" -msgstr "2.0.1" - -msgid "2.0.2" -msgstr "2.0.2" - -msgid "2.0.2-12" -msgstr "2.0.2-12" - -msgid "2023.1 Series Release Notes" -msgstr "2023.1 Series Release Notes" - -msgid "3.0.0" -msgstr "3.0.0" - -msgid "3.0.0-15" -msgstr "3.0.0-15" - -msgid "3.1.0" -msgstr "3.1.0" - -msgid "3.2.0" -msgstr "3.2.0" - -msgid "4.0.0" -msgstr "4.0.0" - -msgid "5.0.0" -msgstr "5.0.0" - -msgid "7.0.0" -msgstr "7.0.0" - -msgid "9.0.0" -msgstr "9.0.0" - -msgid "" -"A configuration file setting `[engine]/agent_source` was added. The value is " -"then used for the `pip install` command to install the murano-agent. Since " -"pip accepts http and git URLs, this can be used to install agent from the " -"custom git repo or install version other than the latest." -msgstr "" -"A configuration file setting `[engine]/agent_source` was added. The value is " -"then used for the `pip install` command to install the murano-agent. Since " -"pip accepts HTTP and git URLs, this can be used to install agent from the " -"custom git repo or install version other than the latest." - -msgid "" -"API call for deleting a service from environment template did not return " -"result of its operation. The issue is fixed." -msgstr "" -"API call for deleting a service from environment template did not return " -"result of its operation. The issue is fixed." - -msgid "Add multiple engine workers" -msgstr "Add multiple engine workers" - -msgid "" -"Add notifications about environment events that are required for tracking. " -"These are AMQP notifications and oslo.messaging library is used for sending " -"them. The follow event types are provided: environment.deploy.end, " -"environment.delete.end, environment.exists There are 2 new configuration " -"options controlling these notifications: stats.env_audit_period, " -"env_audit_enabled." -msgstr "" -"Add notifications about environment events that are required for tracking. " -"These are AMQP notifications and oslo.messaging library is used for sending " -"them. The follow event types are provided: environment.deploy.end, " -"environment.delete.end, environment.exists There are 2 new configuration " -"options controlling these notifications: stats.env_audit_period, " -"env_audit_enabled." - -msgid "" -"Added :command:`murano-test-tunner` command to run murano package tests." -msgstr "" -"Added :command:`murano-test-tunner` command to run Murano package tests." - -msgid "" -"Added API endpoint ``/templates/{env_template_id}/services/{path:.*?}`` for " -"environment template application update operation." -msgstr "" -"Added API endpoint ``/templates/{env_template_id}/services/{path:.*?}`` for " -"environment template application update operation." - -msgid "" -"Added Support for application deployment across OpenStack regions. Now, all " -"OpenStack resource classes inherit from ``io.murano.CloudResource`` that " -"provides ``.getRegion()`` method and ``regionName`` property. This allows to " -"assign resources to different regions. ``.getRegion()`` returns ``io.murano." -"CloudRegion`` instance that resource or its parent belongs to. " -"``CloudRegion`` has interface similar to ``Environment`` class and is the " -"correct way to get ``HeatStack`` instance associated with the region, " -"default network configuration, security group manager and agent listener " -"instances. ``Environment`` now acts as default region so backward " -"compatibility is not broken. However new applications should not use " -"environment to set security group rules but rather a region(s) of their " -"instance(s) in order to work correctly when their instances were configured " -"to use region other than the default." -msgstr "" -"Added Support for application deployment across OpenStack regions. Now, all " -"OpenStack resource classes inherit from ``io.murano.CloudResource`` that " -"provides ``.getRegion()`` method and ``regionName`` property. This allows to " -"assign resources to different regions. ``.getRegion()`` returns ``io.murano." -"CloudRegion`` instance that resource or its parent belongs to. " -"``CloudRegion`` has interface similar to ``Environment`` class and is the " -"correct way to get ``HeatStack`` instance associated with the region, " -"default network configuration, security group manager and agent listener " -"instances. ``Environment`` now acts as default region so backward " -"compatibility is not broken. However new applications should not use " -"environment to set security group rules but rather a region(s) of their " -"instance(s) in order to work correctly when their instances were configured " -"to use region other than the default." - -msgid "" -"Added a MetadataAware mixin class capable to retrieve the metadata " -"attributes from the implementing objects and all its parents." -msgstr "" -"Added a MetadataAware mixin class capable to retrieve the metadata " -"attributes from the implementing objects and all its parents." - -msgid "" -"Added a ``metadata()`` yaql function to retrieve the meta information about " -"the object, stored in the \"?/metadata\" section of object model." -msgstr "" -"Added a ``metadata()`` yaql function to retrieve the meta information about " -"the object, stored in the \"?/metadata\" section of object model." - -msgid "" -"Added a new API endpoint ``v1/actions`` to call static public methods. It " -"accepts class name, method name, method arguments, and optionally package " -"name and class version in the request body. This call does not create an " -"environment, object instances or database records." -msgstr "" -"Added a new API endpoint ``v1/actions`` to call static public methods. It " -"accepts class name, method name, method arguments, and optionally package " -"name and class version in the request body. This call does not create an " -"environment, object instances or database records." - -msgid "" -"Added a new engine RPC call to generate json-schema from MuranoPL class. The " -"schema may be generated either from the entire class or for specific model " -"builders - static actions that can be used to generate object model from " -"their input. Class schema is built by inspecting class properties and method " -"schema using the same algorithm but applied to its arguments." -msgstr "" -"Added a new engine RPC call to generate json-schema from MuranoPL class. The " -"schema may be generated either from the entire class or for specific model " -"builders - static actions that can be used to generate object model from " -"their input. Class schema is built by inspecting class properties and method " -"schema using the same algorithm but applied to its arguments." - -msgid "" -"Added a new manifest format 1.4.0. Introduced the 'Scope' keyword for class " -"methods to declare a method's accessibility from outside through the API " -"call." -msgstr "" -"Added a new manifest format 1.4.0. Introduced the 'Scope' keyword for class " -"methods to declare a method's accessibility from outside through the API " -"call." - -msgid "" -"Added a new yaql function 'decryptData' which pairs with 'encryptData' on " -"the dashboard side. Application authors can use these functions to secure " -"sensitive input to their Murano applications such as passwords." -msgstr "" -"Added a new YAQL function 'decryptData' which pairs with 'encryptData' on " -"the dashboard side. Application authors can use these functions to secure " -"sensitive input to their Murano applications such as passwords." - -msgid "Added ability to extend MuranoPL entities with custom metadata." -msgstr "Added ability to extend MuranoPL entities with custom metadata." - -msgid "Added ability to retrieve current user and project info" -msgstr "Added ability to retrieve current user and project info" - -msgid "Added ability to retrieve environment owner user and project info" -msgstr "Added ability to retrieve environment owner user and project info" - -msgid "" -"Added an overload of the new function - ``new($model, $owner)``. It loads " -"complete object graph in a single call. Objects in the model can have cross " -"references. In that case, this is the only way to instantiate the graph. " -"Objects might be specified either in object model format (with '?' attribute " -"or in MuranoPL format (used for Meta definitions)." -msgstr "" -"Added an overload of the new function - ``new($model, $owner)``. It loads " -"complete object graph in a single call. Objects in the model can have cross " -"references. In that case, this is the only way to instantiate the graph. " -"Objects might be specified either in object model format (with '?' attribute " -"or in MuranoPL format (used for Meta definitions)." - -msgid "Added classes that represent OpenStack user and project" -msgstr "Added classes that represent OpenStack user and project" - -msgid "Added clone action to environment templates." -msgstr "Added clone action to environment templates." - -msgid "Added filter by 'Name' which only matches package name." -msgstr "Added filter by 'Name' which only matches package name." - -msgid "" -"Added magnum plugin to murano, that allows creating/deleting of magnum " -"baymodels and bays from MuranoPL" -msgstr "" -"Added Magnum plugin to Murano, that allows creating/deleting of Magnum " -"baymodels and bays from MuranoPL" - -msgid "Added new tool ``murano-status upgrade check``." -msgstr "Added new tool ``murano-status upgrade check``." - -msgid "" -"Added public field to environment templates. GET method for api now displays " -"public templates from other projects(tenants)." -msgstr "" -"Added public field to environment templates. GET method for API now displays " -"public templates from other projects(tenants)." - -msgid "Added public filter to environment templates api." -msgstr "Added public filter to environment templates API." - -msgid "" -"Added the ``api_workers`` option to ``murano`` config group. It controls the " -"number of API workers launched by murano. If not set, it would default to " -"the number of CPUs available." -msgstr "" -"Added the ``api_workers`` option to ``murano`` config group. It controls the " -"number of API workers launched by Murano. If not set, it would default to " -"the number of CPUs available." - -msgid "" -"Added the ``description_text`` field to environment and environment " -"templates database tables and respective API objects." -msgstr "" -"Added the ``description_text`` field to environment and environment " -"templates database tables and respective API objects." - -msgid "" -"Added the ``driver`` configuration option to the ``networking`` group. It " -"allows to explicitly select the networking driver. It supports 'neutron' and " -"'nova' options. If set to ``None`` (default), murano attempts to use " -"'neutron' if available, 'nova' otherwise. The change is backward compatible." -msgstr "" -"Added the ``driver`` configuration option to the ``networking`` group. It " -"allows to explicitly select the networking driver. It supports 'neutron' and " -"'nova' options. If set to ``None`` (default), Murano attempts to use " -"'neutron' if available, 'nova' otherwise. The change is backward compatible." - -msgid "" -"Added the ``timeout`` parameter to ``runCommand`` and ``putFile`` methods of " -"the ``io.murano.configuration.Linux`` class." -msgstr "" -"Added the ``timeout`` parameter to ``runCommand`` and ``putFile`` methods of " -"the ``io.murano.configuration.Linux`` class." - -msgid "" -"Added the capability to declare MuranoPL YAML methods with variable length " -"positional and keyword arguments. This is done using argument ``Usage`` " -"attribute. Regular arguments have Standard usage which is the default. " -"Variable length args (args in Python) should have \"Usage: VarArgs\" and " -"keyword args (kwargs) are declared with \"Usage: KwArgs\". Inside the method " -"they are seen as a list and a dictionary correspondingly. For such arguments " -"contracts are written for individual argument values thus no need to write " -"them as lists/dicts." -msgstr "" -"Added the capability to declare MuranoPL YAML methods with variable length " -"positional and keyword arguments. This is done using argument ``Usage`` " -"attribute. Regular arguments have Standard usage which is the default. " -"Variable length args (args in Python) should have \"Usage: VarArgs\" and " -"keyword args (kwargs) are declared with \"Usage: KwArgs\". Inside the method " -"they are seen as a list and a dictionary correspondingly. For such arguments " -"contracts are written for individual argument values thus no need to write " -"them as lists/dicts." - -msgid "" -"Added the following meta-classes to the core library - ``Title`` " -"``Description`` ``HelpText`` ``Hidden`` ``Section`` ``Position`` " -"``ModelBuilder``. These classes will later be used to implement dynamic " -"object model generation." -msgstr "" -"Added the following meta-classes to the core library - ``Title`` " -"``Description`` ``HelpText`` ``Hidden`` ``Section`` ``Position`` " -"``ModelBuilder``. These classes will later be used to implement dynamic " -"object model generation." - -msgid "" -"All HOT template outputs are put into a single dictionary property " -"'templateOutputs' rather than in a generated property per each output. As a " -"result there are no more constraints on output names." -msgstr "" -"All HOT template outputs are put into a single dictionary property " -"'templateOutputs' rather than in a generated property per each output. As a " -"result there are no more constraints on output names." - -msgid "" -"Avoid race condition during parallel upload of packages, when packages have " -"same tags." -msgstr "" -"Avoid race condition during parallel upload of packages, when packages have " -"same tags." - -msgid "" -"Avoid the `'method' object has no attribute '__yaql_function__'` error when " -"calling some YAQL functions with Python 3" -msgstr "" -"Avoid the `'method' object has no attribute '__yaql_function__'` error when " -"calling some YAQL functions with Python 3" - -msgid "" -"Basic reflection capabilities were added to MuranoPL. Now it is possible to " -"get type info with typeinfo() function and using it as a starting point " -"obtain information about the class, its methods and properties as well as " -"the package of the class. Reflected properties can be used to obtain or set " -"its value in a given object or invoke its method." -msgstr "" -"Basic reflection capabilities were added to MuranoPL. Now it is possible to " -"get type info with typeinfo() function and using it as a starting point " -"obtain information about the class, its methods and properties as well as " -"the package of the class. Reflected properties can be used to obtain or set " -"its value in a given object or invoke its method." - -msgid "Bug Fixes" -msgstr "Bug Fixes" - -msgid "Bumped the RUNTIME_VERSION attribute to 1.5" -msgstr "Bumped the RUNTIME_VERSION attribute to 1.5" - -msgid "" -"Changed the type representation in object model. Previous format was to have " -"three attributes in \"?\" section of the object - type, classVersion and " -"package where only the \"type\" is mandatory. Now they are merged into " -"single attribute \"type\" that has a format ``typeName/version@package``. " -"Version and package parts are still optional." -msgstr "" -"Changed the type representation in object model. Previous format was to have " -"three attributes in \"?\" section of the object - type, classVersion and " -"package where only the \"type\" is mandatory. Now they are merged into " -"single attribute \"type\" that has a format ``typeName/version@package``. " -"Version and package parts are still optional." - -msgid "" -"Class configs are now also versioned. For class foo.bar version 1.2.3 the " -"following file names will be examined: foo.bar-1.2.3.json foo.bar-1.2.3.yaml " -"foo.bar-1.2.json foo.bar-1.2.yaml foo.bar-1.json foo.bar-1.yaml In addition " -"for classes of version 0.x.y file name without version suffix are also " -"examined as a last attempt so the backward compatibility is retained" -msgstr "" -"Class configs are now also versioned. For class foo.bar version 1.2.3 the " -"following file names will be examined: foo.bar-1.2.3.json foo.bar-1.2.3.yaml " -"foo.bar-1.2.json foo.bar-1.2.yaml foo.bar-1.json foo.bar-1.yaml In addition " -"for classes of version 0.x.y file name without version suffix are also " -"examined as a last attempt so the backward compatibility is retained" - -msgid "" -"Classes to work with Cinder volumes were added to core library. Now it is " -"possible to create new volume from various sources or use existing volume. " -"Also it is possible to attach volumes to instances and boot instances from " -"volumes." -msgstr "" -"Classes to work with Cinder volumes were added to core library. Now it is " -"possible to create new volume from various sources or use existing volume. " -"Also it is possible to attach volumes to instances and boot instances from " -"volumes." - -msgid "" -"Core Library's init scripts used to have various problems detecting pre-" -"installed (by DIB) murano-agent on non-ubuntu images. Agent setup script now " -"checks wider list of directories before attempting to install murano-agnet " -"and service script now does not impose strict script location." -msgstr "" -"Core Library's init scripts used to have various problems detecting pre-" -"installed (by DIB) murano-agent on non-ubuntu images. Agent setup script now " -"checks wider list of directories before attempting to install murano-agnet " -"and service script now does not impose strict script location." - -msgid "Current Series Release Notes" -msgstr "Current Series Release Notes" - -msgid "" -"Deprecated the 'Usage Action' keyword. For format versions >= 1.4.0, use " -"'Scope Public' instead." -msgstr "" -"Deprecated the 'Usage Action' keyword. For format versions >= 1.4.0, use " -"'Scope Public' instead." - -msgid "Deprecation Notes" -msgstr "Deprecation Notes" - -msgid "" -"Due to refactoring, contracts work a little bit faster because there is no " -"more need to generate yaql function definition for each contract method on " -"each call." -msgstr "" -"Due to refactoring, contracts work a little bit faster because there is no " -"more need to generate a YAQL function definition for each contract method on " -"each call." - -msgid "Enable mocks in MuranoPL tests cases." -msgstr "Enable mocks in MuranoPL tests cases." - -msgid "" -"Enabling multiple workers might break workflows under BSD and Windows systems" -msgstr "" -"Enabling multiple workers might break workflows under BSD and Windows systems" - -msgid "" -"Equality check (assertEqual) in test-runner can now properly compare two " -"MuranoPl objects." -msgstr "" -"Equality check (assertEqual) in test-runner can now properly compare two " -"MuranoPl objects." - -msgid "" -"File ``murano-paste.ini has been updated to use oslo HTTPProxyToWSGI " -"middleware. Config option ``secure_proxy_ssl_header`` has been removed. " -"Please refer to oslo_middleware configuration options if you wish deploy " -"murano behind TLS proxy. Most notably you would need to set " -"``enable_proxy_headers_parsing`` under group ``oslo_middleware`` to True, to " -"enable header parsing." -msgstr "" -"File ``murano-paste.ini has been updated to use oslo HTTPProxyToWSGI " -"middleware. Config option ``secure_proxy_ssl_header`` has been removed. " -"Please refer to oslo_middleware configuration options if you wish deploy " -"murano behind TLS proxy. Most notably you would need to set " -"``enable_proxy_headers_parsing`` under group ``oslo_middleware`` to True, to " -"enable header parsing." - -msgid "" -"Fixed 'io.murano.SharedIp' class to properly wotk in muti-region " -"environments." -msgstr "" -"Fixed 'io.murano.SharedIp' class to properly wotk in muti-region " -"environments." - -msgid "" -"Fixed a bug when the UI dialog was not displayed in Murano Dashboard for " -"applications which don't have UI definitions bundled in the package but " -"generate them based on the package contents instead. This usually affected " -"HOT-based packages and other non-muranopl-based applications." -msgstr "" -"Fixed a bug when the UI dialogue was not displayed in Murano Dashboard for " -"applications which don't have UI definitions bundled in the package but " -"generate them based on the package contents instead. This usually affected " -"HOT-based packages and other non-muranopl-based applications." - -msgid "" -"Fixed incorrect murano behaviour if deployed on devstack with keystone v3 by " -"default." -msgstr "" -"Fixed incorrect Murano behaviour if deployed on devstack with keystone v3 by " -"default." - -msgid "" -"Fixed the issue that prevented the test-runner from properly invoking " -"``setUp`` and ``tearDown`` methods of fixtures in some cases." -msgstr "" -"Fixed the issue that prevented the test-runner from properly invoking " -"``setUp`` and ``tearDown`` methods of fixtures in some cases." - -msgid "" -"For MuranoPL classes new key \"Usage\" was added. By default it is \"Class" -"\". But it can also be \"Meta\" to define meta-class. Meta-class has all the " -"capabilities of regular classes and in addition has 3 new attributes: " -"Cardinality, Applies and Inherited." -msgstr "" -"For MuranoPL classes new key \"Usage\" was added. By default it is \"Class" -"\". But it can also be \"Meta\" to define meta-class. Meta-class has all the " -"capabilities of regular classes and in addition has 3 new attributes: " -"Cardinality, Applies and Inherited." - -msgid "" -"Heat stacks created by murano during environment deployment now have " -"'murano' tag by default. This is controlled by ``stack_tags`` config " -"parameter." -msgstr "" -"Heat stacks created by Murano during environment deployment now have " -"'murano' tag by default. This is controlled by ``stack_tags`` config " -"parameter." - -msgid "" -"If a VM being a part of some shared-ip group is attached to the network " -"which is not owned by the current tenant (shared network) a policy violation " -"may occur thus failing the deployment." -msgstr "" -"If a VM being a part of some shared-ip group is attached to the network " -"which is not owned by the current tenant (shared network) a policy violation " -"may occur thus failing the deployment." - -msgid "" -"Implemented a new contract function ``template()``. ``template()`` works " -"similar to the ``class()`` in regards to the data validation but does not " -"instantiate objects. Instead, the data is left in the object model in " -"dictionary format so that it could be instantiated later with the ``new()`` " -"function. Additionally, the function allows excluding specified properties " -"from validation and from the resulting template so that they could be " -"provided later. Objects that are assigned to the property or argument with " -"``template()`` contract will be automatically converted to their object " -"model representation." -msgstr "" -"Implemented a new contract function ``template()``. ``template()`` works " -"similar to the ``class()`` in regards to the data validation but does not " -"instantiate objects. Instead, the data is left in the object model in " -"dictionary format so that it could be instantiated later with the ``new()`` " -"function. Additionally, the function allows excluding specified properties " -"from validation and from the resulting template so that they could be " -"provided later. Objects that are assigned to the property or argument with " -"``template()`` contract will be automatically converted to their object " -"model representation." - -msgid "" -"Implemented a new framework for MuranoPL contracts. Now, instead of several " -"independent implementations of the same yaql methods (string(), class() " -"etc.) all implementations of the same method are combined into single class. " -"Therefore, we now have a class per contract method. This also simplifies " -"development of new contracts. Each such class can provide methods for data " -"transformation (default contract usage), validation that is used to decide " -"if the method can be considered an extension method for the value, and json " -"schema generation method that was moved from the schema generator script." -msgstr "" -"Implemented a new framework for MuranoPL contracts. Now, instead of several " -"independent implementations of the same YAQL methods (string(), class() " -"etc.) all implementations of the same method are combined into single class. " -"Therefore, we now have a class per contract method. This also simplifies " -"development of new contracts. Each such class can provide methods for data " -"transformation (default contract usage), validation that is used to decide " -"if the method can be considered an extension method for the value, and JSON " -"schema generation method that was moved from the schema generator script." - -msgid "" -"Implemented the capability for API endpoint ``/catalog/packages`` to filter " -"'id', 'category', 'tag' properties using the 'in' operator. An example of " -"using the 'in' operator for 'id' is 'id=in:id1,id2,id3'. This filter is " -"added using syntax that conforms to the latest guidelines from the OpenStack " -"API-WG." -msgstr "" -"Implemented the capability for API endpoint ``/catalog/packages`` to filter " -"'id', 'category', 'tag' properties using the 'in' operator. An example of " -"using the 'in' operator for 'id' is 'id=in:id1,id2,id3'. This filter is " -"added using syntax that conforms to the latest guidelines from the OpenStack " -"API-WG." - -msgid "" -"Implemented the capability for the helper methods of Linux class to run " -"concurrently if executed for different VM agents." -msgstr "" -"Implemented the capability for the helper methods of Linux class to run " -"concurrently if executed for different VM agents." - -msgid "" -"Internally, both pre-deployment garbage collection (that was done by " -"comparision of ``Objects`` and ``ObjectsCopy``) and post-deployment orphan " -"object collection are now done through the new GC." -msgstr "" -"Internally, both pre-deployment garbage collection (that was done by " -"comparison of ``Objects`` and ``ObjectsCopy``) and post-deployment orphan " -"object collection are now done through the new GC." - -msgid "" -"Introduced a new MuranoPL class ``io.murano.system.GC`` Now MuranoPL garbage " -"collector can be used to set up destruction dependencies between murano " -"objects. If object Foo is subscribed to object Bar's destruction, it will be " -"notified through a specific handler. If both Foo and Bar are going to be " -"destroyed during one execution session, Foo will be destroyed after Bar. You " -"can omit the handler, in this case destruction order will also be preserved. " -"Handler can be a static or a usual function." -msgstr "" -"Introduced a new MuranoPL class ``io.murano.system.GC`` Now MuranoPL garbage " -"collector can be used to set up destruction dependencies between Murano " -"objects. If object Foo is subscribed to object Bar's destruction, it will be " -"notified through a specific handler. If both Foo and Bar are going to be " -"destroyed during one execution session, Foo will be destroyed after Bar. You " -"can omit the handler, in this case destruction order will also be preserved. " -"Handler can be a static or a usual function." - -msgid "" -"Introduced two YAQL *inject* functions to enable mocks ``def inject(target, " -"target_method, mock_object, mock_name)`` and ``def inject(target, " -"target_method, yaql_expr)``." -msgstr "" -"Introduced two YAQL *inject* functions to enable mocks ``def inject(target, " -"target_method, mock_object, mock_name)`` and ``def inject(target, " -"target_method, yaql_expr)``." - -msgid "" -"It is now possible to configure the notifications to use a different " -"transport URL than the RPCs. These could potentially be completely different " -"message broker hosts (though they doesn't need to be). If the notification-" -"specific configuration is not provided, the notifier will use the same " -"transport as the RPCs." -msgstr "" -"It is now possible to configure the notifications to use a different " -"transport URL than the RPCs. These could potentially be completely different " -"message broker hosts (though they doesn't need to be). If the notification-" -"specific configuration is not provided, the notifier will use the same " -"transport as the RPCs." - -msgid "" -"It is now possible to make a GET request to '/deployments' endpoint. This " -"will result in deployments for all environments in a specific project " -"(tenant) being returned." -msgstr "" -"It is now possible to make a GET request to '/deployments' endpoint. This " -"will result in deployments for all environments in a specific project " -"(tenant) being returned." - -msgid "" -"It is now possible to make a PUT request with body equal to '[]' to '/" -"environments//services' endpoint. This will result in removing all " -"apps from current session. This allows deleting the last application from " -"environment from CLI." -msgstr "" -"It is now possible to make a PUT request with body equal to '[]' to '/" -"environments//services' endpoint. This will result in removing all " -"apps from current session. This allows deleting the last application from " -"environment from CLI." - -msgid "" -"It is now possible to use version specifications like '=0.0.0' when " -"``semantic_version`` library version '2.3.1' is installed. Previously such " -"specifications caused an error and '==0.0.0' had to be used." -msgstr "" -"It is now possible to use version specifications like '=0.0.0' when " -"``semantic_version`` library version '2.3.1' is installed. Previously such " -"specifications caused an error and '==0.0.0' had to be used." - -msgid "" -"It is possible to attach meta-class instances to packages, classes " -"(including other meta-classes), properties, methods and method arguments. " -"Each of them got new \"Meta\" key containing list (or single scalar) of meta-" -"class instances." -msgstr "" -"It is possible to attach meta-class instances to packages, classes " -"(including other meta-classes), properties, methods and method arguments. " -"Each of them got new \"Meta\" key containing list (or single scalar) of meta-" -"class instances." - -msgid "" -"It was impossible to explicitly provide attribute owner class to getAttr/" -"setAttr methods without using namespace prefix or if the type was not from " -"the core library." -msgstr "" -"It was impossible to explicitly provide attribute owner class to getAttr/" -"setAttr methods without using namespace prefix or if the type was not from " -"the core library." - -msgid "Known Issues" -msgstr "Known Issues" - -msgid "Liberty Series Release Notes" -msgstr "Liberty Series Release Notes" - -msgid "Mitaka Series Release Notes" -msgstr "Mitaka Series Release Notes" - -msgid "Murano Release Notes" -msgstr "Murano Release Notes" - -msgid "" -"Murano engine can be configured to sign all the RabbitMQ messages sent to " -"the agents. When the RSA key is provided, engine will provide agents with " -"its public part and sign all the messages sent. Agents then will ignore any " -"command that was not sent by the engine." -msgstr "" -"Murano engine can be configured to sign all the RabbitMQ messages sent to " -"the agents. When the RSA key is provided, engine will provide agents with " -"its public part and sign all the messages sent. Agents then will ignore any " -"command that was not sent by the engine." - -msgid "" -"Murano engine is now capable of caching packages on disk for reuse. This is " -"controlled by `packages_cache` directory path and `enable_packages_cache` " -"boolean parameter (true by default). The packages are cached in a eventlet/" -"inter-process safe manner and are cleaned up as soon as newer version of the " -"package becomes available (unless it's used by ongoing deployment)" -msgstr "" -"Murano engine is now capable of caching packages on disk for reuse. This is " -"controlled by `packages_cache` directory path and `enable_packages_cache` " -"boolean parameter (true by default). The packages are cached in a eventlet/" -"inter-process safe manner and are cleaned up as soon as newer version of the " -"package becomes available (unless it's used by ongoing deployment)" - -msgid "" -"Murano engine no longer logs methods ``string()``, ``json()``, and " -"``yaml()`` of the 'io.murano.system.Resources' class. This is done to " -"prevent UnicodeDecodeError's when transferring binary files to murano agent." -msgstr "" -"Murano engine no longer logs methods ``string()``, ``json()``, and " -"``yaml()`` of the 'io.murano.system.Resources' class. This is done to " -"prevent UnicodeDecodeError's when transferring binary files to Murano agent." - -msgid "" -"Murano is now able to assign correct floating IPs when using multiple " -"external networks. It attempts to choose one that shares a router with " -"internal network." -msgstr "" -"Murano is now able to assign correct Floating IPs when using multiple " -"external networks. It attempts to choose one that shares a router with " -"internal network." - -msgid "" -"Murano is now able to deploy applications in the environments with disabled " -"Neutron Security Groups. Detection is based on the presence of 'security-" -"group' Neutron extension." -msgstr "" -"Murano is now able to deploy applications in the environments with disabled " -"Neutron Security Groups. Detection is based on the presence of 'security-" -"group' Neutron extension." - -msgid "" -"Murano is now able to work with keystone configured to use a templated " -"catalog." -msgstr "" -"Murano is now able to work with Keystone configured to use a templated " -"catalogue." - -msgid "" -"Murano no longer specifies fixed-ip parameter for ports when creating VMs " -"attached to networks owned and shared by other tenants. Specifying this " -"parameter for non-owned networks could cause violation of neutron policies." -msgstr "" -"Murano no longer specifies fixed-ip parameter for ports when creating VMs " -"attached to networks owned and shared by other tenants. Specifying this " -"parameter for non-owned networks could cause violation of Neutron policies." - -msgid "" -"Murano switched to using standard oslo middleware HTTPProxyToWSGI instead of " -"custom implementation. This middleware parses the X-Forwarded-Proto HTTP " -"header or the Proxy protocol in order to help murano respond with the " -"correct URL refs when it's put behind a TLS proxy (such as HAProxy). This " -"middleware is disabled by default, but can be enabled via a configuration " -"option in the oslo_middleware group." -msgstr "" -"Murano switched to using standard Oslo middleware HTTPProxyToWSGI instead of " -"custom implementation. This middleware parses the X-Forwarded-Proto HTTP " -"header or the Proxy protocol in order to help Murano respond with the " -"correct URL refs when it's put behind a TLS proxy (such as HAProxy). This " -"middleware is disabled by default, but can be enabled via a configuration " -"option in the oslo_middleware group." - -msgid "Murano was migrated to yaql 1.1" -msgstr "Murano was migrated to YAQL 1.1" - -msgid "New Features" -msgstr "New Features" - -msgid "New database migration 015 has to be applied." -msgstr "New database migration 015 has to be applied." - -msgid "" -"New format MuranoPL/1.3 can be specified in manifest files. MuranoPL/1.3 is " -"identical to MuranoPL/1.2 but except for the fact that MuranoPL/1.3 packages " -"cannot be imported to earlier Murano versions. Thus applications that use " -"new features of yaql 1.1 should use this format version." -msgstr "" -"New format MuranoPL/1.3 can be specified in manifest files. MuranoPL/1.3 is " -"identical to MuranoPL/1.2 but except for the fact that MuranoPL/1.3 packages " -"cannot be imported to earlier Murano versions. Thus applications that use " -"new features of YAQL 1.1 should use this format version." - -msgid "" -"New framework for ``murano-status upgrade check`` command is added. This " -"framework allows adding various checks which can be run before a Murano " -"upgrade to ensure if the upgrade can be performed safely." -msgstr "" -"New framework for ``murano-status upgrade check`` command is added. This " -"framework allows adding various checks which can be run before a Murano " -"upgrade to ensure if the upgrade can be performed safely." - -msgid "" -"New method type: extension methods. Extension methods enable you to \"add\" " -"methods to existing types without modifying the original type. Extension " -"methods are a special kind of static method, but they are called as if they " -"were instance methods on the extended type. Extension methods are identified " -"by \"Usage: Extension\" and the type they extend is determined by their " -"first argument contract. Thus such methods must have at lease one parameter." -msgstr "" -"New method type: extension methods. Extension methods enable you to \"add\" " -"methods to existing types without modifying the original type. Extension " -"methods are a special kind of static method, but they are called as if they " -"were instance methods on the extended type. Extension methods are identified " -"by \"Usage: Extension\" and the type they extend is determined by their " -"first argument contract. Thus such methods must have at lease one parameter." - -msgid "" -"New on-request garbage collector for MuranoPL objects were implemented. " -"Garbage collection is triggered by io.murano.system.GC.collect() static " -"method. Garbage collector destroys all object that are not reachable " -"anymore. GC can handle objects with cross-references and isolated object " -"graphs. When portion of object model becomes not reachable it destroyed in " -"predictable order such that child objects get destroyed before their parents " -"and, when possible, before objects that are subscribed to their destruction " -"notifications." -msgstr "" -"New on-request garbage collector for MuranoPL objects were implemented. " -"Garbage collection is triggered by io.murano.system.GC.collect() static " -"method. Garbage collector destroys all object that are not reachable any " -"more. GC can handle objects with cross-references and isolated object " -"graphs. When portion of object model becomes not reachable it destroyed in " -"predictable order such that child objects get destroyed before their parents " -"and, when possible, before objects that are subscribed to their destruction " -"notifications." - -msgid "" -"New operator *is* was added to MuranoPL. Now it is possible to test if " -"MuranoPL object is of particular type." -msgstr "" -"New operator *is* was added to MuranoPL. Now it is possible to test if " -"MuranoPL object is of particular type." - -msgid "" -"New plugin 'murano_heat-translator_plugin' was added. Now it is possible to " -"deploy applications from CSAR templates." -msgstr "" -"New plugin 'murano_heat-translator_plugin' was added. Now it is possible to " -"deploy applications from CSAR templates." - -msgid "" -"New type-level keyword \"Import\" which can be either list or scalar that " -"specifies type names which extensions methods should be imported into class " -"context and thus become available to type members." -msgstr "" -"New type-level keyword \"Import\" which can be either list or scalar that " -"specifies type names which extensions methods should be imported into class " -"context and thus become available to type members." - -msgid "Newton Series Release Notes" -msgstr "Newton Series Release Notes" - -msgid "Now admin can delete user's environments." -msgstr "Now admin can delete user's environments." - -msgid "" -"Now all native MuranoPL methods (those that are written in Python) have \"?" -"muranoMethod\" metadata key referring to MuranoMethod instance for the " -"method." -msgstr "" -"Now all native MuranoPL methods (those that are written in Python) have \"?" -"muranoMethod\" metadata key referring to MuranoMethod instance for the " -"method." - -msgid "" -"Now it is possible to have several classes in one YAML file. Classes are " -"separated using YAML document separator (3 dashes). Empty documents are " -"skipped. If the class doesn't have Namespace section corresponding section " -"from the previous class in the same file is used. Thus it is possible to " -"declare namespace prefixes once at the file header. Even if there are " -"several classes in one file all of them are still required to be declared in " -"manifest file." -msgstr "" -"Now it is possible to have several classes in one YAML file. Classes are " -"separated using YAML document separator (3 dashes). Empty documents are " -"skipped. If the class doesn't have Namespace section corresponding section " -"from the previous class in the same file is used. Thus it is possible to " -"declare namespace prefixes once at the file header. Even if there are " -"several classes in one file all of them are still required to be declared in " -"manifest file." - -msgid "Ocata Series Release Notes" -msgstr "Ocata Series Release Notes" - -msgid "" -"Operator can now use new CLI tool ``murano-status upgrade check`` to check " -"if Murano deployment can be safely upgraded from N-1 to N release." -msgstr "" -"Operator can now use new CLI tool ``murano-status upgrade check`` to check " -"if Murano deployment can be safely upgraded from N-1 to N release." - -msgid "Other Notes" -msgstr "Other Notes" - -msgid "Pike Series Release Notes" -msgstr "Pike Series Release Notes" - -msgid "Prelude" -msgstr "Prelude" - -msgid "" -"Prevented the resource leak for objects created during deployment with " -"``new()`` function call." -msgstr "" -"Prevented the resource leak for objects created during deployment with " -"``new()`` function call." - -msgid "" -"Previously Cinder Volumes created in MuranoPL were not released correctly on " -"object destruction. The issue is now fixed." -msgstr "" -"Previously Cinder Volumes created in MuranoPL were not released correctly on " -"object destruction. The issue is now fixed." - -msgid "" -"Previously murano assumed that the service user and service project are in " -"the 'Default' domain. These values can now be set in ``keystone_authtoken`` " -"config group." -msgstr "" -"Previously Murano assumed that the service user and service project are in " -"the 'Default' domain. These values can now be set in ``keystone_authtoken`` " -"config group." - -msgid "" -"Previously, when a class overrode a property from its parent class the value " -"was stored separately for both of them, transformed by each of the " -"contracts. Thus each class saw the value of its contract. In absolute " -"majority of the cases, the observed value was the same. However, if the " -"contracts were compatible on the provided value (say int() and string() " -"contracts on the value \"123\") they were different. This is considered to " -"be a bad pattern. Now, the value is stored only once per object and " -"transformed by the contract defined in the actual object type. All base " -"contracts are used to validate the transformed object thus this pattern will " -"not work anymore." -msgstr "" -"Previously, when a class overrode a property from its parent class the value " -"was stored separately for both of them, transformed by each of the " -"contracts. Thus each class saw the value of its contract. In absolute " -"majority of the cases, the observed value was the same. However, if the " -"contracts were compatible on the provided value (say int() and string() " -"contracts on the value \"123\") they were different. This is considered to " -"be a bad pattern. Now, the value is stored only once per object and " -"transformed by the contract defined in the actual object type. All base " -"contracts are used to validate the transformed object thus this pattern will " -"not work any more." - -msgid "" -"Previously, when pre-deployment garbage collection occurred it executed ``." -"destroy`` method for objects that were present in the ``ObjectsCopy`` " -"section of the object model (which is the the snapshot of the model after " -"last deployment) and not present in the current model anymore (because they " -"were deleted through the API between deployments). If the destroyed objects " -"were to access another object that was not deleted it was accessing its copy " -"from the ``ObjectsCopy``. Thus any changes to the internal state made by " -"that object were lost after the garbage collection finished (that is, before " -"the ``.deploy`` method call) and could not affect the deployment. Now, if " -"the object is present in both ``Objects`` and ``ObjectsCopy``, a single " -"instance (the one from ``Objects``) is used for both garbage collection and " -"deployment. As a consequence, instances (in their ``.destroy`` method) now " -"may observe changes made to other objects they refer if they were not " -"deleted, but modified through the API. In some rare cases, it may break " -"existing applications." -msgstr "" -"Previously, when pre-deployment garbage collection occurred it executed ``." -"destroy`` method for objects that were present in the ``ObjectsCopy`` " -"section of the object model (which is the the snapshot of the model after " -"last deployment) and not present in the current model any more (because they " -"were deleted through the API between deployments). If the destroyed objects " -"were to access another object that was not deleted it was accessing its copy " -"from the ``ObjectsCopy``. Thus any changes to the internal state made by " -"that object were lost after the garbage collection finished (that is, before " -"the ``.deploy`` method call) and could not affect the deployment. Now, if " -"the object is present in both ``Objects`` and ``ObjectsCopy``, a single " -"instance (the one from ``Objects``) is used for both garbage collection and " -"deployment. As a consequence, instances (in their ``.destroy`` method) now " -"may observe changes made to other objects they refer if they were not " -"deleted, but modified through the API. In some rare cases, it may break " -"existing applications." - -msgid "" -"Python 2.7 support has been dropped. Last release of Murano to support " -"python 2.7 is OpenStack Train. The minimum version of Python now supported " -"by Murano is Python 3.6." -msgstr "" -"Python 2.7 support has been dropped. Last release of Murano to support " -"python 2.7 is OpenStack Train. The minimum version of Python now supported " -"by Murano is Python 3.6." - -msgid "" -"Python 3.6 & 3.7 support has been dropped. The minimum version of Python now " -"supported is Python 3.8." -msgstr "" -"Python 3.6 & 3.7 support has been dropped. The minimum version of Python now " -"supported is Python 3.8." - -msgid "Queens Series Release Notes" -msgstr "Queens Series Release Notes" - -msgid "" -"Remove hardcoded constant called 'ITERATORS_LIMIT', that can be exceeded " -"(2000) having big amount of objects. Introduce dsl_iterators_limit " -"configuration option instead of constant." -msgstr "" -"Remove hardcoded constant called 'ITERATORS_LIMIT', that can be exceeded " -"(2000) having big amount of objects. Introduce dsl_iterators_limit " -"configuration option instead of constant." - -msgid "" -"Removed `show_categories` endpoint from the application catalog API which " -"has been deprecated since the Liberty cycle. Use `list_categories` instead." -msgstr "" -"Removed `show_categories` endpoint from the application catalogue API which " -"has been deprecated since the Liberty cycle. Use `list_categories` instead." - -msgid "" -"Removed the need for Keystone v2 options (admin_user, admin_password, " -"admin_tenant_name) when Keystone v3 is in use." -msgstr "" -"Removed the need for Keystone v2 options (admin_user, admin_password, " -"admin_tenant_name) when Keystone v3 is in use." - -msgid "" -"Renamed the ``workers`` option from the ``engine`` group to " -"``engine_workers`` to reduce ambiguity with the ``api_workers`` option." -msgstr "" -"Renamed the ``workers`` option from the ``engine`` group to " -"``engine_workers`` to reduce ambiguity with the ``api_workers`` option." - -msgid "" -"RequestContext now serialises it's roles. This should allow murano to work " -"correctly (and allow rules like \"role:xxx\" in policy.json) when using oslo." -"context prior to 2.2.0 and oslo.policy" -msgstr "" -"RequestContext now serialises it's roles. This should allow Murano to work " -"correctly (and allow rules like \"role:xxx\" in policy.json) when using oslo." -"context prior to 2.2.0 and oslo.policy" - -msgid "" -"Requires a valid secret storage backend (e.g. Barbican) to be configured via " -"Castellan." -msgstr "" -"Requires a valid secret storage backend (e.g. Barbican) to be configured via " -"Castellan." - -msgid "Rocky Series Release Notes" -msgstr "Rocky Series Release Notes" - -msgid "Security Issues" -msgstr "Security Issues" - -msgid "" -"Separated murano service broker from murano-api into a murano-cfapi service. " -"Created a separate database and ``paste.ini`` for service broker." -msgstr "" -"Separated Murano service broker from murano-api into a murano-cfapi service. " -"Created a separate database and ``paste.ini`` for service broker." - -msgid "" -"Since Newton release, heat is available as a devstack plugin. So we remove " -"heat as enable_service in devstack." -msgstr "" -"Since Newton release, heat is available as a devstack plugin. So we remove " -"heat as enable_service in devstack." - -msgid "" -"Split ``Instance``'s ``.deploy()`` method into two phases - " -"``beginDeploy()`` and ``endDeploy()``. This allows the application developer " -"to provision multiple instances at once without the need to push the stack " -"for each instance." -msgstr "" -"Split ``Instance``'s ``.deploy()`` method into two phases - " -"``beginDeploy()`` and ``endDeploy()``. This allows the application developer " -"to provision multiple instances at once without the need to push the stack " -"for each instance." - -msgid "" -"Static methods and properties were introduced. Both properties and methods " -"can be marked as Usage: Static Statics can be accessed using ns:Class." -"property / ns:Class.method(), :Class.property / :Class.method() to access " -"class from current namespace or type('full.name').property / type('full." -"name').method() to use full type name." -msgstr "" -"Static methods and properties were introduced. Both properties and methods " -"can be marked as Usage: Static Statics can be accessed using ns:Class." -"property / ns:Class.method(), :Class.property / :Class.method() to access " -"class from current namespace or type('full.name').property / type('full." -"name').method() to use full type name." - -msgid "Stein Series Release Notes" -msgstr "Stein Series Release Notes" - -msgid "" -"The ``string()`` contract no longer converts everything to string values. " -"Now it only converts scalar values to strings. Previous behavior allowed " -"``string()`` property to accept lists and convert them to their Python " -"string representation which is clearly not what developers expected." -msgstr "" -"The ``string()`` contract no longer converts everything to string values. " -"Now it only converts scalar values to strings. Previous behaviour allowed " -"``string()`` property to accept lists and convert them to their Python " -"string representation which is clearly not what developers expected." - -msgid "" -"The contract ``class()`` now uses the same approach to load classes from " -"dictionaries. Thus the same two syntaxes apply there as well." -msgstr "" -"The contract ``class()`` now uses the same approach to load classes from " -"dictionaries. Thus the same two syntaxes apply there as well." - -msgid "" -"The default value of ``[oslo_policy] policy_file`` config option has been " -"changed from ``policy.json`` to ``policy.yaml``. Operators who are utilizing " -"customized or previously generated static policy JSON files (which are not " -"needed by default), should generate new policy files or convert them in YAML " -"format. Use the `oslopolicy-convert-json-to-yaml `_ tool to " -"convert a JSON to YAML formatted policy file in backward compatible way." -msgstr "" -"The default value of ``[oslo_policy] policy_file`` config option has been " -"changed from ``policy.json`` to ``policy.yaml``. Operators who are utilising " -"customised or previously generated static policy JSON files (which are not " -"needed by default), should generate new policy files or convert them in YAML " -"format. Use the `oslopolicy-convert-json-to-yaml `_ tool to " -"convert a JSON to YAML formatted policy file in backward compatible way." - -msgid "" -"The test-runner now does not output logs to stderr by default unless a " -"'use_stderr' parameter is specified in the configuration file." -msgstr "" -"The test-runner now does not output logs to stderr by default unless a " -"'use_stderr' parameter is specified in the configuration file." - -msgid "" -"The test-runner now outputs the tests it runs and their results to stdout " -"directly, instead of the logging system." -msgstr "" -"The test-runner now outputs the tests it runs and their results to stdout " -"directly, instead of the logging system." - -msgid "" -"The value that is stored in the object's properties is obtained by executing " -"special \"finalize\" contract implementation which by default returns the " -"input value unmodified. Because validation happens on the transformed value " -"before it gets finalized it is possible for transformation to return a value " -"that will pass the validation though the final value won't. This is used to " -"relax the template() contract limitation that prevented child class from " -"excluding additional properties from the template." -msgstr "" -"The value that is stored in the object's properties is obtained by executing " -"special \"finalise\" contract implementation which by default returns the " -"input value unmodified. Because validation happens on the transformed value " -"before it gets finalized it is possible for transformation to return a value " -"that will pass the validation though the final value won't. This is used to " -"relax the template() contract limitation that prevented child class from " -"excluding additional properties from the template." - -msgid "Train Series Release Notes" -msgstr "Train Series Release Notes" - -msgid "Upgrade Notes" -msgstr "Upgrade Notes" - -msgid "" -"Use of JSON policy files was deprecated by the ``oslo.policy`` library " -"during the Victoria development cycle. As a result, this deprecation is " -"being noted in the Wallaby cycle with an anticipated future removal of " -"support by ``oslo.policy``. As such operators will need to convert to YAML " -"policy files. Please see the upgrade notes for details on migration of any " -"custom policy files." -msgstr "" -"Use of JSON policy files was deprecated by the ``oslo.policy`` library " -"during the Victoria development cycle. As a result, this deprecation is " -"being noted in the Wallaby cycle with an anticipated future removal of " -"support by ``oslo.policy``. As such operators will need to convert to YAML " -"policy files. Please see the upgrade notes for details on the migration of " -"any custom policy files." - -msgid "" -"Users can now assign an existing security group to an application as an " -"alternative to using the one created by Murano's ``SecurityGroupManager``." -msgstr "" -"Users can now assign an existing security group to an application as an " -"alternative to using the one created by Murano's ``SecurityGroupManager``." - -msgid "Ussuri Series Release Notes" -msgstr "Ussuri Series Release Notes" - -msgid "Victoria Series Release Notes" -msgstr "Victoria Series Release Notes" - -msgid "Wallaby Series Release Notes" -msgstr "Wallaby Series Release Notes" - -msgid "" -"When updating to Mitaka, the operator should update service name and type " -"for endpoint in keystone from \"application_catalog\" to \"application-" -"catalog\" if SQL is used for catalog back-end driver." -msgstr "" -"When updating to Mitaka, the operator should update service name and type " -"for endpoint in Keystone from \"application_catalog\" to \"application-" -"catalog\" if SQL is used for catalogue back-end driver." - -msgid "" -"Whenever murano-engine accesses script files, text script files are opened " -"in 'rU' mode which recognizes all types of newlines, and binary files are " -"opened in 'rb' mode to prevent their corruption." -msgstr "" -"Whenever murano-engine accesses script files, text script files are opened " -"in 'rU' mode which recognises all types of newlines, and binary files are " -"opened in 'rb' mode to prevent their corruption." - -msgid "Xena Series Release Notes" -msgstr "Xena Series Release Notes" - -msgid "Yoga Series Release Notes" -msgstr "Yoga Series Release Notes" - -msgid "Zed Series Release Notes" -msgstr "Zed Series Release Notes" - -msgid "" -"added default rules to NeutronSecurityGroupManager to avoid error if " -"`createDefaultInstanceSecurityGroupRules()` method isn't extended in " -"inheritor and SecurityGroups isn't created in application with call of " -"`addGroupIngress()` method." -msgstr "" -"added default rules to NeutronSecurityGroupManager to avoid error if " -"`createDefaultInstanceSecurityGroupRules()` method isn't extended in " -"inheritor and SecurityGroups isn't created in application with call of " -"`addGroupIngress()` method." - -msgid "" -"cve-2016-4972 has been addressed. In ceveral places Murano used loaders " -"inherited directly from yaml.Loader when parsing MuranoPL and UI files from " -"packages. This is unsafe, because this loader is capable of creating custom " -"python objects from specifically constructed yaml files. With this change " -"all yaml loading operations are done using safe loaders instead." -msgstr "" -"cve-2016-4972 has been addressed. In several places Murano used loaders " -"inherited directly from yaml.Loader when parsing MuranoPL and UI files from " -"packages. This is unsafe, because this loader is capable of creating custom " -"Python objects from specifically constructed YAML files. With this change " -"all YAML loading operations are done using safe loaders instead." - -msgid "io.murano.configuration.Linux methods are now static" -msgstr "io.murano.configuration.Linux methods are now static" - -msgid "" -"io.murano.system.GC.isDestroyed() static method was added. It checks if the " -"object is destroyed and thus no methods can be invoked on it." -msgstr "" -"io.murano.system.GC.isDestroyed() static method was added. It checks if the " -"object is destroyed and thus no methods can be invoked on it." - -msgid "" -"io.murano.system.GC.isDoomed() static method was added. It can be used " -"within the ``.destroy`` method to test if other object is also going to be " -"destroyed." -msgstr "" -"io.murano.system.GC.isDoomed() static method was added. It can be used " -"within the ``.destroy`` method to test if other object is also going to be " -"destroyed." - -msgid "" -"io.murano.system.HeatStack.push can be called with async => true flag for " -"asynchronous push" -msgstr "" -"io.murano.system.HeatStack.push can be called with async => true flag for " -"asynchronous push" diff --git a/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po b/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po deleted file mode 100644 index 8d7494797..000000000 --- a/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po +++ /dev/null @@ -1,69 +0,0 @@ -# Gérald LONLAS , 2016. #zanata -msgid "" -msgstr "" -"Project-Id-Version: murano\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-26 08:25+0000\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"PO-Revision-Date: 2016-10-22 04:53+0000\n" -"Last-Translator: Gérald LONLAS \n" -"Language-Team: French\n" -"Language: fr\n" -"X-Generator: Zanata 4.3.3\n" -"Plural-Forms: nplurals=2; plural=(n > 1)\n" - -msgid "1.0.2" -msgstr "1.0.2" - -msgid "1.0.3" -msgstr "1.0.3" - -msgid "2.0.0" -msgstr "2.0.0" - -msgid "2.0.1" -msgstr "2.0.1" - -msgid "2.0.2" -msgstr "2.0.2" - -msgid "3.0.0" -msgstr "3.0.0" - -msgid "Bug Fixes" -msgstr "Corrections de bugs" - -msgid "Current Series Release Notes" -msgstr "Note de la release actuelle" - -msgid "Deprecation Notes" -msgstr "Notes dépréciées " - -msgid "Known Issues" -msgstr "Problèmes connus" - -msgid "Liberty Series Release Notes" -msgstr "Note de release pour Liberty" - -msgid "Mitaka Series Release Notes" -msgstr "Note de release pour Mitaka" - -msgid "Murano Release Notes" -msgstr "Note de release de Murano" - -msgid "New Features" -msgstr "Nouvelles fonctionnalités" - -msgid "Newton Series Release Notes" -msgstr "Note de release pour Newton" - -msgid "Other Notes" -msgstr "Autres notes" - -msgid "Security Issues" -msgstr "Problèmes de sécurités" - -msgid "Upgrade Notes" -msgstr "Notes de mises à jours" diff --git a/releasenotes/source/mitaka.rst b/releasenotes/source/mitaka.rst deleted file mode 100644 index e54560965..000000000 --- a/releasenotes/source/mitaka.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Mitaka Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/mitaka diff --git a/releasenotes/source/newton.rst b/releasenotes/source/newton.rst deleted file mode 100644 index 97036ed25..000000000 --- a/releasenotes/source/newton.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Newton Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/newton diff --git a/releasenotes/source/ocata.rst b/releasenotes/source/ocata.rst deleted file mode 100644 index ebe62f42e..000000000 --- a/releasenotes/source/ocata.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Ocata Series Release Notes -=================================== - -.. release-notes:: - :branch: origin/stable/ocata diff --git a/releasenotes/source/pike.rst b/releasenotes/source/pike.rst deleted file mode 100644 index e43bfc0ce..000000000 --- a/releasenotes/source/pike.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Pike Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/pike diff --git a/releasenotes/source/queens.rst b/releasenotes/source/queens.rst deleted file mode 100644 index 36ac6160c..000000000 --- a/releasenotes/source/queens.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Queens Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/queens diff --git a/releasenotes/source/rocky.rst b/releasenotes/source/rocky.rst deleted file mode 100644 index 40dd517b7..000000000 --- a/releasenotes/source/rocky.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Rocky Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/rocky diff --git a/releasenotes/source/stein.rst b/releasenotes/source/stein.rst deleted file mode 100644 index efaceb667..000000000 --- a/releasenotes/source/stein.rst +++ /dev/null @@ -1,6 +0,0 @@ -=================================== - Stein Series Release Notes -=================================== - -.. release-notes:: - :branch: stable/stein diff --git a/releasenotes/source/train.rst b/releasenotes/source/train.rst deleted file mode 100644 index 583900393..000000000 --- a/releasenotes/source/train.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================== -Train Series Release Notes -========================== - -.. release-notes:: - :branch: stable/train diff --git a/releasenotes/source/unreleased.rst b/releasenotes/source/unreleased.rst deleted file mode 100644 index cd22aabcc..000000000 --- a/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -============================== - Current Series Release Notes -============================== - -.. release-notes:: diff --git a/releasenotes/source/ussuri.rst b/releasenotes/source/ussuri.rst deleted file mode 100644 index e21e50e0c..000000000 --- a/releasenotes/source/ussuri.rst +++ /dev/null @@ -1,6 +0,0 @@ -=========================== -Ussuri Series Release Notes -=========================== - -.. release-notes:: - :branch: stable/ussuri diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst deleted file mode 100644 index 4efc7b6f3..000000000 --- a/releasenotes/source/victoria.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================= -Victoria Series Release Notes -============================= - -.. release-notes:: - :branch: stable/victoria diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst deleted file mode 100644 index d77b56599..000000000 --- a/releasenotes/source/wallaby.rst +++ /dev/null @@ -1,6 +0,0 @@ -============================ -Wallaby Series Release Notes -============================ - -.. release-notes:: - :branch: stable/wallaby diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst deleted file mode 100644 index 1be85be3e..000000000 --- a/releasenotes/source/xena.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -Xena Series Release Notes -========================= - -.. release-notes:: - :branch: stable/xena diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst deleted file mode 100644 index 7cd5e908a..000000000 --- a/releasenotes/source/yoga.rst +++ /dev/null @@ -1,6 +0,0 @@ -========================= -Yoga Series Release Notes -========================= - -.. release-notes:: - :branch: stable/yoga diff --git a/releasenotes/source/zed.rst b/releasenotes/source/zed.rst deleted file mode 100644 index 9608c05e4..000000000 --- a/releasenotes/source/zed.rst +++ /dev/null @@ -1,6 +0,0 @@ -======================== -Zed Series Release Notes -======================== - -.. release-notes:: - :branch: stable/zed diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 63be51922..000000000 --- a/requirements.txt +++ /dev/null @@ -1,53 +0,0 @@ -# Requirements lower bounds listed here are our best effort to keep them up to -# date but we do not test them so no guarantee of having them all correct. If -# you find any incorrect lower bounds, let us know or propose a fix. - -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -pbr!=2.1.0,>=2.0.0 # Apache-2.0 -Babel!=2.4.0,>=2.3.4 # BSD -SQLAlchemy!=1.1.5,!=1.1.6,!=1.1.7,!=1.1.8,>=1.0.10 # MIT -stevedore>=1.20.0 # Apache-2.0 -alembic>=0.9.6 # MIT -eventlet>=0.26.0 # MIT -PasteDeploy>=1.5.0 # MIT -Routes>=2.3.1 # MIT -tenacity>=4.12.0 # Apache-2.0 -WebOb>=1.7.1 # MIT -kombu>=4.6.1 # BSD -psutil>=3.2.2 # BSD -netaddr>=0.7.18 # BSD -PyYAML>=5.1 # MIT -jsonpatch!=1.20,>=1.16 # BSD -keystoneauth1>=3.8.0 # Apache-2.0 -keystonemiddleware>=4.17.0 # Apache-2.0 -testtools>=2.2.0 # MIT -yaql>=1.1.3 # Apache 2.0 License -debtcollector>=1.2.0 # Apache-2.0 -cryptography>=2.7 # BSD/Apache-2.0 - -# For paste.util.template used in keystone.common.template -Paste>=2.0.2 # MIT - -jsonschema>=3.2.0 # MIT -python-keystoneclient>=3.17.0 # Apache-2.0 -python-heatclient>=1.10.0 # Apache-2.0 -python-neutronclient>=6.7.0 # Apache-2.0 -python-muranoclient>=0.8.2 # Apache-2.0 -python-mistralclient!=3.2.0,>=3.1.0 # Apache-2.0 -oslo.db>=4.44.0 # Apache-2.0 -oslo.config>=6.8.0 # Apache-2.0 -oslo.concurrency>=3.26.0 # Apache-2.0 -oslo.context>=2.22.0 # Apache-2.0 -oslo.policy>=3.6.0 # Apache-2.0 -oslo.messaging>=5.29.0 # Apache-2.0 -oslo.middleware>=3.31.0 # Apache-2.0 -oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 -oslo.service>=1.31.0 # Apache-2.0 -oslo.utils>=4.5.0 # Apache-2.0 -oslo.i18n>=3.15.3 # Apache-2.0 -oslo.log>=3.36.0 # Apache-2.0 -oslo.upgradecheck>=1.3.0 # Apache-2.0 -semantic-version>=2.8.2 # BSD -castellan>=0.18.0 # Apache-2.0 diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 0cf3c3755..000000000 --- a/setup.cfg +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed 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. - - -[metadata] -name = murano -summary = Murano API -description-file = - README.rst -license = Apache License, Version 2.0 -author = OpenStack -author-email = openstack-discuss@lists.openstack.org -home-page = https://www.openstack.org/software/releases/mitaka/components/murano -python-requires = >=3.8 -classifier = - Development Status :: 5 - Production/Stable - Environment :: OpenStack - Intended Audience :: Developers - Intended Audience :: Information Technology - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - Programming Language :: Python - Programming Language :: Python :: Implementation :: CPython - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 - -[files] -packages = - murano -data_files = - etc/murano = - etc/murano/murano-cfapi-paste.ini - etc/murano/murano-paste.ini - -[entry_points] -console_scripts = - murano-api = murano.cmd.api:main - murano-engine = murano.cmd.engine:main - murano-manage = murano.cmd.manage:main - murano-db-manage = murano.cmd.db_manage:main - murano-cfapi-db-manage = murano.cmd.cfapi_db_manage:main - murano-test-runner = murano.cmd.test_runner:main - murano-cfapi = murano.cmd.cfapi:main - murano-status = murano.cmd.status:main - -wsgi_scripts = - murano-wsgi-api = murano.httpd.murano_api:init_application - -oslo.config.opts = - murano = murano.opts:list_opts - keystone_authtoken = keystonemiddleware.opts:list_auth_token_opts - murano.cfapi = murano.opts:list_cfapi_opts - castellan.config = castellan.options:list_opts - -oslo.config.opts.defaults = - murano = murano.common.config:set_lib_defaults - -oslo.policy.policies = - # With the move of default policy in code list_rules returns a list of - # the default defined polices. - murano = murano.common.policies:list_rules - -murano_policy_modify_actions = - remove-object = murano.policy.modify.actions.default_actions:RemoveObjectAction - add-object = murano.policy.modify.actions.default_actions:AddObjectAction - set-property = murano.policy.modify.actions.default_actions:SetPropertyAction - remove-relation = murano.policy.modify.actions.default_actions:RemoveRelationAction - add-relation = murano.policy.modify.actions.default_actions:AddRelationAction diff --git a/setup.py b/setup.py deleted file mode 100644 index cd35c3c35..000000000 --- a/setup.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. -# -# Licensed 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 setuptools - -setuptools.setup( - setup_requires=['pbr>=2.0.0'], - pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 3ae121734..000000000 --- a/test-requirements.txt +++ /dev/null @@ -1,23 +0,0 @@ -# The order of packages is significant, because pip processes them in the order -# of appearance. Changing the order has an impact on the overall integration -# process, which may cause wedges in the gate later. -hacking>=3.0.1,<3.1.0 # Apache-2.0 - -coverage!=4.4,>=4.0 # Apache-2.0 -fixtures>=3.0.0 # Apache-2.0/BSD -nose>=1.3.7 # LGPL -oslotest>=4.4.1 # Apache-2.0 -sqlalchemy-migrate>=0.11.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD -testresources>=2.0.0 # Apache-2.0/BSD -testscenarios>=0.4 # Apache-2.0/BSD -pylint==1.4.5 # GPLv2 -pycodestyle>=2.5.0 # MIT License -requests>=2.20.0 # Apache-2.0 -stestr>=2.0.0 # Apache-2.0 -murano-pkg-check>=0.3.0 # Apache-2.0 -bandit>=1.1.0,!=1.6.0 # Apache-2.0 - -# Some of the tests use real MySQL and Postgres databases -PyMySQL>=0.8.0 # MIT License -psycopg2>=2.8.5 # LGPL/ZPL diff --git a/tools/lintstack.py b/tools/lintstack.py deleted file mode 100755 index d5f19a08d..000000000 --- a/tools/lintstack.py +++ /dev/null @@ -1,241 +0,0 @@ -#!/usr/bin/env python -# Copyright (c) 2015 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed 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. - -"""pylint error checking.""" - -import io -import json -import os -import re -import sys - -from pylint import lint -from pylint.reporters import text - -# enabled checks -# http://pylint-messages.wikidot.com/all-codes -ENABLED_CODES = ( - # refactor category - "R0801", "R0911", "R0912", "R0913", "R0914", "R0915", - # warning category - "W0612", "W0613", "W0703", - # convention category - "C1001") - -LINE_PATTERN = r"(\S+):(\d+): \[(\S+)(, \S*)?] (.*)" - -KNOWN_PYLINT_EXCEPTIONS_FILE = "tools/pylint_exceptions" - - -class LintOutput(object): - - _cached_filename = None - _cached_content = None - - def __init__(self, filename, lineno, line_content, code, message, - lintoutput, additional_content): - self.filename = filename - self.lineno = lineno - self.line_content = line_content - self.code = code - self.message = message - self.lintoutput = lintoutput - self.additional_content = additional_content - - @classmethod - def get_duplicate_code_location(cls, remaining_lines): - module, lineno = remaining_lines.pop(0)[2:].split(":") - filename = module.replace(".", os.sep) + ".py" - return filename, int(lineno) - - @classmethod - def get_line_content(cls, filename, lineno): - if cls._cached_filename != filename: - with open(filename) as f: - cls._cached_content = list(f.readlines()) - cls._cached_filename = filename - # find first non-empty line - lineno -= 1 - while True: - line_content = cls._cached_content[lineno].rstrip() - lineno += 1 - if line_content: - return line_content - - @classmethod - def from_line(cls, line, remaining_lines): - m = re.search(LINE_PATTERN, line) - if not m: - return None - matched = m.groups() - filename, lineno, code, message = (matched[0], int(matched[1]), - matched[2], matched[-1]) - additional_content = None - - # duplicate code output needs special handling - if "duplicate-code" in code: - line_count = 0 - for next_line in remaining_lines: - if re.search(LINE_PATTERN, next_line): - break - line_count += 1 - if line_count: - additional_content = remaining_lines[0:line_count] - - filename, lineno = cls.get_duplicate_code_location(remaining_lines) - # fixes incorrectly reported file path - line = line.replace(matched[0], filename) - line = line.replace(":%s:" % matched[1], "") - - line_content = cls.get_line_content(filename, lineno) - return cls(filename, lineno, line_content, code, message, - line.rstrip(), additional_content) - - @classmethod - def from_msg_to_dict(cls, msg): - """Creates dict from pylint msg - - From the output of pylint msg, to a dict, where each key - is a unique error identifier, value is a list of LintOutput - """ - result = {} - lines = msg.splitlines() - while lines: - line = lines.pop(0) - obj = cls.from_line(line, lines) - if not obj: - continue - key = obj.key() - if key not in result: - result[key] = [] - result[key].append(obj) - return result - - def key(self): - return self.message, self.line_content.strip() - - def json(self): - return json.dumps(self.__dict__) - - def review_str(self): - kargs = {"filename": self.filename, - "lineno": self.lineno, - "line_content": self.line_content, - "code": self.code, - "message": self.message} - return ("File %(filename)s\nLine %(lineno)d:%(line_content)s\n" - "%(code)s: %(message)s" % kargs) - - -class ErrorKeys(object): - - @classmethod - def print_json(cls, errors, output=sys.stdout): - print("# automatically generated by tools/lintstack.py", file=output) - for i in sorted(errors.keys()): - print(json.dumps(i), file=output) - - @classmethod - def from_file(cls, filename): - keys = set() - for line in open(filename): - if line and line[0] != "#": - d = json.loads(line) - keys.add(tuple(d)) - return keys - - -def run_pylint(): - buff = io.StringIO() - reporter = text.ParseableTextReporter(output=buff) - args = ["-rn", "--disable=all", "--enable=" + ",".join(ENABLED_CODES), - "murano"] - lint.Run(args, reporter=reporter, exit=False) - val = buff.getvalue() - buff.close() - return val - - -def generate_error_keys(msg=None): - print("Generating", KNOWN_PYLINT_EXCEPTIONS_FILE) - if msg is None: - msg = run_pylint() - - errors = LintOutput.from_msg_to_dict(msg) - with open(KNOWN_PYLINT_EXCEPTIONS_FILE, "w") as f: - ErrorKeys.print_json(errors, output=f) - - -def validate(newmsg=None): - print("Loading", KNOWN_PYLINT_EXCEPTIONS_FILE) - known = ErrorKeys.from_file(KNOWN_PYLINT_EXCEPTIONS_FILE) - if newmsg is None: - print("Running pylint. Be patient...") - newmsg = run_pylint() - errors = LintOutput.from_msg_to_dict(newmsg) - - print() - print("Unique errors reported by pylint: was %d, now %d." - % (len(known), len(errors))) - passed = True - for err_key, err_list in errors.items(): - for err in err_list: - if err_key not in known: - print() - print(err.lintoutput) - print(err.review_str()) - if err.additional_content: - max_len = max(map(len, err.additional_content)) - print("-" * max_len) - print(os.linesep.join(err.additional_content)) - print("-" * max_len) - passed = False - if passed: - print("Congrats! pylint check passed.") - redundant = known - set(errors.keys()) - if redundant: - print("Extra credit: some known pylint exceptions disappeared.") - for i in sorted(redundant): - print(json.dumps(i)) - print("Consider regenerating the exception file if you will.") - else: - print() - print("Please fix the errors above. If you believe they are false " - "positives, run 'tools/lintstack.py generate' to overwrite.") - sys.exit(1) - - -def usage(): - print("""Usage: tools/lintstack.py [generate|validate] - To generate pylint_exceptions file: tools/lintstack.py generate - To validate the current commit: tools/lintstack.py - """) - - -def main(): - option = "validate" - if len(sys.argv) > 1: - option = sys.argv[1] - if option == "generate": - generate_error_keys() - elif option == "validate": - validate() - else: - usage() - - -if __name__ == "__main__": - main() diff --git a/tools/lintstack.sh b/tools/lintstack.sh deleted file mode 100755 index 7ba585e17..000000000 --- a/tools/lintstack.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# Use lintstack.py to compare pylint errors. -# We run pylint twice, once on HEAD, once on the code before the latest -# commit for review. -set -e -TOOLS_DIR=$(cd $(dirname "$0") && pwd) -# Get the current branch name. -GITHEAD=`git rev-parse --abbrev-ref HEAD` -if [[ "$GITHEAD" == "HEAD" ]]; then - # In detached head mode, get revision number instead - GITHEAD=`git rev-parse HEAD` - echo "Currently we are at commit $GITHEAD" -else - echo "Currently we are at branch $GITHEAD" -fi - -cp -f $TOOLS_DIR/lintstack.py $TOOLS_DIR/lintstack.head.py - -if git rev-parse HEAD^2 2>/dev/null; then - # The HEAD is a Merge commit. Here, the patch to review is - # HEAD^2, the master branch is at HEAD^1, and the patch was - # written based on HEAD^2~1. - PREV_COMMIT=`git rev-parse HEAD^2~1` - git checkout HEAD~1 - # The git merge is necessary for reviews with a series of patches. - # If not, this is a no-op so won't hurt either. - git merge $PREV_COMMIT -else - # The HEAD is not a merge commit. This won't happen on gerrit. - # Most likely you are running against your own patch locally. - # We assume the patch to examine is HEAD, and we compare it against - # HEAD~1 - git checkout HEAD~1 -fi - -# First generate tools/pylint_exceptions from HEAD~1 -$TOOLS_DIR/lintstack.head.py generate -# Then use that as a reference to compare against HEAD -git checkout $GITHEAD -$TOOLS_DIR/lintstack.head.py -echo "Check passed. FYI: the pylint exceptions are:" -cat $TOOLS_DIR/pylint_exceptions - diff --git a/tools/test-setup.sh b/tools/test-setup.sh deleted file mode 100755 index b112326fb..000000000 --- a/tools/test-setup.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -xe - -# This script will be run by OpenStack CI before unit tests are run, -# it sets up the test system as needed. -# Developers should setup their test systems in a similar way. - -# This setup needs to be run as a user that can run sudo. - -# The root password for the MySQL database; pass it in via -# MYSQL_ROOT_PW. -DB_ROOT_PW=${MYSQL_ROOT_PW:-insecure_slave} - -# This user and its password are used by the tests, if you change it, -# your tests might fail. -DB_USER=openstack_citest -DB_PW=openstack_citest - -sudo -H mysqladmin -u root password $DB_ROOT_PW - -# It's best practice to remove anonymous users from the database. If -# an anonymous user exists, then it matches first for connections and -# other connections from that host will not work. -sudo -H mysql -u root -p$DB_ROOT_PW -h localhost -e " - DELETE FROM mysql.user WHERE User=''; - FLUSH PRIVILEGES; - CREATE USER '$DB_USER'@'%' IDENTIFIED BY '$DB_PW'; - GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'%' WITH GRANT OPTION;" - -# Now create our database. -mysql -u $DB_USER -p$DB_PW -h 127.0.0.1 -e " - SET default_storage_engine=MYISAM; - DROP DATABASE IF EXISTS openstack_citest; - CREATE DATABASE openstack_citest CHARACTER SET utf8;" - -# Same for PostgreSQL -# The root password for the PostgreSQL database; pass it in via -# POSTGRES_ROOT_PW. -DB_ROOT_PW=${POSTGRES_ROOT_PW:-insecure_slave} - -# Setup user -root_roles=$(sudo -H -u postgres psql -t -c " - SELECT 'HERE' from pg_roles where rolname='$DB_USER'") -if [[ ${root_roles} == *HERE ]];then - sudo -H -u postgres psql -c "ALTER ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" -else - sudo -H -u postgres psql -c "CREATE ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" -fi - -# Store password for tests -cat << EOF > $HOME/.pgpass -*:*:*:$DB_USER:$DB_PW -EOF -chmod 0600 $HOME/.pgpass - -# Now create our database -psql -h 127.0.0.1 -U $DB_USER -d template1 -c "DROP DATABASE IF EXISTS openstack_citest" -createdb -h 127.0.0.1 -U $DB_USER -l C -T template0 -E utf8 openstack_citest diff --git a/tox.ini b/tox.ini deleted file mode 100644 index fadfc0179..000000000 --- a/tox.ini +++ /dev/null @@ -1,129 +0,0 @@ -[tox] -envlist = py38,pep8 -minversion = 2.0 -skipsdist = True - -[testenv] -usedevelop = True -setenv = - VIRTUAL_ENV={envdir} -passenv = - http_proxy - HTTP_PROXY - https_proxy - HTTPS_PROXY - no_proxy - NO_PROXY -deps = -c{env:TOX_CONSTRAINTS_FILTOX_CONSTRAINTS_FILEE:https://releases.openstack.org/constraints/upper/master} - -r{toxinidir}/requirements.txt - -r{toxinidir}/test-requirements.txt -commands = rm -f .testrepository/times.dbm - stestr run {posargs} -allowlist_externals = bash - find - rm - bandit - -[testenv:murano-test-runner] -commands = murano-test-runner {posargs} - -[testenv:pep8] -commands = - flake8 {posargs} - {[testenv:bandit]commands} - -[testenv:bandit] -commands = bandit -c bandit.yaml -r murano -x tests -n 5 -ll - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -setenv = - {[testenv]setenv} - PYTHON=coverage run --source murano --parallel-mode -commands = - stestr run {posargs} - coverage combine - coverage html -d cover - coverage xml -o cover/coverage.xml - -[testenv:debug] -commands = - find . -type f -name "*.pyc" -delete - oslo_debug_helper {posargs} - -[testenv:docs] -deps = -r{toxinidir}/doc/requirements.txt -commands = sphinx-build -a -E -W -d doc/build/doctrees -b html doc/source doc/build/html -allowlist_externals = sphinx-build - -[testenv:pdf-docs] -deps = {[testenv:docs]deps} -allowlist_externals = - make - sphinx-build -commands = - sphinx-build -W -b latex doc/source doc/build/pdf - make -C doc/build/pdf - -[testenv:pyflakes] -deps = flake8 -commands = flake8 - -[testenv:pylint] -setenv = VIRTUAL_ENV={envdir} -commands = bash tools/lintstack.sh - -[testenv:genconfig] -commands = - oslo-config-generator --config-file etc/oslo-config-generator/murano.conf - -[testenv:gencfconfig] -commands = - oslo-config-generator --config-file etc/oslo-config-generator/murano-cfapi.conf - -[testenv:genpolicy] -commands = oslopolicy-sample-generator --config-file etc/oslo-policy-generator/murano-policy-generator.conf - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html - -[testenv:api-ref] -# This environment is called from CI scripts to test and publish -# the API Ref to docs.openstack.org. -deps = -r{toxinidir}/doc/requirements.txt -commands = - rm -rf api-ref/build - sphinx-build -W -b html -d api-ref/build/doctrees api-ref/source api-ref/build/html -whitelist_externals = rm - -[flake8] -show-source = true -builtins = _ -# W605 invalid escape sequence -# E402 fires an error on eventlet import. Should probably remove condition alltogether -ignore = W605,W504,W503,E123,H405,E402 -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build - -[hacking] -import_exceptions = oslo.db.sqlalchemy.test_base, - murano.common.i18n - -[flake8:local-plugins] -extension = - M318 = checks:assert_equal_none - M322 = checks:no_mutable_default_args - M326 = checks:check_no_basestring -paths = ./murano/hacking - -[testenv:bindep] -# Do not install any requirements. We want this to be fast and work even if -# system dependencies are missing, since it's used to tell you what system -# dependencies are missing! This also means that bindep must be installed -# separately, outside of the requirements files, and develop mode disabled -# explicitly to avoid unnecessarily installing the checked-out repo too (this -# further relies on "tox.skipsdist = True" above). -deps = bindep -commands = bindep test -usedevelop = False