move everything and more to ariatosca
Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/c52f949e Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/c52f949e Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/c52f949e Branch: refs/heads/wf-wip Commit: c52f949e98e33c06de15a900dd922734585f5165 Parents: Author: Dan Kilman <dankil...@gmail.com> Authored: Thu Oct 13 12:35:17 2016 +0300 Committer: Dan Kilman <dankil...@gmail.com> Committed: Thu Oct 13 12:35:17 2016 +0300 ---------------------------------------------------------------------- .gitignore | 64 + .pylintrc | 389 +++++ .travis.yml | 14 + LICENSE | 191 +++ MANIFEST.in | 1 + README.md | 10 + TODO.md | 8 + aria/VERSION.py | 21 + aria/__init__.py | 76 + aria/cli/__init__.py | 14 + aria/cli/args_parser.py | 115 ++ aria/cli/cli.py | 80 + aria/cli/commands.py | 280 ++++ aria/cli/config.py | 37 + aria/cli/exceptions.py | 47 + aria/cli/storage.py | 70 + aria/contexts.py | 155 ++ aria/decorators.py | 85 + aria/events/__init__.py | 46 + aria/events/builtin_event_handlers.py | 44 + aria/events/workflow_engine_event_handler.py | 74 + aria/exceptions.py | 30 + aria/from_cloudify/__init__.py | 0 aria/from_cloudify/workflows/__init__.py | 20 + aria/from_cloudify/workflows/events.py | 197 +++ aria/from_cloudify/workflows/local.py | 598 +++++++ aria/from_cloudify/workflows/tasks.py | 767 +++++++++ aria/from_cloudify/workflows/tasks_graph.py | 372 +++++ aria/from_cloudify/workflows/workflow_api.py | 47 + .../from_cloudify/workflows/workflow_context.py | 1525 ++++++++++++++++++ aria/logger.py | 130 ++ aria/storage/__init__.py | 371 +++++ aria/storage/drivers.py | 407 +++++ aria/storage/models.py | 344 ++++ aria/storage/structures.py | 286 ++++ aria/tools/__init__.py | 14 + aria/tools/application.py | 279 ++++ aria/tools/lru_cache.py | 127 ++ aria/tools/plugin.py | 38 + aria/tools/process.py | 153 ++ aria/tools/validation.py | 78 + aria/workflows/__init__.py | 54 + aria/workflows/api/__init__.py | 14 + aria/workflows/api/tasks_graph.py | 203 +++ aria/workflows/builtin/__init__.py | 32 + .../builtin/deployment_modification.py | 221 +++ aria/workflows/builtin/execute_operation.py | 77 + aria/workflows/builtin/heal.py | 147 ++ aria/workflows/builtin/install.py | 42 + aria/workflows/builtin/scale.py | 416 +++++ aria/workflows/builtin/uninstall.py | 41 + aria/workflows/builtin/update.py | 21 + aria/workflows/builtin/workflows.py | 194 +++ aria/workflows/engine/__init__.py | 14 + aria/workflows/engine/engine.py | 185 +++ aria/workflows/engine/executor.py | 41 + aria/workflows/exceptions.py | 47 + ctx_api | 113 ++ requirements.txt | 6 + setup.py | 59 + tests/__init__.py | 14 + tests/events/__init__.py | 14 + tests/events/test_builtin_event_handlers.py | 58 + .../test_workflow_enginge_event_handlers.py | 0 tests/requirements.txt | 6 + tests/storage/__init__.py | 53 + tests/storage/test_drivers.py | 136 ++ tests/storage/test_field.py | 108 ++ tests/storage/test_model_storage.py | 162 ++ tests/storage/test_models.py | 289 ++++ tests/storage/test_models_api.py | 70 + tests/storage/test_resource_storage.py | 175 ++ tests/test_logger.py | 126 ++ tox.ini | 15 + 74 files changed, 10757 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29c4e9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +# Sphinx documentation +docs/_build/ + +*.iml + +*COMMIT_MSG + +*.noseids + +# QuickBuild +.qbcache/ + +.idea/ http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/.pylintrc ---------------------------------------------------------------------- diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..a7d560d --- /dev/null +++ b/.pylintrc @@ -0,0 +1,389 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +#disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating +disable=old-octal-literal,oct-method,unpacking-in-except,parameter-unpacking,backtick,old-ne-operator,long-suffix,dict-iter-method,metaclass-assignment,next-method-called,indexing-exception,long-builtin,file-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,suppressed-message,cmp-method,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,round-builtin,hex-method,nonzero-method,missing-docstring,no-self-use + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +#output-format=text +output-format=colorized + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=yes + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +#generated-members= +generated-members=objects + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +# min-similarity-lines=4 +min-similarity-lines=6 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )?<?https?://\S+>?$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,input + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct function names +# function-rgx=[a-z_][a-z0-9_]{2,30}$ +function-rgx=[a-z_][a-z0-9_]{2,40}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{1,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO,todo + + +[DESIGN] + +# Maximum number of arguments for function / method +# max-args=5 +max-args=9 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +# max-locals=15 +max-locals=16 + +# Maximum number of return / yield for function / method body +# max-returns=6 +max-returns=7 + +# Maximum number of branch for function / method body +# max-branches=12 +max-branches=13 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=10 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..381cf43 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +sudo: false +language: python +python: + - "2.7" +env: + - TOX_ENV=pylint_package + - TOX_ENV=pylint_tests + - TOX_ENV=py26 + - TOX_ENV=py27 +install: + - pip install tox==1.6.1 +script: + - tox -e $TOX_ENV + http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/LICENSE ---------------------------------------------------------------------- diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..37ec93a --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +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: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +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 +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. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/MANIFEST.in ---------------------------------------------------------------------- diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..f9bd145 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include requirements.txt http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md new file mode 100644 index 0000000..d58cc03 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +Aria +==== + + +[![Build Status](https://travis-ci.org/cloudify-cosmo/aria.svg?branch=master)](https://travis-ci.org/cloudify-cosmo/aria) +[![PyPI](http://img.shields.io/pypi/dm/aria.svg)](http://img.shields.io/pypi/dm/aria.svg) +[![PypI](http://img.shields.io/pypi/v/aria.svg)](http://img.shields.io/pypi/v/aria.svg) + + +See http://ariatosca.org/ http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/TODO.md ---------------------------------------------------------------------- diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..ab32cf7 --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +- aria/parser/extension_tools.py:66: # todo: maybe add replace action and check in add that we don't replace... +- aria/parser/framework/elements/policies.py:128: # TODO: policies should be implemented according to TOSCA as generic types +- aria/parser/framework/elements/node_templates.py:42: # TODO: Capabilities should be implemented according to TOSCA as generic types +- aria/parser/framework/functions.py:25: # TODO: ugly huck for now..., sort the imports when you have time +- aria/parser/framework/parser.py:258: # TODO: need to clean up +- tests/parser/test_parser_api.py:430: # TODO: assert node-type's default and description values once +- tests/parser/test_parser_api.py:450: # TODO: assert type's default and description values once 'type' is +- tests/parser/test_parser_api.py:472: # TODO: assert type's default and description values once 'type' is http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/VERSION.py ---------------------------------------------------------------------- diff --git a/aria/VERSION.py b/aria/VERSION.py new file mode 100644 index 0000000..7e11072 --- /dev/null +++ b/aria/VERSION.py @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Aria Version module: + * version: Aria Package version +""" + +version = '0.1.0' # pylint: disable=C0103 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/__init__.py ---------------------------------------------------------------------- diff --git a/aria/__init__.py b/aria/__init__.py new file mode 100644 index 0000000..32c0df3 --- /dev/null +++ b/aria/__init__.py @@ -0,0 +1,76 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +aria Package +Path: aria + +Methods: + * __version__ - Aria Package Version. + * tosca_templates - Use the parser.parse method to validate tosca_templates. + * parser - aria.parser sub-package TOSCA parser and validation. + * deployment - aria.deployment sub-package convert plan, + from parser to a deployment plan. +""" + +from .VERSION import version as __version__ +from .storage.drivers import ResourceDriver, ModelDriver, FileSystemModelDriver, FileSystemResourceDriver +from .storage import ModelStorage, ResourceStorage, models +from .decorators import workflow, operation + +__all__ = ( + '__version__', + 'workflow', + 'operation', +) + +_model_storage = {} +_resource_storage = {} + + +def application_model_storage(driver): + assert isinstance(driver, ModelDriver) + global _model_storage + if driver not in _model_storage: + _model_storage[driver] = ModelStorage( + driver, models=[ + models.Node, + models.NodeInstance, + models.Plugin, + models.Blueprint, + models.Snapshot, + models.Deployment, + models.DeploymentUpdate, + models.DeploymentModification, + models.Execution, + models.ProviderContext, + models.Operation, + ]) + return _model_storage[driver] + + +def application_resource_storage(driver): + assert isinstance(driver, ResourceDriver) + global _resource_storage + if driver not in _resource_storage: + _resource_storage[driver] = ResourceStorage( + driver, + resources=[ + 'blueprint', + 'deployment', + 'plugin', + 'snapshot', + ]) + return _resource_storage[driver] http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/__init__.py ---------------------------------------------------------------------- diff --git a/aria/cli/__init__.py b/aria/cli/__init__.py new file mode 100644 index 0000000..ae1e83e --- /dev/null +++ b/aria/cli/__init__.py @@ -0,0 +1,14 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/args_parser.py ---------------------------------------------------------------------- diff --git a/aria/cli/args_parser.py b/aria/cli/args_parser.py new file mode 100644 index 0000000..30d213f --- /dev/null +++ b/aria/cli/args_parser.py @@ -0,0 +1,115 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +from functools import partial + +NO_VERBOSE = 0 + + +class SmartFormatter(argparse.HelpFormatter): + def _split_lines(self, text, width): + if text.startswith('R|'): + return text[2:].splitlines() + return super(SmartFormatter, self)._split_lines(text, width) + + +def sub_parser_decorator(func=None, **parser_settings): + if not func: + return partial(sub_parser_decorator, **parser_settings) + + def wrapper(parser): + sub_parser = parser.add_parser(**parser_settings) + sub_parser.add_argument( + '-v', '--verbose', + dest='verbosity', + action='count', + default=NO_VERBOSE, + help='Set verbosity level (can be passed multiple times)') + sub_parser.add_argument( + '-d', '--deployment-id', + required=True, + help='A unique ID for the deployment') + func(sub_parser) + return sub_parser + return wrapper + + +def config_parser(parser=None): + parser = parser or argparse.ArgumentParser( + prog='Aria', + description="Aria's Command Line Interface", + formatter_class=SmartFormatter) + parser.add_argument('-v', '--version', action='version') + sub_parser = parser.add_subparsers(title='Commands', dest='command') + add_init_parser(sub_parser) + add_execute_parser(sub_parser) + return parser + + +@sub_parser_decorator( + name='init', + help='Initialize environment', + formatter_class=SmartFormatter) +def add_init_parser(init): + init.add_argument( + '-p', '--blueprint-path', + dest='blueprint_path', + required=True, + help='The path to the desired blueprint') + init.add_argument( + '-i', '--inputs', + dest='input', + action='append', + help='R|Inputs for the local workflow creation \n' + '(Can be provided as wildcard based paths (*.yaml, etc..) to YAML files, \n' + 'a JSON string or as "key1=value1;key2=value2"). \n' + 'This argument can be used multiple times') + init.add_argument( + '-b', '--blueprint-id', + dest='blueprint_id', + required=True, + help='The blueprint ID' + ) + + +@sub_parser_decorator( + name='execute', + help='Execute a workflow', + formatter_class=SmartFormatter) +def add_execute_parser(execute): + execute.add_argument( + '-w', '--workflow', + dest='workflow_id', + help='The workflow to execute') + execute.add_argument( + '-p', '--parameters', + dest='parameters', + action='append', + help='R|Parameters for the workflow execution\n' + '(Can be provided as wildcard based paths (*.yaml, etc..) to YAML files,\n' + 'a JSON string or as "key1=value1;key2=value2").\n' + 'This argument can be used multiple times.') + execute.add_argument( + '--task-retries', + dest='task_retries', + type=int, + help='How many times should a task be retried in case of failure') + execute.add_argument( + '--task-retry-interval', + dest='task_retry_interval', + default=1, + type=int, + help='How many seconds to wait before each task is retried') http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/cli.py ---------------------------------------------------------------------- diff --git a/aria/cli/cli.py b/aria/cli/cli.py new file mode 100644 index 0000000..24573c9 --- /dev/null +++ b/aria/cli/cli.py @@ -0,0 +1,80 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from aria.logger import ( + create_logger, + create_console_log_handler, + create_file_log_handler, + LoggerMixin, +) +from .args_parser import config_parser +from .commands import ( + InitCommand, + ExecuteCommand, +) + +__version__ = '0.1.0' + + +class AriaCli(LoggerMixin): + + def __init__(self, *args, **kwargs): + super(AriaCli, self).__init__(*args, **kwargs) + self.commands = { + 'init': InitCommand.with_logger(base_logger=self.logger), + 'execute': ExecuteCommand.with_logger(base_logger=self.logger), + } + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """ + Here we will handle errors + :param exc_type: + :param exc_val: + :param exc_tb: + :return: + """ + # todo: error handling + # todo: cleanup if needed + # TODO: user message if needed + pass + + def run(self): + parser = config_parser() + args = parser.parse_args() + + command_handler = self.commands[args.command] + self.logger.debug('Running command: {args.command} handler: {0}'.format( + command_handler, args=args)) + command_handler(args) + + +def main(): + create_logger( + handlers=[ + create_console_log_handler(), + create_file_log_handler(file_path='/tmp/aria_cli.log'), + ], + level=logging.INFO) + with AriaCli() as aria: + aria.run() + + +if __name__ == '__main__': + main() http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/commands.py ---------------------------------------------------------------------- diff --git a/aria/cli/commands.py b/aria/cli/commands.py new file mode 100644 index 0000000..465569f --- /dev/null +++ b/aria/cli/commands.py @@ -0,0 +1,280 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import os +import sys +from glob import glob +from importlib import import_module + +from yaml import safe_load, YAMLError + +from aria import application_model_storage, application_resource_storage +from aria.logger import LoggerMixin +from aria.storage import FileSystemModelDriver, FileSystemResourceDriver +from aria.tools.application import StorageManager +from aria.contexts import WorkflowContext +from aria.workflows.engine.engine import Engine +from aria.workflows.engine.executor import LocalExecutor + +from .storage import ( + local_resource_storage, + create_local_storage, + local_model_storage, + create_user_space, + user_space, + local_storage, +) + +from .exceptions import ( + AriaCliFormatInputsError, + AriaCliYAMLInputsError, + AriaCliInvalidInputsError +) + +####################################### +from dsl_parser.parser import parse_from_path +from dsl_parser.tasks import prepare_deployment_plan +####################################### + + +class BaseCommand(LoggerMixin): + def __repr__(self): + return 'AriaCli({cls.__name__})'.format(cls=self.__class__) + + def __call__(self, args_namespace): + """ + __call__ method is called when running command + :param args_namespace: + """ + pass + + def parse_inputs(self, inputs): + """ + Returns a dictionary of inputs `resources` can be: + - A list of files. + - A single file + - A directory containing multiple input files + - A key1=value1;key2=value2 pairs string. + - Wildcard based string (e.g. *-inputs.yaml) + """ + + parsed_dict = {} + + def format_to_dict(input_string): + self.logger.info('Processing inputs source: {0}'.format(input_string)) + try: + input_string = input_string.strip() + try: + parsed_dict.update(json.loads(input_string)) + except: + parsed_dict.update((input.split('=') + for input in input_string.split(';') + if input)) + except Exception as exc: + raise AriaCliFormatInputsError(str(exc), inputs=input_string) + + def handle_inputs_source(input_path): + self.logger.info('Processing inputs source: {0}'.format(input_path)) + try: + with open(input_path) as input_file: + content = safe_load(input_file) + except YAMLError as exc: + raise AriaCliYAMLInputsError( + '"{0}" is not a valid YAML. {1}'.format(input_path, str(exc))) + if isinstance(content, dict): + parsed_dict.update(content) + return + if content is None: + return + raise AriaCliInvalidInputsError('Invalid inputs', inputs=input_path) + + for input_string in inputs if isinstance(inputs, list) else [inputs]: + if os.path.isdir(input_string): + for input_file in os.listdir(input_string): + handle_inputs_source(os.path.join(input_string, input_file)) + continue + input_files = glob(input_string) + if input_files: + for input_file in input_files: + handle_inputs_source(input_file) + continue + format_to_dict(input_string) + return parsed_dict + + +class InitCommand(BaseCommand): + _IN_VIRTUAL_ENV = hasattr(sys, 'real_prefix') + + def __call__(self, args_namespace): + super(InitCommand, self).__call__(args_namespace) + self.workspace_setup() + inputs = self.parse_inputs(args_namespace.input) if args_namespace.input else None + plan, deployment_plan = self.parse_blueprint(args_namespace.blueprint_path, inputs) + self.create_storage( + blueprint_plan=plan, + blueprint_path=args_namespace.blueprint_path, + deployment_plan=deployment_plan, + blueprint_id=args_namespace.blueprint_id, + deployment_id=args_namespace.deployment_id, + main_file_name=os.path.basename(args_namespace.blueprint_path)) + self.logger.info('Initiated {0}'.format(args_namespace.blueprint_path)) + self.logger.info( + 'If you make changes to the blueprint, ' + 'run `aria local init` command again to apply them'.format( + args_namespace.blueprint_path)) + + def workspace_setup(self): + try: + create_user_space() + self.logger.debug( + 'created user space path in: {0}'.format(user_space())) + except IOError: + self.logger.debug( + 'user space path already exist - {0}'.format(user_space())) + try: + create_local_storage() + self.logger.debug( + 'created local storage path in: {0}'.format(local_storage())) + except IOError: + self.logger.debug( + 'local storage path already exist - {0}'.format(local_storage())) + return local_storage() + + def parse_blueprint(self, blueprint_path, inputs=None): + plan = parse_from_path(blueprint_path) + self.logger.info('blueprint parsed successfully') + deployment_plan = prepare_deployment_plan(plan=plan.copy(), inputs=inputs) + return plan, deployment_plan + + def create_storage( + self, + blueprint_path, + blueprint_plan, + deployment_plan, + blueprint_id, + deployment_id, + main_file_name=None): + resource_storage = application_resource_storage( + FileSystemResourceDriver(local_resource_storage())) + model_storage = application_model_storage( + FileSystemModelDriver(local_model_storage())) + resource_storage.setup() + model_storage.setup() + storage_manager = StorageManager( + model_storage=model_storage, + resource_storage=resource_storage, + blueprint_path=blueprint_path, + blueprint_id=blueprint_id, + blueprint_plan=blueprint_plan, + deployment_id=deployment_id, + deployment_plan=deployment_plan + ) + storage_manager.create_blueprint_storage( + blueprint_path, + main_file_name=main_file_name + ) + storage_manager.create_nodes_storage() + storage_manager.create_deployment_storage() + storage_manager.create_node_instances_storage() + + +class ExecuteCommand(BaseCommand): + def __call__(self, args_namespace): + super(ExecuteCommand, self).__call__(args_namespace) + parameters = (self.parse_inputs(args_namespace.parameters) + if args_namespace.parameters else {}) + resource_storage = application_resource_storage( + FileSystemResourceDriver(local_resource_storage())) + model_storage = application_model_storage( + FileSystemModelDriver(local_model_storage())) + deployment = model_storage.deployment.get(args_namespace.deployment_id) + + try: + workflow = deployment.workflows[args_namespace.workflow_id] + except KeyError: + raise ValueError( + '{0} workflow does not exist. existing workflows are: {1}'.format( + args_namespace.workflow_id, + deployment.workflows.keys())) + + workflow_parameters = self._merge_and_validate_execution_parameters( + workflow, + args_namespace.workflow_id, + parameters + ) + workflow_context = WorkflowContext( + name=args_namespace.workflow_id, + model_storage=model_storage, + resource_storage=resource_storage, + deployment_id=args_namespace.deployment_id, + workflow_id=args_namespace.workflow_id, + parameters=workflow_parameters, + ) + workflow_function = self._load_workflow_handler(workflow['operation']) + tasks_graph = workflow_function(workflow_context, **workflow_context.parameters) + workflow_engine = Engine(executor=LocalExecutor(), + workflow_context=workflow_context, + tasks_graph=tasks_graph) + workflow_engine.execute() + + def _merge_and_validate_execution_parameters( + self, + workflow, + workflow_name, + execution_parameters): + merged_parameters = {} + workflow_parameters = workflow.get('parameters', {}) + missing_mandatory_parameters = set() + + for name, param in workflow_parameters.iteritems(): + if 'default' not in param: + if name not in execution_parameters: + missing_mandatory_parameters.add(name) + continue + merged_parameters[name] = execution_parameters[name] + continue + merged_parameters[name] = (execution_parameters[name] if name in execution_parameters + else param['default']) + + if missing_mandatory_parameters: + raise ValueError( + 'Workflow "{0}" must be provided with the following ' + 'parameters to execute: {1}'.format( + workflow_name, ','.join(missing_mandatory_parameters))) + + custom_parameters = dict( + (k, v) for (k, v) in execution_parameters.iteritems() + if k not in workflow_parameters) + + if custom_parameters: + raise ValueError( + 'Workflow "{0}" does not have the following parameters declared: {1}. ' + 'Remove these parameters'.format( + workflow_name, ','.join(custom_parameters.keys()))) + + return merged_parameters + + def _load_workflow_handler(self, handler_path): + module_name, spec_handler_name = handler_path.rsplit('.', 1) + try: + module = import_module(module_name) + return getattr(module, spec_handler_name) + except ImportError: + # todo: exception handler + raise + except AttributeError: + # todo: exception handler + raise http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/config.py ---------------------------------------------------------------------- diff --git a/aria/cli/config.py b/aria/cli/config.py new file mode 100644 index 0000000..a230bc0 --- /dev/null +++ b/aria/cli/config.py @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from getpass import getuser +from tempfile import gettempdir +from yaml import safe_load +import logging + +from .storage import config_file_path + +# path to a file where cli logs will be saved. +logging_filename = os.path.join(gettempdir(), 'aria_cli_{0}.log'.format(getuser())) +# loggers log level to show +logger_level = logging.INFO +# loggers log level to show +colors = True + +import_resolver = None + + +def load_configurations(): + config_path = config_file_path() + with open(config_path) as config_file: + globals().update(safe_load(config_file) or {}) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/exceptions.py ---------------------------------------------------------------------- diff --git a/aria/cli/exceptions.py b/aria/cli/exceptions.py new file mode 100644 index 0000000..fb120be --- /dev/null +++ b/aria/cli/exceptions.py @@ -0,0 +1,47 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class AriaCliError(Exception): + pass + + +class AriaCliFormatInputsError(AriaCliError): + def __init__(self, message, inputs): + self.inputs = inputs + super(AriaCliFormatInputsError, self).__init__(message) + + def user_message(self): + return ( + 'Invalid input format: {0}, ' + 'the expected format is: ' + 'key1=value1;key2=value2'.format(self.inputs)) + + +class AriaCliYAMLInputsError(AriaCliError): + pass + + +class AriaCliInvalidInputsError(AriaCliFormatInputsError): + def user_message(self): + return ( + 'Invalid input: {0}. input must represent a dictionary.\n' + 'Valid values can be one of:\n' + '- a path to a YAML file\n' + '- a path to a directory containing YAML files\n' + '- a single quoted wildcard based path (e.g. "*-inputs.yaml")\n' + '- a string formatted as JSON\n' + '- a string formatted as key1=value1;key2=value2'.format(self.inputs) + ) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/cli/storage.py ---------------------------------------------------------------------- diff --git a/aria/cli/storage.py b/aria/cli/storage.py new file mode 100644 index 0000000..05ed395 --- /dev/null +++ b/aria/cli/storage.py @@ -0,0 +1,70 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import getpass +from shutil import rmtree + +work_space_directory = '.aria' +storage_directory_name = 'local-storage' + + +def user_space(user_name=getpass.getuser()): + user_path = '~{0}'.format(user_name) + real_path = os.path.expanduser(user_path) + if os.path.exists(real_path): + return os.path.join(real_path, work_space_directory) + return os.path.join(os.getcwd(), work_space_directory) + + +def local_storage(user_name=getpass.getuser()): + return os.path.join(user_space(user_name), storage_directory_name) + + +def local_model_storage(): + return os.path.join(local_storage(), 'models') + + +def local_resource_storage(): + return os.path.join(local_storage(), 'resources') + + +def config_file_path(): + path = os.path.join(user_space(), 'config.yaml') + if not os.path.exists(path): + open(path, 'w').close() + return path + + +def create_user_space(user_name=getpass.getuser(), override=False): + path = user_space(user_name) + if os.path.exists(path): + if override: + rmtree(path, ignore_errors=True) + else: + raise IOError('user space {0} already exists'.format(path)) + os.mkdir(path) + return path + + +def create_local_storage(user_name=getpass.getuser(), override=False): + path = local_storage(user_name) + if os.path.exists(path): + if override: + rmtree(path, ignore_errors=True) + else: + raise IOError('local storage {0} already exists'.format(path)) + os.mkdir(path) + return path http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/contexts.py ---------------------------------------------------------------------- diff --git a/aria/contexts.py b/aria/contexts.py new file mode 100644 index 0000000..11d33ac --- /dev/null +++ b/aria/contexts.py @@ -0,0 +1,155 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from uuid import uuid4 + +from aria.logger import LoggerMixin +from aria.tools.lru_cache import lru_cache +from aria.workflows.api.tasks_graph import TaskGraph + + +class WorkflowContext(LoggerMixin): + # todo: add documentations + + def __init__( + self, + name, + model_storage, + resource_storage, + deployment_id, + workflow_id, + parameters=None, + **kwargs): + super(WorkflowContext, self).__init__(**kwargs) + self.name = name + self.id = str(uuid4()) + self.model = model_storage + self.resource = resource_storage + self.deployment_id = deployment_id + self.workflow_id = workflow_id + self.execution_id = str(uuid4()) + self.parameters = parameters or {} + + def __repr__(self): + return ( + '{name}(deployment_id={self.deployment_id}, ' + 'workflow_id={self.workflow_id}, ' + 'execution_id={self.execution_id})'.format( + name=self.__class__.__name__, self=self)) + + def operation( + self, + name, + operation_details, + node_instance, + inputs=None): + return OperationContext( + name=name, + operation_details=operation_details, + workflow_context=self, + node_instance=node_instance, + inputs=inputs or {}) + + @property + def task_graph(self): + return TaskGraph + + @property + def blueprint_id(self): + return self.deployment.blueprint_id + + @property + @lru_cache() + def blueprint(self): + return self.model.blueprint.get(self.blueprint_id) + + @property + @lru_cache() + def deployment(self): + return self.model.deployment.get(self.deployment_id) + + @property + def nodes(self): + return self.model.node.iter( + filters={'blueprint_id': self.blueprint_id}) + + @property + def node_instances(self): + return self.model.node_instance.iter(filters={'deployment_id': self.deployment_id}) + + @property + def execution(self): + return self.model.execution.get(self.execution_id) + + @execution.setter + def execution(self, value): + self.model.execution.store(value) + + def download_blueprint_resource(self, destination, path=None): + return self.resource.blueprint.download( + entry_id=self.blueprint_id, + destination=destination, + path=path) + + def download_deployment_resource(self, destination, path=None): + return self.resource.deployment.download( + entry_id=self.deployment_id, + destination=destination, + path=path) + + @lru_cache() + def get_deployment_resource_data(self, path=None): + return self.resource.deployment.data(entry_id=self.deployment_id, path=path) + + @lru_cache() + def get_blueprint_resource_data(self, path=None): + return self.resource.blueprint.data(entry_id=self.blueprint_id, path=path) + + +class OperationContext(LoggerMixin): + def __init__( + self, + name, + operation_details, + workflow_context, + node_instance, + inputs=None): + super(OperationContext, self).__init__() + self.name = name + self.id = str(uuid4()) + self.operation_details = operation_details + self.workflow_context = workflow_context + self.node_instance = node_instance + self.inputs = inputs or {} + + def __repr__(self): + details = ', '.join( + '{0}={1}'.format(key, value) + for key, value in self.operation_details.items()) + return '{name}({0})'.format(details, name=self.name) + + def __getattr__(self, attr): + try: + return getattr(self.workflow_context, attr) + except AttributeError: + return super(OperationContext, self).__getattribute__(attr) + + @property + def operation(self): + return self.storage.operation.get(self.id) + + @operation.setter + def operation(self, value): + self.storage.operation.store(value) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/decorators.py ---------------------------------------------------------------------- diff --git a/aria/decorators.py b/aria/decorators.py new file mode 100644 index 0000000..e2ea4e5 --- /dev/null +++ b/aria/decorators.py @@ -0,0 +1,85 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from uuid import uuid4 +from functools import partial, wraps + +from aria.tools.validation import validate_function_arguments + + +def workflow( + func=None, + workflow_context=True, + simple_workflow=True, + suffix_template=''): + if func is None: + return partial( + workflow, + workflow_context=workflow_context, + simple_workflow=simple_workflow, + suffix_template=suffix_template) + + @wraps(func) + def wrapper(context, **custom_kwargs): + workflow_name = _generate_workflow_name( + func_name=func.__name__, + suffix_template=suffix_template, + context=context, + **custom_kwargs) + func_kwargs = _create_func_kwargs( + custom_kwargs, + context, + add_context=workflow_context, + workflow_name=workflow_name) + validate_function_arguments(func, func_kwargs) + func(**func_kwargs) + return func_kwargs['graph'] + return wrapper + + +def operation( + func=None, + operation_context=True): + if func is None: + return partial(operation) + + @wraps(func) + def wrapper(context, **custom_kwargs): + func_kwargs = _create_func_kwargs( + custom_kwargs, + context, + add_context=operation_context) + validate_function_arguments(func, func_kwargs) + context.description = func.__doc__ + return func(**func_kwargs) + return wrapper + + +def _generate_workflow_name(func_name, context, suffix_template, **custom_kwargs): + return '{func_name}.{suffix}'.format( + func_name=func_name, + context=context, + suffix=suffix_template.format(context=context, **custom_kwargs) or str(uuid4())) + + +def _create_func_kwargs( + kwargs, + context, + add_context=True, + workflow_name=None): + if add_context: + kwargs['context'] = context + kwargs.setdefault('graph', context.task_graph(workflow_name)) + return kwargs http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/events/__init__.py ---------------------------------------------------------------------- diff --git a/aria/events/__init__.py b/aria/events/__init__.py new file mode 100644 index 0000000..70e7e03 --- /dev/null +++ b/aria/events/__init__.py @@ -0,0 +1,46 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +from blinker import signal + +from ..tools.plugin import plugin_installer + + +# workflow engine default signals: +start_task_signal = signal('start_task_signal') +end_task_signal = signal('end_task_signal') +on_success_task_signal = signal('success_task_signal') +on_failure_task_signal = signal('failure_task_signal') + +# workflow engine workflow signals: +start_workflow_signal = signal('start_workflow_signal') +end_workflow_signal = signal('end_workflow_signal') +on_success_workflow_signal = signal('on_success_workflow_signal') +on_failure_workflow_signal = signal('on_failure_workflow_signal') +start_sub_workflow_signal = signal('start_sub_workflow_signal') +end_sub_workflow_signal = signal('end_sub_workflow_signal') + +# workflow engine operation signals: +start_operation_signal = signal('start_operation_signal') +end_operation_signal = signal('end_operation_signal') +on_success_operation_signal = signal('on_success_operation_signal') +on_failure_operation_signal = signal('on_failure_operation_signal') + +plugin_installer( + path=os.path.dirname(os.path.realpath(__file__)), + plugin_suffix='_event_handler', + package=__package__) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/events/builtin_event_handlers.py ---------------------------------------------------------------------- diff --git a/aria/events/builtin_event_handlers.py b/aria/events/builtin_event_handlers.py new file mode 100644 index 0000000..59f59c1 --- /dev/null +++ b/aria/events/builtin_event_handlers.py @@ -0,0 +1,44 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ..storage.models import NodeInstance +from . import start_operation_signal + + +class _OperationToNodeInstanceState(dict): + def __missing__(self, key): + for cached_key, value in self.items(): + if key.startswith(cached_key): + return value + raise KeyError(key) + +_operation_to_node_instance_state = _OperationToNodeInstanceState({ + 'cloudify.interfaces.lifecycle.create': NodeInstance.INITIALIZING, + 'cloudify.interfaces.lifecycle.configure': NodeInstance.CONFIGURING, + 'cloudify.interfaces.lifecycle.start': NodeInstance.STARTING, + 'cloudify.interfaces.lifecycle.stop': NodeInstance.STOPPING, + 'cloudify.interfaces.lifecycle.delete': NodeInstance.DELETING +}) + + +@start_operation_signal.connect +def _update_node_instance_state(sender, **kwargs): + try: + next_state = _operation_to_node_instance_state[sender.task_name] + except KeyError: + return + node_instance = sender.context.node_instance + node_instance.state = next_state + sender.context.storage.node_instance.store(node_instance) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/events/workflow_engine_event_handler.py ---------------------------------------------------------------------- diff --git a/aria/events/workflow_engine_event_handler.py b/aria/events/workflow_engine_event_handler.py new file mode 100644 index 0000000..59bed99 --- /dev/null +++ b/aria/events/workflow_engine_event_handler.py @@ -0,0 +1,74 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from . import ( + start_operation_signal, + end_operation_signal, + on_success_operation_signal, + on_failure_operation_signal, + start_workflow_signal, + end_workflow_signal, + start_sub_workflow_signal, + end_sub_workflow_signal, +) + + +@start_operation_signal.connect +def start_operation_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - starting operation: {sender.task_name}'.format(sender=sender)) + + +@end_operation_signal.connect +def end_operation_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - finished operation: {sender.task_name}'.format(sender=sender)) + + +@on_success_operation_signal.connect +def success_operation_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - operation success: {sender.task_name}'.format(sender=sender)) + + +@on_failure_operation_signal.connect +def failure_operation_handler(sender, **kwargs): + sender.context.logger.error( + 'Event - operation failure: {sender.task_name}'.format(sender=sender), + exc_info=kwargs.get('exception', True)) + + +@start_workflow_signal.connect +def start_workflow_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - starting workflow: {sender.task_name}'.format(sender=sender)) + + +@end_workflow_signal.connect +def end_workflow_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - finished workflow: {sender.task_name}'.format(sender=sender)) + + +@start_sub_workflow_signal.connect +def start_sub_workflow_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - starting sub workflow: {sender.task_name}'.format(sender=sender)) + + +@end_sub_workflow_signal.connect +def end_sub_workflow_handler(sender, **kwargs): + sender.context.logger.debug( + 'Event - finished sub workflow: {sender.task_name}'.format(sender=sender)) http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/exceptions.py ---------------------------------------------------------------------- diff --git a/aria/exceptions.py b/aria/exceptions.py new file mode 100644 index 0000000..d035b92 --- /dev/null +++ b/aria/exceptions.py @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Aria exceptions module. + Every sub-package in Aria have a module with his Exceptions. + aria.exceptions module is a center for all exceptions. +""" + +from .workflows.exceptions import * # pylint: disable=W0401, W0614 + + +class AriaError(Exception): + pass + + +class StorageError(AriaError): + pass http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/from_cloudify/__init__.py ---------------------------------------------------------------------- diff --git a/aria/from_cloudify/__init__.py b/aria/from_cloudify/__init__.py new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/c52f949e/aria/from_cloudify/workflows/__init__.py ---------------------------------------------------------------------- diff --git a/aria/from_cloudify/workflows/__init__.py b/aria/from_cloudify/workflows/__init__.py new file mode 100644 index 0000000..8a0a917 --- /dev/null +++ b/aria/from_cloudify/workflows/__init__.py @@ -0,0 +1,20 @@ +######## +# Copyright (c) 2014 GigaSpaces Technologies Ltd. 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. + +__author__ = 'dank' + +from cloudify.workflows import workflow_api as api # noqa +from cloudify.state import workflow_ctx as ctx # noqa +from cloudify.state import workflow_parameters as parameters # noqa