diff -Nru python-saharaclient-1.6.0/AUTHORS python-saharaclient-2.0.0/AUTHORS --- python-saharaclient-1.6.0/AUTHORS 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/AUTHORS 2018-07-26 22:37:14.000000000 +0000 @@ -7,6 +7,8 @@ Andrew Lazarev Andrey Pavlov Chad Roberts +Charles Short +Chen Christian Berendt Cyril Roelandt Davanum Srinivas @@ -61,6 +63,7 @@ Telles Nobrega Tetiana Lashchova Thomas Bechtold +Thomas Goirand Tony Breeds Trevor McKay Vitaly Gridnev @@ -77,11 +80,13 @@ kavithahr llg8212 luhuichun +malei ricolin ricolin shu-mutou ting.wang venkatamahesh +wu.chunyang yankee yuhara.motoki zemuvier diff -Nru python-saharaclient-1.6.0/ChangeLog python-saharaclient-2.0.0/ChangeLog --- python-saharaclient-1.6.0/ChangeLog 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/ChangeLog 2018-07-26 22:37:14.000000000 +0000 @@ -1,6 +1,29 @@ CHANGES ======= +2.0.0 +----- + +* Clean S3 data source create/update +* Support of S3 data sources in OSC +* Adding boot from volume to osc +* Adding boot from volume +* Correct a missed job\_execution->job +* Fix the cover tox target (switch to stestr) +* Revert "Fix the cover tox target (switch to stestr)" +* Allow S3 credentials in data source create +* Rework saharaclient authentication +* Reflect response fixes for jobs/jobs templates +* Fix the cover tox target (switch to stestr) +* Switch to using stestr +* Add release note link in README +* Reflect change to multiple clusters creation +* Support of the improved force-delete in client +* Remove PyPI downloads +* fix tox python3 overrides +* Fix build with Sphinx 1.7.x +* Trivial: Update pypi url to new url + 1.6.0 ----- diff -Nru python-saharaclient-1.6.0/debian/changelog python-saharaclient-2.0.0/debian/changelog --- python-saharaclient-1.6.0/debian/changelog 2018-06-12 18:52:29.000000000 +0000 +++ python-saharaclient-2.0.0/debian/changelog 2018-08-03 15:33:16.000000000 +0000 @@ -1,3 +1,11 @@ +python-saharaclient (2.0.0-0ubuntu1) cosmic; urgency=medium + + * New upstream release for OpenStack Rocky. + * d/control: Align (Build-)Depends with upstream. + * d/p/fix-sphinx-Directive-import.patch: Dropped. Fixed in upstream release. + + -- Corey Bryant Fri, 03 Aug 2018 11:33:16 -0400 + python-saharaclient (1.6.0-0ubuntu1) cosmic; urgency=low * Merge from Debian unstable. Remaining changes: diff -Nru python-saharaclient-1.6.0/debian/control python-saharaclient-2.0.0/debian/control --- python-saharaclient-1.6.0/debian/control 2018-06-12 18:52:29.000000000 +0000 +++ python-saharaclient-2.0.0/debian/control 2018-08-03 15:33:16.000000000 +0000 @@ -26,7 +26,7 @@ python-openstackclient (>= 3.12.0), python-openstackdocstheme (>= 1.18.1) , python-os-testr (>= 1.0.0), - python-osc-lib (>= 1.8.0), + python-osc-lib (>= 1.11.0), python-oslo.i18n (>= 3.15.3), python-oslo.log (>= 3.36.0), python-oslo.serialization (>= 2.18.0), @@ -36,6 +36,7 @@ python-requests (>= 2.14.2), python-requests-mock (>= 1.2.0), python-six (>= 1.10.0), + python-stestr (>= 1.0.0), python-testrepository (>= 0.0.18), python3-babel (>= 2.3.4), python3-coverage (>= 4.0), @@ -45,12 +46,13 @@ python3-openstackclient (>= 3.12.0), python3-openstackdocstheme (>= 1.18.1) , python3-os-testr (>= 1.0.0), - python3-osc-lib (>= 1.8.0), + python3-osc-lib (>= 1.11.0), python3-oslo.i18n (>= 3.15.3), python3-oslo.log (>= 3.36.0), python3-oslo.serialization (>= 2.18.0), python3-oslo.utils (>= 3.33.0), python3-oslotest (>= 1:3.2.0), + python3-stestr (>= 1.0.0), python3-pep8, python3-requests (>= 2.14.2), python3-requests-mock (>= 1.2.0), @@ -70,7 +72,7 @@ python-babel (>= 2.3.4), python-keystoneauth1 (>= 3.4.0), python-openstackclient (>= 3.12.0), - python-osc-lib (>= 1.8.0), + python-osc-lib (>= 1.11.0), python-oslo.i18n (>= 3.15.3), python-oslo.log (>= 3.36.0), python-oslo.serialization (>= 2.18.0), @@ -113,7 +115,7 @@ python3-babel (>= 2.3.4), python3-keystoneauth1 (>= 3.4.0), python3-openstackclient (>= 3.12.0), - python3-osc-lib (>= 1.8.0), + python3-osc-lib (>= 1.11.0), python3-oslo.i18n (>= 3.15.3), python3-oslo.log (>= 3.36.0), python3-oslo.serialization (>= 2.18.0), diff -Nru python-saharaclient-1.6.0/debian/patches/fix-sphinx-Directive-import.patch python-saharaclient-2.0.0/debian/patches/fix-sphinx-Directive-import.patch --- python-saharaclient-1.6.0/debian/patches/fix-sphinx-Directive-import.patch 2018-06-12 18:52:29.000000000 +0000 +++ python-saharaclient-2.0.0/debian/patches/fix-sphinx-Directive-import.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Description: Fixed sphinx Directive import -Author: Thomas Goirand -Forwarded: https://review.openstack.org/564136 -Bug-Debian: https://bugs.debian.org/896634 -Last-Update: 2018-04-25 - ---- python-saharaclient-1.5.0.orig/doc/ext/ext.py -+++ python-saharaclient-1.5.0/doc/ext/ext.py -@@ -19,7 +19,10 @@ import os - from docutils import nodes - from docutils.statemachine import StringList - from docutils.parsers.rst.directives import flag, unchanged --from sphinx.util.compat import Directive -+try: -+ from sphinx.util.compat import Directive -+except ImportError: -+ from docutils.parsers.rst import Directive - from sphinx.util.nodes import nested_parse_with_titles - - from .parser import parse_parser, parser_navigate diff -Nru python-saharaclient-1.6.0/debian/patches/series python-saharaclient-2.0.0/debian/patches/series --- python-saharaclient-1.6.0/debian/patches/series 2018-06-12 18:52:29.000000000 +0000 +++ python-saharaclient-2.0.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -fix-sphinx-Directive-import.patch diff -Nru python-saharaclient-1.6.0/doc/ext/ext.py python-saharaclient-2.0.0/doc/ext/ext.py --- python-saharaclient-1.6.0/doc/ext/ext.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/doc/ext/ext.py 2018-07-26 22:34:12.000000000 +0000 @@ -19,7 +19,7 @@ from docutils import nodes from docutils.statemachine import StringList from docutils.parsers.rst.directives import flag, unchanged -from sphinx.util.compat import Directive +from docutils.parsers.rst import Directive from sphinx.util.nodes import nested_parse_with_titles from .parser import parse_parser, parser_navigate diff -Nru python-saharaclient-1.6.0/lower-constraints.txt python-saharaclient-2.0.0/lower-constraints.txt --- python-saharaclient-1.6.0/lower-constraints.txt 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/lower-constraints.txt 2018-07-26 22:34:12.000000000 +0000 @@ -34,8 +34,7 @@ openstacksdk==0.11.2 os-client-config==1.28.0 os-service-types==1.2.0 -os-testr==1.0.0 -osc-lib==1.8.0 +osc-lib==1.11.0 oslo.config==5.2.0 oslo.context==2.19.2 oslo.i18n==3.15.3 @@ -64,14 +63,13 @@ pytz==2013.6 PyYAML==3.12 requests==2.14.2 -requests-mock==1.1.0 +requests-mock==1.2.0 requestsexceptions==1.2.0 rfc3986==0.3.1 simplejson==3.5.1 six==1.10.0 stestr==1.0.0 stevedore==1.20.0 -testrepository==0.0.18 testtools==2.2.0 traceback2==1.4.0 unittest2==1.1.0 diff -Nru python-saharaclient-1.6.0/PKG-INFO python-saharaclient-2.0.0/PKG-INFO --- python-saharaclient-1.6.0/PKG-INFO 2018-04-19 22:21:51.000000000 +0000 +++ python-saharaclient-2.0.0/PKG-INFO 2018-07-26 22:37:15.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-saharaclient -Version: 1.6.0 +Version: 2.0.0 Summary: Client library for Sahara API Home-page: https://docs.openstack.org/python-saharaclient/latest/ Author: OpenStack @@ -19,13 +19,9 @@ =========================================== .. image:: https://img.shields.io/pypi/v/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ + :target: https://pypi.org/project/python-saharaclient/ :alt: Latest Version - .. image:: https://img.shields.io/pypi/dm/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ - :alt: Downloads - This is a client for the OpenStack Sahara API. There's a Python API (the ``saharaclient`` module), and a command-line script (``sahara``). Each implements the OpenStack Sahara API. You can find documentation for both @@ -46,13 +42,14 @@ * `Specs`_ * `How to Contribute`_ - .. _PyPi: https://pypi.python.org/pypi/python-saharaclient + .. _PyPi: https://pypi.org/project/python-saharaclient .. _Online Documentation: https://docs.openstack.org/python-saharaclient/latest/ .. _Blueprints: http://specs.openstack.org/openstack/sahara-specs/ .. _Bugs: https://storyboard.openstack.org/#!/project/934 .. _Source: https://git.openstack.org/cgit/openstack/python-saharaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Specs: https://specs.openstack.org/openstack/sahara-specs/ + .. _Release Notes: https://docs.openstack.org/releasenotes/python-saharaclient Platform: UNKNOWN diff -Nru python-saharaclient-1.6.0/python_saharaclient.egg-info/entry_points.txt python-saharaclient-2.0.0/python_saharaclient.egg-info/entry_points.txt --- python-saharaclient-1.6.0/python_saharaclient.egg-info/entry_points.txt 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/python_saharaclient.egg-info/entry_points.txt 2018-07-26 22:37:14.000000000 +0000 @@ -58,3 +58,12 @@ dataprocessing_plugin_show = saharaclient.osc.v1.plugins:ShowPlugin dataprocessing_plugin_update = saharaclient.osc.v1.plugins:UpdatePlugin +[openstack.data_processing.v2] +dataprocessing_node_group_template_create = saharaclient.osc.v2.node_group_templates:CreateNodeGroupTemplate +dataprocessing_node_group_template_delete = saharaclient.osc.v2.node_group_templates:DeleteNodeGroupTemplate +dataprocessing_node_group_template_export = saharaclient.osc.v2.node_group_templates:ExportNodeGroupTemplate +dataprocessing_node_group_template_import = saharaclient.osc.v2.node_group_templates:ImportNodeGroupTemplate +dataprocessing_node_group_template_list = saharaclient.osc.v2.node_group_templates:ListNodeGroupTemplates +dataprocessing_node_group_template_show = saharaclient.osc.v2.node_group_templates:ShowNodeGroupTemplate +dataprocessing_node_group_template_update = saharaclient.osc.v2.node_group_templates:UpdateNodeGroupTemplate + diff -Nru python-saharaclient-1.6.0/python_saharaclient.egg-info/pbr.json python-saharaclient-2.0.0/python_saharaclient.egg-info/pbr.json --- python-saharaclient-1.6.0/python_saharaclient.egg-info/pbr.json 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/python_saharaclient.egg-info/pbr.json 2018-07-26 22:37:14.000000000 +0000 @@ -1 +1 @@ -{"git_version": "c3fd27e", "is_release": true} \ No newline at end of file +{"git_version": "2a66f9a", "is_release": true} \ No newline at end of file diff -Nru python-saharaclient-1.6.0/python_saharaclient.egg-info/PKG-INFO python-saharaclient-2.0.0/python_saharaclient.egg-info/PKG-INFO --- python-saharaclient-1.6.0/python_saharaclient.egg-info/PKG-INFO 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/python_saharaclient.egg-info/PKG-INFO 2018-07-26 22:37:14.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: python-saharaclient -Version: 1.6.0 +Version: 2.0.0 Summary: Client library for Sahara API Home-page: https://docs.openstack.org/python-saharaclient/latest/ Author: OpenStack @@ -19,13 +19,9 @@ =========================================== .. image:: https://img.shields.io/pypi/v/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ + :target: https://pypi.org/project/python-saharaclient/ :alt: Latest Version - .. image:: https://img.shields.io/pypi/dm/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ - :alt: Downloads - This is a client for the OpenStack Sahara API. There's a Python API (the ``saharaclient`` module), and a command-line script (``sahara``). Each implements the OpenStack Sahara API. You can find documentation for both @@ -46,13 +42,14 @@ * `Specs`_ * `How to Contribute`_ - .. _PyPi: https://pypi.python.org/pypi/python-saharaclient + .. _PyPi: https://pypi.org/project/python-saharaclient .. _Online Documentation: https://docs.openstack.org/python-saharaclient/latest/ .. _Blueprints: http://specs.openstack.org/openstack/sahara-specs/ .. _Bugs: https://storyboard.openstack.org/#!/project/934 .. _Source: https://git.openstack.org/cgit/openstack/python-saharaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Specs: https://specs.openstack.org/openstack/sahara-specs/ + .. _Release Notes: https://docs.openstack.org/releasenotes/python-saharaclient Platform: UNKNOWN diff -Nru python-saharaclient-1.6.0/python_saharaclient.egg-info/requires.txt python-saharaclient-2.0.0/python_saharaclient.egg-info/requires.txt --- python-saharaclient-1.6.0/python_saharaclient.egg-info/requires.txt 2018-04-19 22:21:49.000000000 +0000 +++ python-saharaclient-2.0.0/python_saharaclient.egg-info/requires.txt 2018-07-26 22:37:14.000000000 +0000 @@ -1,7 +1,7 @@ pbr!=2.1.0,>=2.0.0 Babel!=2.4.0,>=2.3.4 keystoneauth1>=3.4.0 -osc-lib>=1.8.0 +osc-lib>=1.11.0 oslo.log>=3.36.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.i18n>=3.15.3 diff -Nru python-saharaclient-1.6.0/python_saharaclient.egg-info/SOURCES.txt python-saharaclient-2.0.0/python_saharaclient.egg-info/SOURCES.txt --- python-saharaclient-1.6.0/python_saharaclient.egg-info/SOURCES.txt 2018-04-19 22:21:51.000000000 +0000 +++ python-saharaclient-2.0.0/python_saharaclient.egg-info/SOURCES.txt 2018-07-26 22:37:15.000000000 +0000 @@ -50,6 +50,8 @@ releasenotes/notes/job-binary-create-optional-bc0f9ee6426c5659.yaml releasenotes/notes/job-create-optional-034307a6b5db2cf2.yaml releasenotes/notes/job-execution-create-optional-1014a403e5ffa7ac.yaml +releasenotes/notes/job-job-template-apiv2-change-93ffbf2b1360cddc.yaml +releasenotes/notes/multiple-clusters-change-69a15f00597739d7.yaml releasenotes/notes/new-cli-6119bf8a4fb24ab6.yaml releasenotes/notes/plugin-api-f650c26a030b7df8.yaml releasenotes/notes/remove-functional-tests-c4b9d43c2c32d121.yaml @@ -57,6 +59,7 @@ releasenotes/notes/remove-py26-dad75fc8d602b3c5.yaml releasenotes/notes/remove-py33-8364cb4805391750.yaml releasenotes/notes/rename_version_to_plugin-version-20cfe17530446391.yaml +releasenotes/notes/rework-auth-c3e13a68a935671e.yaml releasenotes/notes/shares-update-d6f7e28acd27aa7f.yaml releasenotes/notes/start-using-reno-1f3418c11785c9ab.yaml releasenotes/notes/tags-update-c794416bcc035cb8.yaml @@ -97,6 +100,7 @@ saharaclient/api/v2/jobs.py saharaclient/osc/__init__.py saharaclient/osc/plugin.py +saharaclient/osc/utils.py saharaclient/osc/v1/__init__.py saharaclient/osc/v1/cluster_templates.py saharaclient/osc/v1/clusters.py @@ -108,7 +112,8 @@ saharaclient/osc/v1/jobs.py saharaclient/osc/v1/node_group_templates.py saharaclient/osc/v1/plugins.py -saharaclient/osc/v1/utils.py +saharaclient/osc/v2/__init__.py +saharaclient/osc/v2/node_group_templates.py saharaclient/tests/__init__.py saharaclient/tests/hacking/__init__.py saharaclient/tests/hacking/checks.py @@ -146,4 +151,6 @@ saharaclient/tests/unit/osc/v1/test_jobs.py saharaclient/tests/unit/osc/v1/test_node_group_templates.py saharaclient/tests/unit/osc/v1/test_plugins.py -saharaclient/tests/unit/osc/v1/test_utils.py \ No newline at end of file +saharaclient/tests/unit/osc/v1/test_utils.py +saharaclient/tests/unit/osc/v2/__init__.py +saharaclient/tests/unit/osc/v2/test_node_group_templates.py \ No newline at end of file diff -Nru python-saharaclient-1.6.0/README.rst python-saharaclient-2.0.0/README.rst --- python-saharaclient-1.6.0/README.rst 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/README.rst 2018-07-26 22:34:12.000000000 +0000 @@ -11,13 +11,9 @@ =========================================== .. image:: https://img.shields.io/pypi/v/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ + :target: https://pypi.org/project/python-saharaclient/ :alt: Latest Version -.. image:: https://img.shields.io/pypi/dm/python-saharaclient.svg - :target: https://pypi.python.org/pypi/python-saharaclient/ - :alt: Downloads - This is a client for the OpenStack Sahara API. There's a Python API (the ``saharaclient`` module), and a command-line script (``sahara``). Each implements the OpenStack Sahara API. You can find documentation for both @@ -38,11 +34,12 @@ * `Specs`_ * `How to Contribute`_ -.. _PyPi: https://pypi.python.org/pypi/python-saharaclient +.. _PyPi: https://pypi.org/project/python-saharaclient .. _Online Documentation: https://docs.openstack.org/python-saharaclient/latest/ .. _Blueprints: http://specs.openstack.org/openstack/sahara-specs/ .. _Bugs: https://storyboard.openstack.org/#!/project/934 .. _Source: https://git.openstack.org/cgit/openstack/python-saharaclient .. _How to Contribute: https://docs.openstack.org/infra/manual/developers.html .. _Specs: https://specs.openstack.org/openstack/sahara-specs/ +.. _Release Notes: https://docs.openstack.org/releasenotes/python-saharaclient diff -Nru python-saharaclient-1.6.0/releasenotes/notes/job-job-template-apiv2-change-93ffbf2b1360cddc.yaml python-saharaclient-2.0.0/releasenotes/notes/job-job-template-apiv2-change-93ffbf2b1360cddc.yaml --- python-saharaclient-1.6.0/releasenotes/notes/job-job-template-apiv2-change-93ffbf2b1360cddc.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/releasenotes/notes/job-job-template-apiv2-change-93ffbf2b1360cddc.yaml 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,4 @@ +other: + - When using APIv2, the viewing (GET) of specific job templates and jobs and + the creation (POST) of job templates and jobs now only supports the API + behavior of Sahara 9.0.0.0b3 or later. diff -Nru python-saharaclient-1.6.0/releasenotes/notes/multiple-clusters-change-69a15f00597739d7.yaml python-saharaclient-2.0.0/releasenotes/notes/multiple-clusters-change-69a15f00597739d7.yaml --- python-saharaclient-1.6.0/releasenotes/notes/multiple-clusters-change-69a15f00597739d7.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/releasenotes/notes/multiple-clusters-change-69a15f00597739d7.yaml 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,4 @@ +--- +other: + - When using APIv2, the creation of multiple clusters simultaneously + now only supports the API behavior of Sahara 9.0.0.0b2 or later. diff -Nru python-saharaclient-1.6.0/releasenotes/notes/rework-auth-c3e13a68a935671e.yaml python-saharaclient-2.0.0/releasenotes/notes/rework-auth-c3e13a68a935671e.yaml --- python-saharaclient-1.6.0/releasenotes/notes/rework-auth-c3e13a68a935671e.yaml 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/releasenotes/notes/rework-auth-c3e13a68a935671e.yaml 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,6 @@ +--- +upgrade: + - | + The Sahara client library now only supports authentication with a Keystone + session object. Consequently the arguments which `saharaclient.api.Client` + accepts, and the order of those arguments, have changed. diff -Nru python-saharaclient-1.6.0/requirements.txt python-saharaclient-2.0.0/requirements.txt --- python-saharaclient-1.6.0/requirements.txt 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/requirements.txt 2018-07-26 22:34:12.000000000 +0000 @@ -6,7 +6,7 @@ Babel!=2.4.0,>=2.3.4 # BSD keystoneauth1>=3.4.0 # Apache-2.0 -osc-lib>=1.8.0 # Apache-2.0 +osc-lib>=1.11.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 diff -Nru python-saharaclient-1.6.0/saharaclient/api/base.py python-saharaclient-2.0.0/saharaclient/api/base.py --- python-saharaclient-1.6.0/saharaclient/api/base.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/base.py 2018-07-26 22:34:12.000000000 +0000 @@ -213,9 +213,12 @@ else: resp = self.api.delete(url) - if resp.status_code != 204: + if resp.status_code not in [200, 204]: self._raise_api_exception(resp) + if resp.status_code == 200: + return get_json(resp) + def _plurify_resource_name(self): return self.resource_class.resource_name + 's' diff -Nru python-saharaclient-1.6.0/saharaclient/api/client.py python-saharaclient-2.0.0/saharaclient/api/client.py --- python-saharaclient-1.6.0/saharaclient/api/client.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/client.py 2018-07-26 22:34:12.000000000 +0000 @@ -13,13 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import warnings - from keystoneauth1 import adapter -from keystoneauth1.identity import v2 -from keystoneauth1.identity import v3 -from keystoneauth1 import session as keystone_session -from keystoneauth1 import token_endpoint from saharaclient.api import cluster_templates from saharaclient.api import clusters @@ -52,59 +46,21 @@ _api_version = '1.1' """Client for the OpenStack Data Processing API. - - :param str username: Username for Keystone authentication. - :param str api_key: Password for Keystone authentication. - :param str project_id: Keystone Tenant id. - :param str project_name: Keystone Tenant name. - :param str auth_url: Keystone URL that will be used for authentication. - :param str sahara_url: Sahara REST API URL to communicate with. - :param str endpoint_type: Desired Sahara endpoint type. - :param str service_type: Sahara service name in Keystone catalog. - :param str input_auth_token: Keystone authorization token. - :param session: Keystone Session object. - :param auth: Keystone Authentication Plugin object. - :param boolean insecure: Allow insecure. - :param string cacert: Path to the Privacy Enhanced Mail (PEM) file - which contains certificates needed to establish - SSL connection with the identity service. + :param session: Keystone session object. Required. + :param string sahara_url: Endpoint override. + :param string endpoint_type: Desired Sahara endpoint type. + :param string service_type: Sahara service name in Keystone catalog. :param string region_name: Name of a region to select when choosing an endpoint from the service catalog. """ - def __init__(self, username=None, api_key=None, project_id=None, - project_name=None, auth_url=None, sahara_url=None, + def __init__(self, session=None, sahara_url=None, endpoint_type='publicURL', service_type='data-processing', - input_auth_token=None, session=None, auth=None, - insecure=False, cacert=None, region_name=None, **kwargs): + region_name=None, **kwargs): if not session: - warnings.simplefilter('once', category=DeprecationWarning) - warnings.warn('Passing authentication parameters to saharaclient ' - 'is deprecated. Please construct and pass an ' - 'authenticated session object directly.', - DeprecationWarning) - warnings.resetwarnings() - - if input_auth_token: - auth = token_endpoint.Token(sahara_url, input_auth_token) - - else: - auth = self._get_keystone_auth(auth_url=auth_url, - username=username, - api_key=api_key, - project_id=project_id, - project_name=project_name) - - verify = True - if insecure: - verify = False - elif cacert: - verify = cacert + raise RuntimeError("Must provide session") - session = keystone_session.Session(verify=verify) - - if not auth: - auth = session.auth + auth = session.auth kwargs['user_agent'] = USER_AGENT kwargs.setdefault('interface', endpoint_type) @@ -138,28 +94,6 @@ ) self.job_types = job_types.JobTypesManager(client) - def _get_keystone_auth(self, username=None, api_key=None, auth_url=None, - project_id=None, project_name=None): - if not auth_url: - raise RuntimeError("No auth url specified") - - if 'v2.0' in auth_url: - return v2.Password(auth_url=auth_url, - username=username, - password=api_key, - tenant_id=project_id, - tenant_name=project_name) - else: - # NOTE(jamielennox): Setting these to default is what - # keystoneclient does in the event they are not passed. - return v3.Password(auth_url=auth_url, - username=username, - password=api_key, - user_domain_id='default', - project_id=project_id, - project_name=project_name, - project_domain_id='default') - class ClientV2(Client): diff -Nru python-saharaclient-1.6.0/saharaclient/api/clusters.py python-saharaclient-2.0.0/saharaclient/api/clusters.py --- python-saharaclient-1.6.0/saharaclient/api/clusters.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/clusters.py 2018-07-26 22:34:12.000000000 +0000 @@ -45,12 +45,12 @@ is_transient, description, cluster_configs, node_groups, user_keypair_id, anti_affinity, net_id, count, use_autoconfig, shares, - is_public, is_protected) + is_public, is_protected, api_ver=1.1) def _do_create(self, data, cluster_template_id, default_image_id, is_transient, description, cluster_configs, node_groups, user_keypair_id, anti_affinity, net_id, count, - use_autoconfig, shares, is_public, is_protected): + use_autoconfig, shares, is_public, is_protected, api_ver): # Checking if count is greater than 1, otherwise we set it to None # so the created dict in the _copy_if_defined method does not contain @@ -75,7 +75,10 @@ is_protected=is_protected) if count: - return self._create('/clusters/multiple', data) + if api_ver >= 2: + return self._create('/clusters', data) + else: + return self._create('/clusters/multiple', data) return self._create('/clusters', data, 'cluster') @@ -170,7 +173,7 @@ is_transient, description, cluster_configs, node_groups, user_keypair_id, anti_affinity, net_id, count, use_autoconfig, shares, - is_public, is_protected) + is_public, is_protected, api_ver=2) def scale(self, cluster_id, scale_object): """Scale an existing Cluster. @@ -208,7 +211,7 @@ def force_delete(self, cluster_id): """Force Delete a Cluster.""" data = {'force': True} - self._delete('/clusters/%s' % cluster_id, data) + return self._delete('/clusters/%s' % cluster_id, data) # NOTE(jfreud): keep this around for backwards compatibility diff -Nru python-saharaclient-1.6.0/saharaclient/api/data_sources.py python-saharaclient-2.0.0/saharaclient/api/data_sources.py --- python-saharaclient-1.6.0/saharaclient/api/data_sources.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/data_sources.py 2018-07-26 22:34:12.000000000 +0000 @@ -26,7 +26,7 @@ def create(self, name, description, data_source_type, url, credential_user=None, credential_pass=None, - is_public=None, is_protected=None): + is_public=None, is_protected=None, s3_credentials=None): """Create a Data Source.""" data = { @@ -34,14 +34,15 @@ 'description': description, 'type': data_source_type, 'url': url, - 'credentials': {} } - self._copy_if_defined(data['credentials'], + credentials = {} + self._copy_if_defined(credentials, user=credential_user, password=credential_pass) - + credentials = credentials or s3_credentials self._copy_if_defined(data, is_public=is_public, - is_protected=is_protected) + is_protected=is_protected, + credentials=credentials) return self._create('/data-sources', data, 'data_source') @@ -75,7 +76,9 @@ * url * is_public * is_protected - * credentials - dict with `user` and `password` keyword arguments + * credentials - dict with the keys `user` and `password` for data + source in Swift, or with the keys `accesskey`, `secretkey`, + `endpoint`, `ssl`, and `bucket_in_path` for data source in S3 """ if self.version >= 2: diff -Nru python-saharaclient-1.6.0/saharaclient/api/node_group_templates.py python-saharaclient-2.0.0/saharaclient/api/node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/api/node_group_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -57,7 +57,8 @@ auto_security_group, availability_zone, volumes_availability_zone, volume_type, image_id, is_proxy_gateway, volume_local_to_instance, use_autoconfig, - shares, is_public, is_protected, volume_mount_prefix): + shares, is_public, is_protected, volume_mount_prefix, + boot_from_volume=None): self._copy_if_defined(data, description=description, @@ -71,7 +72,8 @@ use_autoconfig=use_autoconfig, shares=shares, is_public=is_public, - is_protected=is_protected + is_protected=is_protected, + boot_from_volume=boot_from_volume ) if volumes_per_node: @@ -160,7 +162,7 @@ volume_type=None, image_id=None, is_proxy_gateway=None, volume_local_to_instance=None, use_autoconfig=None, shares=None, is_public=None, is_protected=None, - volume_mount_prefix=None): + volume_mount_prefix=None, boot_from_volume=None): """Create a Node Group Template.""" data = { @@ -178,7 +180,7 @@ volume_type, image_id, is_proxy_gateway, volume_local_to_instance, use_autoconfig, shares, is_public, is_protected, - volume_mount_prefix) + volume_mount_prefix, boot_from_volume) def update(self, ng_template_id, name=NotUpdated, plugin_name=NotUpdated, plugin_version=NotUpdated, flavor_id=NotUpdated, @@ -191,7 +193,8 @@ image_id=NotUpdated, is_proxy_gateway=NotUpdated, volume_local_to_instance=NotUpdated, use_autoconfig=NotUpdated, shares=NotUpdated, is_public=NotUpdated, - is_protected=NotUpdated, volume_mount_prefix=NotUpdated): + is_protected=NotUpdated, volume_mount_prefix=NotUpdated, + boot_from_volume=NotUpdated): """Update a Node Group Template.""" data = {} @@ -210,7 +213,8 @@ volume_local_to_instance=volume_local_to_instance, use_autoconfig=use_autoconfig, shares=shares, is_public=is_public, is_protected=is_protected, - volume_mount_prefix=volume_mount_prefix + volume_mount_prefix=volume_mount_prefix, + boot_from_volume=boot_from_volume ) return self._patch('/node-group-templates/%s' % ng_template_id, data, diff -Nru python-saharaclient-1.6.0/saharaclient/api/v2/jobs.py python-saharaclient-2.0.0/saharaclient/api/v2/jobs.py --- python-saharaclient-1.6.0/saharaclient/api/v2/jobs.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/v2/jobs.py 2018-07-26 22:34:12.000000000 +0000 @@ -34,7 +34,7 @@ def get(self, obj_id): """Get information about a Job.""" - return self._get('/jobs/%s' % obj_id, 'job_execution') + return self._get('/jobs/%s' % obj_id, 'job') def delete(self, obj_id): """Delete a Job.""" @@ -54,13 +54,13 @@ job_configs=configs, interface=interface, is_public=is_public, is_protected=is_protected) - return self._create('/jobs', data, 'job_execution') + return self._create('/jobs', data, 'job') def refresh_status(self, obj_id): """Refresh Job Status.""" return self._get( '/jobs/%s?refresh_status=True' % obj_id, - 'job_execution' + 'job' ) def update(self, obj_id, is_public=NotUpdated, is_protected=NotUpdated): diff -Nru python-saharaclient-1.6.0/saharaclient/api/v2/job_templates.py python-saharaclient-2.0.0/saharaclient/api/v2/job_templates.py --- python-saharaclient-1.6.0/saharaclient/api/v2/job_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/api/v2/job_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -36,7 +36,7 @@ libs=libs, interface=interface, is_public=is_public, is_protected=is_protected) - return self._create('/%s' % 'job-templates', data, 'job') + return self._create('/%s' % 'job-templates', data, 'job_template') def list(self, search_opts=None, limit=None, marker=None, sort_by=None, reverse=None): @@ -49,7 +49,7 @@ def get(self, job_id): """Get information about a Job Template.""" - return self._get('/%s/%s' % ('job-templates', job_id), 'job') + return self._get('/%s/%s' % ('job-templates', job_id), 'job_template') def get_configs(self, job_type): """Get config hints for a specified Job Template type.""" diff -Nru python-saharaclient-1.6.0/saharaclient/osc/plugin.py python-saharaclient-2.0.0/saharaclient/osc/plugin.py --- python-saharaclient-1.6.0/saharaclient/osc/plugin.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/plugin.py 2018-07-26 22:34:12.000000000 +0000 @@ -24,7 +24,8 @@ API_VERSION_OPTION = "os_data_processing_api_version" API_NAME = "data_processing" API_VERSIONS = { - "1.1": "saharaclient.api.client.Client" + "1.1": "saharaclient.api.client.Client", + "2": "saharaclient.api.client.ClientV2" } @@ -41,8 +42,6 @@ client = data_processing_client( session=instance.session, region_name=instance._region_name, - cacert=instance._cacert, - insecure=instance._insecure, sahara_url=instance._cli_options.data_processing_url, **kwargs ) diff -Nru python-saharaclient-1.6.0/saharaclient/osc/utils.py python-saharaclient-2.0.0/saharaclient/osc/utils.py --- python-saharaclient-1.6.0/saharaclient/osc/utils.py 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/utils.py 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,373 @@ +# 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 sys +import time + +from osc_lib import exceptions +from osc_lib import utils as osc_utils +from oslo_serialization import jsonutils as json +from oslo_utils import timeutils +from oslo_utils import uuidutils + +from saharaclient.api import base + + +def get_resource(manager, name_or_id, **kwargs): + if uuidutils.is_uuid_like(name_or_id): + return manager.get(name_or_id, **kwargs) + else: + resource = manager.find_unique(name=name_or_id) + if kwargs: + # we really need additional call to apply kwargs + resource = manager.get(resource.id, **kwargs) + return resource + + +def created_at_sorted(objs, reverse=False): + return sorted(objs, key=created_at_key, reverse=reverse) + + +def random_name(prefix=None): + return "%s-%s" % (prefix, uuidutils.generate_uuid()[:8]) + + +def created_at_key(obj): + return timeutils.parse_isotime(obj["created_at"]) + + +def get_resource_id(manager, name_or_id): + if uuidutils.is_uuid_like(name_or_id): + return name_or_id + else: + return manager.find_unique(name=name_or_id).id + + +def create_dict_from_kwargs(**kwargs): + return {k: v for (k, v) in kwargs.items() if v is not None} + + +def prepare_data(data, fields): + new_data = {} + for f in fields: + if f in data: + new_data[f.replace('_', ' ').capitalize()] = data[f] + + return new_data + + +def unzip(data): + return zip(*data) + + +def extend_columns(columns, items): + return unzip(list(unzip(columns)) + [('', '')] + items) + + +def prepare_column_headers(columns, remap=None): + remap = remap if remap else {} + new_columns = [] + for c in columns: + for old, new in remap.items(): + c = c.replace(old, new) + new_columns.append(c.replace('_', ' ').capitalize()) + + return new_columns + + +def get_by_name_substring(data, name): + return [obj for obj in data if name in obj.name] + + +def wait_for_delete(manager, obj_id, sleep_time=5, timeout=3000): + s_time = timeutils.utcnow() + while timeutils.delta_seconds(s_time, timeutils.utcnow()) < timeout: + try: + manager.get(obj_id) + except base.APIException as ex: + if ex.error_code == 404: + return True + raise + time.sleep(sleep_time) + + return False + + +def create_node_group_templates(client, app, parsed_args, flavor_id, configs, + shares): + if app.api_version['data_processing'] == '2': + data = client.node_group_templates.create( + name=parsed_args.name, + plugin_name=parsed_args.plugin, + plugin_version=parsed_args.plugin_version, + flavor_id=flavor_id, + description=parsed_args.description, + volumes_per_node=parsed_args.volumes_per_node, + volumes_size=parsed_args.volumes_size, + node_processes=parsed_args.processes, + floating_ip_pool=parsed_args.floating_ip_pool, + security_groups=parsed_args.security_groups, + auto_security_group=parsed_args.auto_security_group, + availability_zone=parsed_args.availability_zone, + volume_type=parsed_args.volumes_type, + is_proxy_gateway=parsed_args.proxy_gateway, + volume_local_to_instance=parsed_args.volumes_locality, + use_autoconfig=parsed_args.autoconfig, + is_public=parsed_args.public, + is_protected=parsed_args.protected, + node_configs=configs, + shares=shares, + volumes_availability_zone=( + parsed_args.volumes_availability_zone), + volume_mount_prefix=parsed_args.volumes_mount_prefix, + boot_from_volume=parsed_args.boot_from_volume).to_dict() + else: + data = client.node_group_templates.create( + name=parsed_args.name, + plugin_name=parsed_args.plugin, + hadoop_version=parsed_args.plugin_version, + flavor_id=flavor_id, + description=parsed_args.description, + volumes_per_node=parsed_args.volumes_per_node, + volumes_size=parsed_args.volumes_size, + node_processes=parsed_args.processes, + floating_ip_pool=parsed_args.floating_ip_pool, + security_groups=parsed_args.security_groups, + auto_security_group=parsed_args.auto_security_group, + availability_zone=parsed_args.availability_zone, + volume_type=parsed_args.volumes_type, + is_proxy_gateway=parsed_args.proxy_gateway, + volume_local_to_instance=parsed_args.volumes_locality, + use_autoconfig=parsed_args.autoconfig, + is_public=parsed_args.public, + is_protected=parsed_args.protected, + node_configs=configs, + shares=shares, + volumes_availability_zone=( + parsed_args.volumes_availability_zone), + volume_mount_prefix=parsed_args.volumes_mount_prefix).to_dict() + return data + + +class NodeGroupTemplatesUtils(object): + + def _create_take_action(self, client, app, parsed_args): + if parsed_args.json: + blob = osc_utils.read_blob_file_contents(parsed_args.json) + try: + template = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'template from file %s: %s' % (parsed_args.json, e)) + data = client.node_group_templates.create(**template).to_dict() + else: + if (not parsed_args.name or not parsed_args.plugin or + not parsed_args.plugin_version or not parsed_args.flavor or + not parsed_args.processes): + raise exceptions.CommandError( + 'At least --name, --plugin, --plugin-version, --processes,' + ' --flavor arguments should be specified or json template ' + 'should be provided with --json argument') + + configs = None + if parsed_args.configs: + blob = osc_utils.read_blob_file_contents(parsed_args.configs) + try: + configs = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'configs from file %s: %s' % (parsed_args.configs, e)) + + shares = None + if parsed_args.shares: + blob = osc_utils.read_blob_file_contents(parsed_args.shares) + try: + shares = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'shares from file %s: %s' % (parsed_args.shares, e)) + + compute_client = app.client_manager.compute + flavor_id = osc_utils.find_resource( + compute_client.flavors, parsed_args.flavor).id + + data = create_node_group_templates(client, app, parsed_args, + flavor_id, configs, shares) + + return data + + def _list_take_action(self, client, app, parsed_args): + search_opts = {} + if parsed_args.plugin: + search_opts['plugin_name'] = parsed_args.plugin + if parsed_args.plugin_version: + search_opts['hadoop_version'] = parsed_args.plugin_version + + data = client.node_group_templates.list(search_opts=search_opts) + + if parsed_args.name: + data = get_by_name_substring(data, parsed_args.name) + + if app.api_version['data_processing'] == '2': + if parsed_args.long: + columns = ('name', 'id', 'plugin_name', 'plugin_version', + 'node_processes', 'description') + column_headers = prepare_column_headers(columns) + + else: + columns = ('name', 'id', 'plugin_name', 'plugin_version') + column_headers = prepare_column_headers(columns) + else: + if parsed_args.long: + columns = ('name', 'id', 'plugin_name', 'hadoop_version', + 'node_processes', 'description') + column_headers = prepare_column_headers( + columns, {'hadoop_version': 'plugin_version'}) + + else: + columns = ('name', 'id', 'plugin_name', 'hadoop_version') + column_headers = prepare_column_headers( + columns, {'hadoop_version': 'plugin_version'}) + + return ( + column_headers, + (osc_utils.get_item_properties( + s, + columns, + formatters={ + 'node_processes': osc_utils.format_list + } + ) for s in data) + ) + + def _update_take_action(self, client, app, parsed_args): + ngt_id = get_resource_id( + client.node_group_templates, parsed_args.node_group_template) + + if parsed_args.json: + blob = osc_utils.read_blob_file_contents(parsed_args.json) + try: + template = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'template from file %s: %s' % (parsed_args.json, e)) + data = client.node_group_templates.update( + ngt_id, **template).to_dict() + else: + configs = None + if parsed_args.configs: + blob = osc_utils.read_blob_file_contents(parsed_args.configs) + try: + configs = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'configs from file %s: %s' % (parsed_args.configs, e)) + + shares = None + if parsed_args.shares: + blob = osc_utils.read_blob_file_contents(parsed_args.shares) + try: + shares = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'shares from file %s: %s' % (parsed_args.shares, e)) + + flavor_id = None + if parsed_args.flavor: + compute_client = self.app.client_manager.compute + flavor_id = osc_utils.find_resource( + compute_client.flavors, parsed_args.flavor).id + + update_dict = create_dict_from_kwargs( + name=parsed_args.name, + plugin_name=parsed_args.plugin, + hadoop_version=parsed_args.plugin_version, + flavor_id=flavor_id, + description=parsed_args.description, + volumes_per_node=parsed_args.volumes_per_node, + volumes_size=parsed_args.volumes_size, + node_processes=parsed_args.processes, + floating_ip_pool=parsed_args.floating_ip_pool, + security_groups=parsed_args.security_groups, + auto_security_group=parsed_args.use_auto_security_group, + availability_zone=parsed_args.availability_zone, + volume_type=parsed_args.volumes_type, + is_proxy_gateway=parsed_args.is_proxy_gateway, + volume_local_to_instance=parsed_args.volume_locality, + use_autoconfig=parsed_args.use_autoconfig, + is_public=parsed_args.is_public, + is_protected=parsed_args.is_protected, + node_configs=configs, + shares=shares, + volumes_availability_zone=( + parsed_args.volumes_availability_zone), + volume_mount_prefix=parsed_args.volumes_mount_prefix + ) + + if app.api_version['data_processing'] == '2': + if 'hadoop_version' in update_dict: + update_dict.pop('hadoop_version') + update_dict['plugin_version'] = parsed_args.plugin_version + if parsed_args.boot_from_volume is not None: + update_dict['boot_from_volume'] = ( + parsed_args.boot_from_volume) + data = client.node_group_templates.update( + ngt_id, **update_dict).to_dict() + + return data + + def _import_take_action(self, client, parsed_args): + if (not parsed_args.image_id or + not parsed_args.flavor_id): + raise exceptions.CommandError( + 'At least --image_id and --flavor_id should be specified') + blob = osc_utils.read_blob_file_contents(parsed_args.json) + try: + template = json.loads(blob) + except ValueError as e: + raise exceptions.CommandError( + 'An error occurred when reading ' + 'template from file %s: %s' % (parsed_args.json, e)) + template['node_group_template']['floating_ip_pool'] = ( + parsed_args.floating_ip_pool) + template['node_group_template']['image_id'] = ( + parsed_args.image_id) + template['node_group_template']['flavor_id'] = ( + parsed_args.flavor_id) + template['node_group_template']['security_groups'] = ( + parsed_args.security_groups) + if parsed_args.name: + template['node_group_template']['name'] = parsed_args.name + data = client.node_group_templates.create( + **template['node_group_template']).to_dict() + + return data + + def _export_take_action(self, client, parsed_args): + ngt_id = get_resource_id( + client.node_group_templates, parsed_args.node_group_template) + response = client.node_group_templates.export(ngt_id) + result = json.dumps(response._info, indent=4)+"\n" + if parsed_args.file: + with open(parsed_args.file, "w+") as file: + file.write(result) + else: + sys.stdout.write(result) diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/clusters.py python-saharaclient-2.0.0/saharaclient/osc/v1/clusters.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/clusters.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/clusters.py 2018-07-26 22:34:12.000000000 +0000 @@ -21,7 +21,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils CLUSTER_FIELDS = ["cluster_template_id", "use_autoconfig", "user_keypair_id", "status", "image", "node_groups", "id", "info", diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/cluster_templates.py python-saharaclient-2.0.0/saharaclient/osc/v1/cluster_templates.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/cluster_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/cluster_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -21,7 +21,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils as json -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils CT_FIELDS = ['id', 'name', 'plugin_name', 'plugin_version', 'description', 'node_groups', 'anti_affinity', 'use_autoconfig', 'is_default', diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/data_sources.py python-saharaclient-2.0.0/saharaclient/osc/v1/data_sources.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/data_sources.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/data_sources.py 2018-07-26 22:34:12.000000000 +0000 @@ -19,11 +19,11 @@ from osc_lib import utils as osc_utils from oslo_log import log as logging -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils DATA_SOURCE_FIELDS = ['name', 'id', 'type', 'url', 'description', 'is_public', 'is_protected'] -DATA_SOURCE_CHOICES = ["swift", "hdfs", "maprfs", "manila"] +DATA_SOURCE_CHOICES = ["swift", "hdfs", "maprfs", "manila", "s3"] class CreateDataSource(command.ShowOne): @@ -53,16 +53,60 @@ help="URL for the data source [REQUIRED]", required=True ) - parser.add_argument( + username = parser.add_mutually_exclusive_group() + username.add_argument( '--username', metavar="", help="Username for accessing the data source URL" ) - parser.add_argument( + username.add_argument( + '--access-key', + metavar='', + help='S3 access key for accessing the data source URL', + ) + password = parser.add_mutually_exclusive_group() + password.add_argument( '--password', metavar="", help="Password for accessing the data source URL" ) + password.add_argument( + '--secret-key', + metavar='', + help='S3 secret key for accessing the data source URL', + ) + parser.add_argument( + '--s3-endpoint', + metavar='', + help='S3 endpoint for accessing the data source URL (ignored if ' + 'data source not in S3)', + ) + enable_s3_ssl = parser.add_mutually_exclusive_group() + enable_s3_ssl.add_argument( + '--enable-s3-ssl', + action='store_true', + help='Enable access to S3 endpoint using SSL (ignored if data ' + 'source not in S3)' + ) + enable_s3_ssl.add_argument( + '--disable-s3-ssl', + action='store_false', + help='Disable access to S3 endpoint using SSL (ignored if data ' + 'source not in S3)' + ) + s3_bucket_in_path = parser.add_mutually_exclusive_group() + s3_bucket_in_path.add_argument( + '--enable-s3-bucket-in-path', + action='store_true', + help='Access S3 endpoint using bucket name in path ' + '(ignored if data source not in S3)' + ) + s3_bucket_in_path.add_argument( + '--disable-s3-bucket-in-path', + action='store_false', + help='Access S3 endpoint using bucket name in path ' + '(ignored if data source not in S3)' + ) parser.add_argument( '--description', metavar="", @@ -86,6 +130,22 @@ self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing + s3_credentials = {} + if parsed_args.access_key: + s3_credentials['accesskey'] = parsed_args.access_key + if parsed_args.secret_key: + s3_credentials['secretkey'] = parsed_args.secret_key + if parsed_args.s3_endpoint: + s3_credentials['endpoint'] = parsed_args.s3_endpoint + if parsed_args.enable_s3_ssl == parsed_args.disable_s3_ssl: + s3_credentials['ssl'] = parsed_args.enable_s3_ssl + if (parsed_args.enable_s3_bucket_in_path == + parsed_args.disable_s3_bucket_in_path): + s3_credentials['bucket_in_path'] = ( + parsed_args.enable_s3_bucket_in_path) + + s3_credentials = s3_credentials or None + description = parsed_args.description or '' data = client.data_sources.create( name=parsed_args.name, description=description, @@ -93,7 +153,9 @@ credential_user=parsed_args.username, credential_pass=parsed_args.password, is_public=parsed_args.public, - is_protected=parsed_args.protected).to_dict() + is_protected=parsed_args.protected, + s3_credentials=s3_credentials + ).to_dict() data = utils.prepare_data(data, DATA_SOURCE_FIELDS) @@ -230,16 +292,60 @@ metavar="", help="URL for the data source" ) - parser.add_argument( + username = parser.add_mutually_exclusive_group() + username.add_argument( '--username', metavar="", help="Username for accessing the data source URL" ) - parser.add_argument( + username.add_argument( + '--access-key', + metavar='', + help='S3 access key for accessing the data source URL', + ) + password = parser.add_mutually_exclusive_group() + password.add_argument( '--password', metavar="", help="Password for accessing the data source URL" ) + password.add_argument( + '--secret-key', + metavar='', + help='S3 secret key for accessing the data source URL', + ) + parser.add_argument( + '--s3-endpoint', + metavar='', + help='S3 endpoint for accessing the data source URL (ignored if ' + 'data source not in S3)', + ) + enable_s3_ssl = parser.add_mutually_exclusive_group() + enable_s3_ssl.add_argument( + '--enable-s3-ssl', + action='store_true', + help='Enable access to S3 endpoint using SSL (ignored if data ' + 'source not in S3)' + ) + enable_s3_ssl.add_argument( + '--disable-s3-ssl', + action='store_false', + help='Disable access to S3 endpoint using SSL (ignored if data ' + 'source not in S3)' + ) + s3_bucket_in_path = parser.add_mutually_exclusive_group() + s3_bucket_in_path.add_argument( + '--enable-s3-bucket-in-path', + action='store_true', + help='Access S3 endpoint using bucket name in path ' + '(ignored if data source not in S3)' + ) + s3_bucket_in_path.add_argument( + '--disable-s3-bucket-in-path', + action='store_false', + help='Access S3 endpoint using bucket name in path ' + '(ignored if data source not in S3)' + ) parser.add_argument( '--description', metavar="", @@ -280,10 +386,24 @@ client = self.app.client_manager.data_processing credentials = {} - if parsed_args.username: - credentials['user'] = parsed_args.username - if parsed_args.password: - credentials['password'] = parsed_args.password + if parsed_args.type == 'swift': + if parsed_args.username: + credentials['user'] = parsed_args.username + if parsed_args.password: + credentials['password'] = parsed_args.password + elif parsed_args.type == 's3': + if parsed_args.access_key: + credentials['accesskey'] = parsed_args.access_key + if parsed_args.secret_key: + credentials['secretkey'] = parsed_args.secret_key + if parsed_args.s3_endpoint: + credentials['endpoint'] = parsed_args.s3_endpoint + if parsed_args.enable_s3_ssl == parsed_args.disable_s3_ssl: + credentials['ssl'] = parsed_args.enable_s3_ssl + if (parsed_args.enable_s3_bucket_in_path == + parsed_args.disable_s3_bucket_in_path): + credentials['bucket_in_path'] = ( + parsed_args.enable_s3_bucket_in_path) if not credentials: credentials = None diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/images.py python-saharaclient-2.0.0/saharaclient/osc/v1/images.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/images.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/images.py 2018-07-26 22:34:12.000000000 +0000 @@ -19,7 +19,7 @@ from osc_lib import utils as osc_utils from oslo_log import log as logging -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils IMAGE_FIELDS = ['name', 'id', 'username', 'tags', 'status', 'description'] diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/job_binaries.py python-saharaclient-2.0.0/saharaclient/osc/v1/job_binaries.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/job_binaries.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/job_binaries.py 2018-07-26 22:34:12.000000000 +0000 @@ -23,7 +23,7 @@ from oslo_serialization import jsonutils from saharaclient.api import base -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils JOB_BINARY_FIELDS = ['name', 'id', 'url', 'description', 'is_public', 'is_protected'] diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/jobs.py python-saharaclient-2.0.0/saharaclient/osc/v1/jobs.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/jobs.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/jobs.py 2018-07-26 22:34:12.000000000 +0000 @@ -21,7 +21,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils JOB_FIELDS = ['id', 'job_template_id', 'cluster_id', 'input_id', 'output_id', 'start_time', 'end_time', 'status', 'is_public', 'is_protected', diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/job_templates.py python-saharaclient-2.0.0/saharaclient/osc/v1/job_templates.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/job_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/job_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -21,7 +21,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils JOB_TEMPLATE_FIELDS = ['name', 'id', 'type', 'mains', 'libs', 'description', 'is_public', 'is_protected'] diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/job_types.py python-saharaclient-2.0.0/saharaclient/osc/v1/job_types.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/job_types.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/job_types.py 2018-07-26 22:34:12.000000000 +0000 @@ -22,8 +22,8 @@ from oslo_log import log as logging from oslo_serialization import jsonutils +from saharaclient.osc import utils from saharaclient.osc.v1.job_templates import JOB_TYPES_CHOICES -from saharaclient.osc.v1 import utils class ListJobTypes(command.Lister): diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/node_group_templates.py python-saharaclient-2.0.0/saharaclient/osc/v1/node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/node_group_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -16,12 +16,10 @@ import sys from osc_lib.command import command -from osc_lib import exceptions from osc_lib import utils as osc_utils from oslo_log import log as logging -from oslo_serialization import jsonutils as json -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils NGT_FIELDS = ['id', 'name', 'plugin_name', 'plugin_version', 'node_processes', 'description', 'auto_security_group', 'security_groups', @@ -43,7 +41,7 @@ del data['volumes_size'] -class CreateNodeGroupTemplate(command.ShowOne): +class CreateNodeGroupTemplate(command.ShowOne, utils.NodeGroupTemplatesUtils): """Creates node group template""" log = logging.getLogger(__name__ + ".CreateNodeGroupTemplate") @@ -202,73 +200,7 @@ self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing - if parsed_args.json: - blob = osc_utils.read_blob_file_contents(parsed_args.json) - try: - template = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'template from file %s: %s' % (parsed_args.json, e)) - data = client.node_group_templates.create(**template).to_dict() - else: - if (not parsed_args.name or not parsed_args.plugin or - not parsed_args.plugin_version or not parsed_args.flavor or - not parsed_args.processes): - raise exceptions.CommandError( - 'At least --name, --plugin, --plugin-version, --processes,' - ' --flavor arguments should be specified or json template ' - 'should be provided with --json argument') - - configs = None - if parsed_args.configs: - blob = osc_utils.read_blob_file_contents(parsed_args.configs) - try: - configs = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'configs from file %s: %s' % (parsed_args.configs, e)) - - shares = None - if parsed_args.shares: - blob = osc_utils.read_blob_file_contents(parsed_args.shares) - try: - shares = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'shares from file %s: %s' % (parsed_args.shares, e)) - - compute_client = self.app.client_manager.compute - flavor_id = osc_utils.find_resource( - compute_client.flavors, parsed_args.flavor).id - - data = client.node_group_templates.create( - name=parsed_args.name, - plugin_name=parsed_args.plugin, - hadoop_version=parsed_args.plugin_version, - flavor_id=flavor_id, - description=parsed_args.description, - volumes_per_node=parsed_args.volumes_per_node, - volumes_size=parsed_args.volumes_size, - node_processes=parsed_args.processes, - floating_ip_pool=parsed_args.floating_ip_pool, - security_groups=parsed_args.security_groups, - auto_security_group=parsed_args.auto_security_group, - availability_zone=parsed_args.availability_zone, - volume_type=parsed_args.volumes_type, - is_proxy_gateway=parsed_args.proxy_gateway, - volume_local_to_instance=parsed_args.volumes_locality, - use_autoconfig=parsed_args.autoconfig, - is_public=parsed_args.public, - is_protected=parsed_args.protected, - node_configs=configs, - shares=shares, - volumes_availability_zone=( - parsed_args.volumes_availability_zone), - volume_mount_prefix=parsed_args.volumes_mount_prefix - ).to_dict() + data = self._create_take_action(client, self.app, parsed_args) _format_ngt_output(data) data = utils.prepare_data(data, NGT_FIELDS) @@ -276,7 +208,7 @@ return self.dict2columns(data) -class ListNodeGroupTemplates(command.Lister): +class ListNodeGroupTemplates(command.Lister, utils.NodeGroupTemplatesUtils): """Lists node group templates""" log = logging.getLogger(__name__ + ".ListNodeGroupTemplates") @@ -314,41 +246,10 @@ def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing - search_opts = {} - if parsed_args.plugin: - search_opts['plugin_name'] = parsed_args.plugin - if parsed_args.plugin_version: - search_opts['hadoop_version'] = parsed_args.plugin_version - - data = client.node_group_templates.list(search_opts=search_opts) - - if parsed_args.name: - data = utils.get_by_name_substring(data, parsed_args.name) - - if parsed_args.long: - columns = ('name', 'id', 'plugin_name', 'hadoop_version', - 'node_processes', 'description') - column_headers = utils.prepare_column_headers( - columns, {'hadoop_version': 'plugin_version'}) - - else: - columns = ('name', 'id', 'plugin_name', 'hadoop_version') - column_headers = utils.prepare_column_headers( - columns, {'hadoop_version': 'plugin_version'}) - - return ( - column_headers, - (osc_utils.get_item_properties( - s, - columns, - formatters={ - 'node_processes': osc_utils.format_list - } - ) for s in data) - ) + return self._list_take_action(client, self.app, parsed_args) -class ShowNodeGroupTemplate(command.ShowOne): +class ShowNodeGroupTemplate(command.ShowOne, utils.NodeGroupTemplatesUtils): """Display node group template details""" log = logging.getLogger(__name__ + ".ShowNodeGroupTemplate") @@ -378,7 +279,7 @@ return self.dict2columns(data) -class DeleteNodeGroupTemplate(command.Command): +class DeleteNodeGroupTemplate(command.Command, utils.NodeGroupTemplatesUtils): """Deletes node group template""" log = logging.getLogger(__name__ + ".DeleteNodeGroupTemplate") @@ -406,7 +307,7 @@ 'successfully.\n'.format(ngt=ngt)) -class UpdateNodeGroupTemplate(command.ShowOne): +class UpdateNodeGroupTemplate(command.ShowOne, utils.NodeGroupTemplatesUtils): """Updates node group template""" log = logging.getLogger(__name__ + ".UpdateNodeGroupTemplate") @@ -620,74 +521,7 @@ self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing - ngt_id = utils.get_resource_id( - client.node_group_templates, parsed_args.node_group_template) - - if parsed_args.json: - blob = osc_utils.read_blob_file_contents(parsed_args.json) - try: - template = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'template from file %s: %s' % (parsed_args.json, e)) - data = client.node_group_templates.update( - ngt_id, **template).to_dict() - else: - configs = None - if parsed_args.configs: - blob = osc_utils.read_blob_file_contents(parsed_args.configs) - try: - configs = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'configs from file %s: %s' % (parsed_args.configs, e)) - - shares = None - if parsed_args.shares: - blob = osc_utils.read_blob_file_contents(parsed_args.shares) - try: - shares = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'shares from file %s: %s' % (parsed_args.shares, e)) - - flavor_id = None - if parsed_args.flavor: - compute_client = self.app.client_manager.compute - flavor_id = osc_utils.find_resource( - compute_client.flavors, parsed_args.flavor).id - - update_dict = utils.create_dict_from_kwargs( - name=parsed_args.name, - plugin_name=parsed_args.plugin, - hadoop_version=parsed_args.plugin_version, - flavor_id=flavor_id, - description=parsed_args.description, - volumes_per_node=parsed_args.volumes_per_node, - volumes_size=parsed_args.volumes_size, - node_processes=parsed_args.processes, - floating_ip_pool=parsed_args.floating_ip_pool, - security_groups=parsed_args.security_groups, - auto_security_group=parsed_args.use_auto_security_group, - availability_zone=parsed_args.availability_zone, - volume_type=parsed_args.volumes_type, - is_proxy_gateway=parsed_args.is_proxy_gateway, - volume_local_to_instance=parsed_args.volume_locality, - use_autoconfig=parsed_args.use_autoconfig, - is_public=parsed_args.is_public, - is_protected=parsed_args.is_protected, - node_configs=configs, - shares=shares, - volumes_availability_zone=( - parsed_args.volumes_availability_zone), - volume_mount_prefix=parsed_args.volumes_mount_prefix - ) - - data = client.node_group_templates.update( - ngt_id, **update_dict).to_dict() + data = self._update_take_action(client, self.app, parsed_args) _format_ngt_output(data) data = utils.prepare_data(data, NGT_FIELDS) @@ -695,7 +529,7 @@ return self.dict2columns(data) -class ImportNodeGroupTemplate(command.ShowOne): +class ImportNodeGroupTemplate(command.ShowOne, utils.NodeGroupTemplatesUtils): """Imports node group template""" log = logging.getLogger(__name__ + ".ImportNodeGroupTemplate") @@ -740,29 +574,8 @@ def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing - if (not parsed_args.image_id or - not parsed_args.flavor_id): - raise exceptions.CommandError( - 'At least --image_id and --flavor_id should be specified') - blob = osc_utils.read_blob_file_contents(parsed_args.json) - try: - template = json.loads(blob) - except ValueError as e: - raise exceptions.CommandError( - 'An error occurred when reading ' - 'template from file %s: %s' % (parsed_args.json, e)) - template['node_group_template']['floating_ip_pool'] = ( - parsed_args.floating_ip_pool) - template['node_group_template']['image_id'] = ( - parsed_args.image_id) - template['node_group_template']['flavor_id'] = ( - parsed_args.flavor_id) - template['node_group_template']['security_groups'] = ( - parsed_args.security_groups) - if parsed_args.name: - template['node_group_template']['name'] = parsed_args.name - data = client.node_group_templates.create( - **template['node_group_template']).to_dict() + + data = self._import_take_action(client, parsed_args) _format_ngt_output(data) data = utils.prepare_data(data, NGT_FIELDS) @@ -770,7 +583,7 @@ return self.dict2columns(data) -class ExportNodeGroupTemplate(command.Command): +class ExportNodeGroupTemplate(command.Command, utils.NodeGroupTemplatesUtils): """Export node group template to JSON""" log = logging.getLogger(__name__ + ".ExportNodeGroupTemplate") @@ -794,12 +607,4 @@ def take_action(self, parsed_args): self.log.debug("take_action(%s)", parsed_args) client = self.app.client_manager.data_processing - ngt_id = utils.get_resource_id( - client.node_group_templates, parsed_args.node_group_template) - response = client.node_group_templates.export(ngt_id) - result = json.dumps(response._info, indent=4)+"\n" - if parsed_args.file: - with open(parsed_args.file, "w+") as file: - file.write(result) - else: - sys.stdout.write(result) + self._export_take_action(client, parsed_args) diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/plugins.py python-saharaclient-2.0.0/saharaclient/osc/v1/plugins.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/plugins.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/plugins.py 2018-07-26 22:34:12.000000000 +0000 @@ -22,7 +22,7 @@ from oslo_log import log as logging from oslo_serialization import jsonutils -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils def _serialize_label_items(plugin): diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v1/utils.py python-saharaclient-2.0.0/saharaclient/osc/v1/utils.py --- python-saharaclient-1.6.0/saharaclient/osc/v1/utils.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v1/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +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 time - -from oslo_utils import timeutils -from oslo_utils import uuidutils - -from saharaclient.api import base - - -def get_resource(manager, name_or_id, **kwargs): - if uuidutils.is_uuid_like(name_or_id): - return manager.get(name_or_id, **kwargs) - else: - resource = manager.find_unique(name=name_or_id) - if kwargs: - # we really need additional call to apply kwargs - resource = manager.get(resource.id, **kwargs) - return resource - - -def created_at_sorted(objs, reverse=False): - return sorted(objs, key=created_at_key, reverse=reverse) - - -def random_name(prefix=None): - return "%s-%s" % (prefix, uuidutils.generate_uuid()[:8]) - - -def created_at_key(obj): - return timeutils.parse_isotime(obj["created_at"]) - - -def get_resource_id(manager, name_or_id): - if uuidutils.is_uuid_like(name_or_id): - return name_or_id - else: - return manager.find_unique(name=name_or_id).id - - -def create_dict_from_kwargs(**kwargs): - return {k: v for (k, v) in kwargs.items() if v is not None} - - -def prepare_data(data, fields): - new_data = {} - for f in fields: - if f in data: - new_data[f.replace('_', ' ').capitalize()] = data[f] - - return new_data - - -def unzip(data): - return zip(*data) - - -def extend_columns(columns, items): - return unzip(list(unzip(columns)) + [('', '')] + items) - - -def prepare_column_headers(columns, remap=None): - remap = remap if remap else {} - new_columns = [] - for c in columns: - for old, new in remap.items(): - c = c.replace(old, new) - new_columns.append(c.replace('_', ' ').capitalize()) - - return new_columns - - -def get_by_name_substring(data, name): - return [obj for obj in data if name in obj.name] - - -def wait_for_delete(manager, obj_id, sleep_time=5, timeout=3000): - s_time = timeutils.utcnow() - while timeutils.delta_seconds(s_time, timeutils.utcnow()) < timeout: - try: - manager.get(obj_id) - except base.APIException as ex: - if ex.error_code == 404: - return True - raise - time.sleep(sleep_time) - - return False diff -Nru python-saharaclient-1.6.0/saharaclient/osc/v2/node_group_templates.py python-saharaclient-2.0.0/saharaclient/osc/v2/node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/osc/v2/node_group_templates.py 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/osc/v2/node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,176 @@ +# Copyright (c) 2018 Red Hat 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 osc_lib import utils as osc_utils + +from saharaclient.osc import utils +from saharaclient.osc.v1 import node_group_templates as ngt_v1 + +NGT_FIELDS = ['id', 'name', 'plugin_name', 'plugin_version', 'node_processes', + 'description', 'auto_security_group', 'security_groups', + 'availability_zone', 'flavor_id', 'floating_ip_pool', + 'volumes_per_node', 'volumes_size', + 'volume_type', 'volume_local_to_instance', 'volume_mount_prefix', + 'volumes_availability_zone', 'use_autoconfig', + 'is_proxy_gateway', 'is_default', 'is_protected', 'is_public', + 'boot_from_volume'] + + +def _format_ngt_output(data): + data['node_processes'] = osc_utils.format_list(data['node_processes']) + if data['volumes_per_node'] == 0: + del data['volume_local_to_instance'] + del data['volume_mount_prefix'] + del data['volume_type'], + del data['volumes_availability_zone'] + del data['volumes_size'] + + +class CreateNodeGroupTemplate(ngt_v1.CreateNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Creates node group template""" + + def get_parser(self, prog_name): + parser = super(CreateNodeGroupTemplate, self).get_parser(prog_name) + + parser.add_argument( + '--boot-from-volume', + action='store_true', + default=False, + help="Make the node group bootable from volume", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + + data = self._create_take_action(client, self.app, parsed_args) + + _format_ngt_output(data) + data = utils.prepare_data(data, NGT_FIELDS) + + return self.dict2columns(data) + + +class ListNodeGroupTemplates(ngt_v1.ListNodeGroupTemplates, + utils.NodeGroupTemplatesUtils): + """Lists node group templates""" + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + return self._list_take_action(client, self.app, parsed_args) + + +class ShowNodeGroupTemplate(ngt_v1.ShowNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Display node group template details""" + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + + data = utils.get_resource( + client.node_group_templates, + parsed_args.node_group_template).to_dict() + + _format_ngt_output(data) + + data = utils.prepare_data(data, NGT_FIELDS) + + return self.dict2columns(data) + + +class DeleteNodeGroupTemplate(ngt_v1.DeleteNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Deletes node group template""" + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + for ngt in parsed_args.node_group_template: + ngt_id = utils.get_resource_id( + client.node_group_templates, ngt) + client.node_group_templates.delete(ngt_id) + sys.stdout.write( + 'Node group template "{ngt}" has been removed ' + 'successfully.\n'.format(ngt=ngt)) + + +class UpdateNodeGroupTemplate(ngt_v1.UpdateNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Updates node group template""" + + def get_parser(self, prog_name): + parser = super(UpdateNodeGroupTemplate, self).get_parser(prog_name) + + bootfromvolume = parser.add_mutually_exclusive_group() + bootfromvolume.add_argument( + '--boot-from-volume-enable', + action='store_true', + help='Makes node group bootable from volume.', + dest='boot_from_volume' + ) + bootfromvolume.add_argument( + '--boot-from-volume-disable', + action='store_false', + help='Makes node group not bootable from volume.', + dest='boot_from_volume' + ) + parser.set_defaults(is_public=None, is_protected=None, + is_proxy_gateway=None, volume_locality=None, + use_auto_security_group=None, use_autoconfig=None, + boot_from_volume=None) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + + data = self._update_take_action(client, self.app, parsed_args) + + _format_ngt_output(data) + data = utils.prepare_data(data, NGT_FIELDS) + + return self.dict2columns(data) + + +class ImportNodeGroupTemplate(ngt_v1.ImportNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Imports node group template""" + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + + data = self._import_take_action(client, parsed_args) + + _format_ngt_output(data) + data = utils.prepare_data(data, NGT_FIELDS) + + return self.dict2columns(data) + + +class ExportNodeGroupTemplate(ngt_v1.ExportNodeGroupTemplate, + utils.NodeGroupTemplatesUtils): + """Export node group template to JSON""" + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)", parsed_args) + client = self.app.client_manager.data_processing + self._export_take_action(client, parsed_args) diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/base.py python-saharaclient-2.0.0/saharaclient/tests/unit/base.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/base.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/base.py 2018-07-26 22:34:12.000000000 +0000 @@ -17,19 +17,21 @@ from saharaclient.api import base from saharaclient.api import client +from keystoneauth1 import session from requests_mock.contrib import fixture class BaseTestCase(testtools.TestCase): URL = 'http://localhost:8386' - TOKEN = 'token' + SESSION = session.Session() def setUp(self): super(BaseTestCase, self).setUp() self.responses = self.useFixture(fixture.Fixture()) - self.client = client.Client(sahara_url=self.URL, - input_auth_token=self.TOKEN) + self.client = client.Client(session=self.SESSION, sahara_url=self.URL) + self.client_v2 = client.ClientV2(session=self.SESSION, + sahara_url=self.URL) def assertFields(self, body, obj): for key, value in body.items(): diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/osc/test_plugin.py python-saharaclient-2.0.0/saharaclient/tests/unit/osc/test_plugin.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/osc/test_plugin.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/osc/test_plugin.py 2018-07-26 22:34:12.000000000 +0000 @@ -28,15 +28,27 @@ instance._api_version = {"data_processing": '1.1'} instance.session = 'session' instance._region_name = 'region_name' - instance._cacert = 'cacert' - instance._insecure = 'insecure' instance._cli_options.data_processing_url = 'url' instance._interface = 'public' plugin.make_client(instance) p_client.assert_called_with(session='session', region_name='region_name', - cacert='cacert', - insecure='insecure', + sahara_url='url', + endpoint_type='public') + + @mock.patch("saharaclient.api.client.ClientV2") + def test_make_client_v2(self, p_client): + + instance = mock.Mock() + instance._api_version = {"data_processing": '2'} + instance.session = 'session' + instance._region_name = 'region_name' + instance._cli_options.data_processing_url = 'url' + instance._interface = 'public' + + plugin.make_client(instance) + p_client.assert_called_with(session='session', + region_name='region_name', sahara_url='url', endpoint_type='public') diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_data_sources.py python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_data_sources.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_data_sources.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_data_sources.py 2018-07-26 22:34:12.000000000 +0000 @@ -15,6 +15,7 @@ import mock from osc_lib.tests import utils as osc_utils +import testtools from saharaclient.api import data_sources as api_ds from saharaclient.osc.v1 import data_sources as osc_ds @@ -65,7 +66,8 @@ 'data_source_type': 'swift', 'name': 'source', 'description': '', 'url': 'swift://container.sahara/object', - 'is_public': False, 'is_protected': False} + 'is_public': False, 'is_protected': False, + 's3_credentials': None} self.ds_mock.create.assert_called_once_with(**called_args) # Check that columns are correct @@ -98,7 +100,8 @@ 'data_source_type': 'swift', 'name': 'source', 'description': 'Data Source for tests', 'url': 'swift://container.sahara/object', - 'is_protected': True, 'is_public': True} + 'is_protected': True, 'is_public': True, + 's3_credentials': None} self.ds_mock.create.assert_called_once_with(**called_args) # Check that columns are correct @@ -111,6 +114,13 @@ 'swift', 'swift://container.sahara/object') self.assertEqual(expected_data, data) + def test_data_source_create_mutual_exclusion(self): + arglist = ['data-source', '--name', 'data-source', '--access-key', + 'ak', '--secret-key', 'sk', '--url', 's3a://abc/def', + '--password', 'pw'] + with testtools.ExpectedException(osc_utils.ParserException): + self.check_parser(self.cmd, arglist, mock.Mock()) + class TestListDataSources(TestDataSources): def setUp(self): @@ -304,3 +314,11 @@ # Check that data source was created with correct arguments self.ds_mock.update.assert_called_once_with( 'id', {'is_public': False, 'is_protected': False}) + + def test_data_source_update_mutual_exclusion(self): + arglist = ['data-source', '--name', 'data-source', '--access-key', + 'ak', '--secret-key', 'sk', '--url', 's3a://abc/def', + '--password', 'pw'] + + with testtools.ExpectedException(osc_utils.ParserException): + self.check_parser(self.cmd, arglist, mock.Mock()) diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_node_group_templates.py python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_node_group_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -59,6 +59,7 @@ self.ngt_mock = ( self.app.client_manager.data_processing.node_group_templates) self.ngt_mock.reset_mock() + self.app.api_version['data_processing'] = '1' class TestCreateNodeGroupTemplate(TestNodeGroupTemplates): diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_utils.py python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_utils.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v1/test_utils.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v1/test_utils.py 2018-07-26 22:34:12.000000000 +0000 @@ -15,7 +15,7 @@ import mock -from saharaclient.osc.v1 import utils +from saharaclient.osc import utils from saharaclient.tests.unit import base diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v2/test_node_group_templates.py python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v2/test_node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/osc/v2/test_node_group_templates.py 1970-01-01 00:00:00.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/osc/v2/test_node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -0,0 +1,412 @@ +# 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 mock +from osc_lib.tests import utils as osc_utils + +from saharaclient.api import node_group_templates as api_ngt +from saharaclient.osc.v2 import node_group_templates as osc_ngt +from saharaclient.tests.unit.osc.v1 import fakes + + +NGT_INFO = { + "node_processes": [ + "namenode", + "tasktracker" + ], + "name": "template", + "tenant_id": "tenant_id", + "availability_zone": 'av_zone', + "use_autoconfig": True, + "plugin_version": "0.1", + "shares": None, + "is_default": False, + "description": 'description', + "node_configs": {}, + "is_proxy_gateway": False, + "auto_security_group": True, + "volume_type": None, + "volumes_size": 2, + "volume_mount_prefix": "/volumes/disk", + "plugin_name": "fake", + "is_protected": False, + "security_groups": None, + "floating_ip_pool": "floating_pool", + "is_public": True, + "id": "ng_id", + "flavor_id": "flavor_id", + "volumes_availability_zone": None, + "volumes_per_node": 2, + "volume_local_to_instance": False, + "boot_from_volume": False +} + + +class TestNodeGroupTemplates(fakes.TestDataProcessing): + def setUp(self): + super(TestNodeGroupTemplates, self).setUp() + self.ngt_mock = ( + self.app.client_manager.data_processing.node_group_templates) + self.ngt_mock.reset_mock() + self.app.api_version['data_processing'] = '2' + + +class TestCreateNodeGroupTemplate(TestNodeGroupTemplates): + # TODO(apavlov): check for creation with --json + def setUp(self): + super(TestCreateNodeGroupTemplate, self).setUp() + self.ngt_mock.create.return_value = api_ngt.NodeGroupTemplate( + None, NGT_INFO) + + self.fl_mock = self.app.client_manager.compute.flavors + self.fl_mock.get.return_value = mock.Mock(id='flavor_id') + self.fl_mock.reset_mock() + + # Command to test + self.cmd = osc_ngt.CreateNodeGroupTemplate(self.app, None) + + def test_ngt_create_minimum_options(self): + arglist = ['--name', 'template', '--plugin', 'fake', + '--plugin-version', '0.1', '--processes', 'namenode', + 'tasktracker', '--flavor', 'flavor_id'] + verifylist = [('name', 'template'), ('plugin', 'fake'), + ('plugin_version', '0.1'), ('flavor', 'flavor_id'), + ('processes', ['namenode', 'tasktracker'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.create.assert_called_once_with( + auto_security_group=False, availability_zone=None, + description=None, flavor_id='flavor_id', floating_ip_pool=None, + plugin_version='0.1', is_protected=False, is_proxy_gateway=False, + is_public=False, name='template', + node_processes=['namenode', 'tasktracker'], plugin_name='fake', + security_groups=None, use_autoconfig=False, + volume_local_to_instance=False, + volume_type=None, volumes_availability_zone=None, + volumes_per_node=None, volumes_size=None, shares=None, + node_configs=None, volume_mount_prefix=None, + boot_from_volume=False) + + def test_ngt_create_all_options(self): + arglist = ['--name', 'template', '--plugin', 'fake', + '--plugin-version', '0.1', '--processes', 'namenode', + 'tasktracker', '--security-groups', 'secgr', + '--auto-security-group', '--availability-zone', 'av_zone', + '--flavor', 'flavor_id', '--floating-ip-pool', + 'floating_pool', '--volumes-per-node', + '2', '--volumes-size', '2', '--volumes-type', 'type', + '--volumes-availability-zone', 'vavzone', + '--volumes-mount-prefix', '/volume/asd', + '--volumes-locality', '--description', 'descr', + '--autoconfig', '--proxy-gateway', '--public', + '--protected', '--boot-from-volume'] + + verifylist = [('name', 'template'), ('plugin', 'fake'), + ('plugin_version', '0.1'), + ('processes', ['namenode', 'tasktracker']), + ('security_groups', ['secgr']), + ('auto_security_group', True), + ('availability_zone', 'av_zone'), + ('flavor', 'flavor_id'), + ('floating_ip_pool', 'floating_pool'), + ('volumes_per_node', 2), ('volumes_size', 2), + ('volumes_type', 'type'), + ('volumes_availability_zone', 'vavzone'), + ('volumes_mount_prefix', '/volume/asd'), + ('volumes_locality', True), ('description', 'descr'), + ('autoconfig', True), ('proxy_gateway', True), + ('public', True), ('protected', True), + ('boot_from_volume', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.create.assert_called_once_with( + auto_security_group=True, availability_zone='av_zone', + description='descr', flavor_id='flavor_id', + floating_ip_pool='floating_pool', plugin_version='0.1', + is_protected=True, is_proxy_gateway=True, is_public=True, + name='template', node_processes=['namenode', 'tasktracker'], + plugin_name='fake', security_groups=['secgr'], use_autoconfig=True, + volume_local_to_instance=True, volume_type='type', + volumes_availability_zone='vavzone', volumes_per_node=2, + volumes_size=2, shares=None, node_configs=None, + volume_mount_prefix='/volume/asd', boot_from_volume=True) + + # Check that columns are correct + expected_columns = ( + 'Auto security group', 'Availability zone', 'Boot from volume', + 'Description', 'Flavor id', 'Floating ip pool', 'Id', + 'Is default', 'Is protected', 'Is proxy gateway', 'Is public', + 'Name', 'Node processes', 'Plugin name', 'Plugin version', + 'Security groups', 'Use autoconfig', 'Volume local to instance', + 'Volume mount prefix', 'Volume type', 'Volumes availability zone', + 'Volumes per node', 'Volumes size') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ( + True, 'av_zone', False, 'description', 'flavor_id', + 'floating_pool', 'ng_id', False, False, False, True, + 'template', 'namenode, tasktracker', 'fake', '0.1', None, True, + False, '/volumes/disk', None, None, 2, 2) + self.assertEqual(expected_data, data) + + +class TestListNodeGroupTemplates(TestNodeGroupTemplates): + def setUp(self): + super(TestListNodeGroupTemplates, self).setUp() + self.ngt_mock.list.return_value = [api_ngt.NodeGroupTemplate( + None, NGT_INFO)] + + # Command to test + self.cmd = osc_ngt.ListNodeGroupTemplates(self.app, None) + + def test_ngt_list_no_options(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['Name', 'Id', 'Plugin name', 'Plugin version'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('template', 'ng_id', 'fake', '0.1')] + self.assertEqual(expected_data, list(data)) + + def test_ngt_list_long(self): + arglist = ['--long'] + verifylist = [('long', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['Name', 'Id', 'Plugin name', 'Plugin version', + 'Node processes', 'Description'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('template', 'ng_id', 'fake', '0.1', + 'namenode, tasktracker', 'description')] + self.assertEqual(expected_data, list(data)) + + def test_ngt_list_extra_search_opts(self): + arglist = ['--plugin', 'fake', '--plugin-version', '0.1', '--name', + 'templ'] + verifylist = [('plugin', 'fake'), ('plugin_version', '0.1'), + ('name', 'templ')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that columns are correct + expected_columns = ['Name', 'Id', 'Plugin name', 'Plugin version'] + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = [('template', 'ng_id', 'fake', '0.1')] + self.assertEqual(expected_data, list(data)) + + +class TestShowNodeGroupTemplate(TestNodeGroupTemplates): + def setUp(self): + super(TestShowNodeGroupTemplate, self).setUp() + self.ngt_mock.find_unique.return_value = api_ngt.NodeGroupTemplate( + None, NGT_INFO) + + # Command to test + self.cmd = osc_ngt.ShowNodeGroupTemplate(self.app, None) + + def test_ngt_show(self): + arglist = ['template'] + verifylist = [('node_group_template', 'template')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.find_unique.assert_called_once_with(name='template') + + # Check that columns are correct + expected_columns = ( + 'Auto security group', 'Availability zone', 'Boot from volume', + 'Description', 'Flavor id', 'Floating ip pool', 'Id', + 'Is default', 'Is protected', 'Is proxy gateway', 'Is public', + 'Name', 'Node processes', 'Plugin name', 'Plugin version', + 'Security groups', 'Use autoconfig', 'Volume local to instance', + 'Volume mount prefix', 'Volume type', 'Volumes availability zone', + 'Volumes per node', 'Volumes size') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ( + True, 'av_zone', False, 'description', 'flavor_id', + 'floating_pool', 'ng_id', False, False, False, True, + 'template', 'namenode, tasktracker', 'fake', '0.1', None, True, + False, '/volumes/disk', None, None, 2, 2) + self.assertEqual(expected_data, data) + + +class TestDeleteNodeGroupTemplate(TestNodeGroupTemplates): + def setUp(self): + super(TestDeleteNodeGroupTemplate, self).setUp() + self.ngt_mock.find_unique.return_value = api_ngt.NodeGroupTemplate( + None, NGT_INFO) + + # Command to test + self.cmd = osc_ngt.DeleteNodeGroupTemplate(self.app, None) + + def test_ngt_delete(self): + arglist = ['template'] + verifylist = [('node_group_template', ['template'])] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.delete.assert_called_once_with('ng_id') + + +class TestUpdateNodeGroupTemplate(TestNodeGroupTemplates): + # TODO(apavlov): check for update with --json + def setUp(self): + super(TestUpdateNodeGroupTemplate, self).setUp() + self.ngt_mock.find_unique.return_value = api_ngt.NodeGroupTemplate( + None, NGT_INFO) + self.ngt_mock.update.return_value = api_ngt.NodeGroupTemplate( + None, NGT_INFO) + + self.fl_mock = self.app.client_manager.compute.flavors + self.fl_mock.get.return_value = mock.Mock(id='flavor_id') + self.fl_mock.reset_mock() + + # Command to test + self.cmd = osc_ngt.UpdateNodeGroupTemplate(self.app, None) + + def test_ngt_update_no_options(self): + arglist = [] + verifylist = [] + + self.assertRaises(osc_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_ngt_update_nothing_updated(self): + arglist = ['template'] + verifylist = [('node_group_template', 'template')] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.update.assert_called_once_with('ng_id') + + def test_ngt_update_all_options(self): + arglist = ['template', '--name', 'template', '--plugin', 'fake', + '--plugin-version', '0.1', '--processes', 'namenode', + 'tasktracker', '--security-groups', 'secgr', + '--auto-security-group-enable', + '--availability-zone', 'av_zone', '--flavor', 'flavor_id', + '--floating-ip-pool', 'floating_pool', '--volumes-per-node', + '2', '--volumes-size', '2', '--volumes-type', 'type', + '--volumes-availability-zone', 'vavzone', + '--volumes-mount-prefix', '/volume/asd', + '--volumes-locality-enable', '--description', 'descr', + '--autoconfig-enable', '--proxy-gateway-enable', '--public', + '--protected', '--boot-from-volume-enable'] + + verifylist = [('node_group_template', 'template'), + ('name', 'template'), ('plugin', 'fake'), + ('plugin_version', '0.1'), + ('processes', ['namenode', 'tasktracker']), + ('security_groups', ['secgr']), + ('use_auto_security_group', True), + ('availability_zone', 'av_zone'), + ('flavor', 'flavor_id'), + ('floating_ip_pool', 'floating_pool'), + ('volumes_per_node', 2), ('volumes_size', 2), + ('volumes_type', 'type'), + ('volumes_availability_zone', 'vavzone'), + ('volumes_mount_prefix', '/volume/asd'), + ('volume_locality', True), + ('description', 'descr'), ('use_autoconfig', True), + ('is_proxy_gateway', True), + ('is_public', True), ('is_protected', True), + ('boot_from_volume', True)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.update.assert_called_once_with( + 'ng_id', + auto_security_group=True, availability_zone='av_zone', + description='descr', flavor_id='flavor_id', + floating_ip_pool='floating_pool', plugin_version='0.1', + is_protected=True, is_proxy_gateway=True, is_public=True, + name='template', node_processes=['namenode', 'tasktracker'], + plugin_name='fake', security_groups=['secgr'], use_autoconfig=True, + volume_local_to_instance=True, volume_type='type', + volumes_availability_zone='vavzone', volumes_per_node=2, + volumes_size=2, volume_mount_prefix='/volume/asd', + boot_from_volume=True) + + # Check that columns are correct + expected_columns = ( + 'Auto security group', 'Availability zone', 'Boot from volume', + 'Description', 'Flavor id', 'Floating ip pool', 'Id', + 'Is default', 'Is protected', 'Is proxy gateway', 'Is public', + 'Name', 'Node processes', 'Plugin name', 'Plugin version', + 'Security groups', 'Use autoconfig', 'Volume local to instance', + 'Volume mount prefix', 'Volume type', 'Volumes availability zone', + 'Volumes per node', 'Volumes size') + self.assertEqual(expected_columns, columns) + + # Check that data is correct + expected_data = ( + True, 'av_zone', False, 'description', 'flavor_id', + 'floating_pool', 'ng_id', False, False, False, True, + 'template', 'namenode, tasktracker', 'fake', '0.1', None, True, + False, '/volumes/disk', None, None, 2, 2) + self.assertEqual(expected_data, data) + + def test_ngt_update_private_unprotected(self): + arglist = ['template', '--private', '--unprotected'] + verifylist = [('node_group_template', 'template'), + ('is_public', False), ('is_protected', False)] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + # Check that correct arguments were passed + self.ngt_mock.update.assert_called_once_with( + 'ng_id', is_protected=False, is_public=False) diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/test_data_sources.py python-saharaclient-2.0.0/saharaclient/tests/unit/test_data_sources.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/test_data_sources.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/test_data_sources.py 2018-07-26 22:34:12.000000000 +0000 @@ -15,6 +15,7 @@ from saharaclient.api import data_sources as ds from saharaclient.tests.unit import base +import mock from oslo_serialization import jsonutils as json @@ -92,3 +93,27 @@ updated = self.client.data_sources.update("id", self.update_json) self.assertEqual(self.update_json["name"], updated.name) self.assertEqual(self.update_json["url"], updated.url) + + @mock.patch('saharaclient.api.base.ResourceManager._create') + def test_create_data_source_s3_or_swift_credentials(self, create): + # Data source without any credential arguments + self.client.data_sources.create('ds', '', 'swift', 'swift://path') + self.assertNotIn('credentials', create.call_args[0][1]) + + # Data source with Swift credential arguments + self.client.data_sources.create('ds', '', 'swift', 'swift://path', + credential_user='user') + self.assertIn('credentials', create.call_args[0][1]) + + # Data source with S3 credential arguments + self.client.data_sources.create('ds', '', 'swift', 'swift://path', + s3_credentials={'accesskey': 'a'}) + self.assertIn('credentials', create.call_args[0][1]) + self.assertIn('accesskey', create.call_args[0][1]['credentials']) + + # Data source with both S3 and swift credential arguments + self.client.data_sources.create('ds', '', 's3', 's3://path', + credential_user='swift_user', + s3_credentials={'accesskey': 's3_a'}) + self.assertIn('user', create.call_args[0][1]['credentials']) + self.assertNotIn('accesskey', create.call_args[0][1]['credentials']) diff -Nru python-saharaclient-1.6.0/saharaclient/tests/unit/test_node_group_templates.py python-saharaclient-2.0.0/saharaclient/tests/unit/test_node_group_templates.py --- python-saharaclient-1.6.0/saharaclient/tests/unit/test_node_group_templates.py 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/saharaclient/tests/unit/test_node_group_templates.py 2018-07-26 22:34:12.000000000 +0000 @@ -154,3 +154,111 @@ self.assertEqual(url, self.responses.last_request.url) self.assertIsInstance(resp, ng.NodeGroupTemplate) self.assertDictsEqual(self.body, resp.__dict__[u'node_group_template']) + + +class NodeGroupTemplateTestV2(base.BaseTestCase): + body = { + "name": "name", + "plugin_name": "plugin", + "plugin_version": "1", + "flavor_id": "2", + "description": "description", + "volumes_per_node": "3", + "volumes_size": "4", + "node_processes": ["datanode"], + "use_autoconfig": True, + "volume_mount_prefix": '/volumes/disk', + "boot_from_volume": False + } + + update_json = { + "node_group_template": { + "name": "UpdatedName", + "plugin_name": "new_plugin", + "plugin_version": "2", + "flavor_id": "7", + "description": "description", + "volumes_per_node": "3", + "volumes_size": "4", + "node_processes": ["datanode", "namenode"], + "use_autoconfig": False, + "volume_mount_prefix": '/volumes/newdisk', + "boot_from_volume": True + } + } + + def test_create_node_group_template_v2(self): + url = self.URL + '/node-group-templates' + self.responses.post(url, status_code=202, + json={'node_group_template': self.body}) + + resp = self.client_v2.node_group_templates.create(**self.body) + + self.assertEqual(url, self.responses.last_request.url) + self.assertEqual(self.body, + json.loads(self.responses.last_request.body)) + self.assertIsInstance(resp, ng.NodeGroupTemplate) + self.assertFields(self.body, resp) + + def test_update_node_group_template_v2(self): + url = self.URL + '/node-group-templates' + self.responses.post(url, status_code=202, + json={'node_group_template': self.body}) + resp = self.client_v2.node_group_templates.create(**self.body) + + update_url = self.URL + '/node-group-templates/id' + self.responses.patch(update_url, status_code=202, + json=self.update_json) + + # check that all parameters will be updated + updated = self.client_v2.node_group_templates.update( + "id", + resp.name, + resp.plugin_name, + resp.plugin_version, + resp.flavor_id, + description=getattr(resp, "description", None), + volumes_per_node=getattr(resp, "volumes_per_node", None), + node_configs=getattr(resp, "node_configs", None), + floating_ip_pool=getattr(resp, "floating_ip_pool", None), + security_groups=getattr(resp, "security_groups", None), + auto_security_group=getattr(resp, "auto_security_group", None), + availability_zone=getattr(resp, "availability_zone", None), + volumes_availability_zone=getattr(resp, + "volumes_availability_zone", + None), + volume_type=getattr(resp, "volume_type", None), + image_id=getattr(resp, "image_id", None), + is_proxy_gateway=getattr(resp, "is_proxy_gateway", None), + volume_local_to_instance=getattr(resp, + "volume_local_to_instance", + None), + use_autoconfig=False, + boot_from_volume=getattr(resp, "boot_from_volume", None) + ) + self.assertIsInstance(updated, ng.NodeGroupTemplate) + self.assertFields(self.update_json["node_group_template"], updated) + + # check that parameters will not be updated + self.client_v2.node_group_templates.update("id") + self.assertEqual(update_url, self.responses.last_request.url) + self.assertEqual({}, + json.loads(self.responses.last_request.body)) + + # check that all parameters will be unset + unset_json = { + 'auto_security_group': None, 'availability_zone': None, + 'description': None, 'flavor_id': None, 'floating_ip_pool': None, + 'plugin_version': None, 'image_id': None, 'is_protected': None, + 'is_proxy_gateway': None, 'is_public': None, 'name': None, + 'node_configs': None, 'node_processes': None, 'plugin_name': None, + 'security_groups': None, 'shares': None, 'use_autoconfig': None, + 'volume_local_to_instance': None, 'volume_mount_prefix': None, + 'volume_type': None, 'volumes_availability_zone': None, + 'volumes_per_node': None, 'volumes_size': None, + 'boot_from_volume': None} + + self.client_v2.node_group_templates.update("id", **unset_json) + self.assertEqual(update_url, self.responses.last_request.url) + self.assertEqual(unset_json, + json.loads(self.responses.last_request.body)) diff -Nru python-saharaclient-1.6.0/setup.cfg python-saharaclient-2.0.0/setup.cfg --- python-saharaclient-1.6.0/setup.cfg 2018-04-19 22:21:51.000000000 +0000 +++ python-saharaclient-2.0.0/setup.cfg 2018-07-26 22:37:15.000000000 +0000 @@ -34,11 +34,13 @@ dataprocessing_plugin_show = saharaclient.osc.v1.plugins:ShowPlugin dataprocessing_plugin_configs_get = saharaclient.osc.v1.plugins:GetPluginConfigs dataprocessing_plugin_update = saharaclient.osc.v1.plugins:UpdatePlugin + dataprocessing_data_source_create = saharaclient.osc.v1.data_sources:CreateDataSource dataprocessing_data_source_list = saharaclient.osc.v1.data_sources:ListDataSources dataprocessing_data_source_show = saharaclient.osc.v1.data_sources:ShowDataSource dataprocessing_data_source_delete = saharaclient.osc.v1.data_sources:DeleteDataSource dataprocessing_data_source_update = saharaclient.osc.v1.data_sources:UpdateDataSource + dataprocessing_image_list = saharaclient.osc.v1.images:ListImages dataprocessing_image_show = saharaclient.osc.v1.images:ShowImage dataprocessing_image_register = saharaclient.osc.v1.images:RegisterImage @@ -46,6 +48,7 @@ dataprocessing_image_tags_add = saharaclient.osc.v1.images:AddImageTags dataprocessing_image_tags_remove = saharaclient.osc.v1.images:RemoveImageTags dataprocessing_image_tags_set = saharaclient.osc.v1.images:SetImageTags + dataprocessing_node_group_template_create = saharaclient.osc.v1.node_group_templates:CreateNodeGroupTemplate dataprocessing_node_group_template_list = saharaclient.osc.v1.node_group_templates:ListNodeGroupTemplates dataprocessing_node_group_template_show = saharaclient.osc.v1.node_group_templates:ShowNodeGroupTemplate @@ -53,6 +56,7 @@ dataprocessing_node_group_template_delete = saharaclient.osc.v1.node_group_templates:DeleteNodeGroupTemplate dataprocessing_node_group_template_import = saharaclient.osc.v1.node_group_templates:ImportNodeGroupTemplate dataprocessing_node_group_template_export = saharaclient.osc.v1.node_group_templates:ExportNodeGroupTemplate + dataprocessing_cluster_template_create = saharaclient.osc.v1.cluster_templates:CreateClusterTemplate dataprocessing_cluster_template_list = saharaclient.osc.v1.cluster_templates:ListClusterTemplates dataprocessing_cluster_template_show = saharaclient.osc.v1.cluster_templates:ShowClusterTemplate @@ -60,6 +64,7 @@ dataprocessing_cluster_template_delete = saharaclient.osc.v1.cluster_templates:DeleteClusterTemplate dataprocessing_cluster_template_import = saharaclient.osc.v1.cluster_templates:ImportClusterTemplate dataprocessing_cluster_template_export = saharaclient.osc.v1.cluster_templates:ExportClusterTemplate + dataprocessing_cluster_create = saharaclient.osc.v1.clusters:CreateCluster dataprocessing_cluster_list = saharaclient.osc.v1.clusters:ListClusters dataprocessing_cluster_show = saharaclient.osc.v1.clusters:ShowCluster @@ -67,24 +72,36 @@ dataprocessing_cluster_delete = saharaclient.osc.v1.clusters:DeleteCluster dataprocessing_cluster_scale = saharaclient.osc.v1.clusters:ScaleCluster dataprocessing_cluster_verification = saharaclient.osc.v1.clusters:VerificationUpdateCluster + dataprocessing_job_template_create = saharaclient.osc.v1.job_templates:CreateJobTemplate dataprocessing_job_template_list = saharaclient.osc.v1.job_templates:ListJobTemplates dataprocessing_job_template_show = saharaclient.osc.v1.job_templates:ShowJobTemplate dataprocessing_job_template_update = saharaclient.osc.v1.job_templates:UpdateJobTemplate dataprocessing_job_template_delete = saharaclient.osc.v1.job_templates:DeleteJobTemplate + dataprocessing_job_type_list = saharaclient.osc.v1.job_types:ListJobTypes dataprocessing_job_type_configs_get = saharaclient.osc.v1.job_types:GetJobTypeConfigs + dataprocessing_job_execute = saharaclient.osc.v1.jobs:ExecuteJob dataprocessing_job_list = saharaclient.osc.v1.jobs:ListJobs dataprocessing_job_show = saharaclient.osc.v1.jobs:ShowJob dataprocessing_job_update = saharaclient.osc.v1.jobs:UpdateJob dataprocessing_job_delete = saharaclient.osc.v1.jobs:DeleteJob + dataprocessing_job_binary_create = saharaclient.osc.v1.job_binaries:CreateJobBinary dataprocessing_job_binary_list = saharaclient.osc.v1.job_binaries:ListJobBinaries dataprocessing_job_binary_show = saharaclient.osc.v1.job_binaries:ShowJobBinary dataprocessing_job_binary_update = saharaclient.osc.v1.job_binaries:UpdateJobBinary dataprocessing_job_binary_delete = saharaclient.osc.v1.job_binaries:DeleteJobBinary dataprocessing_job_binary_download = saharaclient.osc.v1.job_binaries:DownloadJobBinary +openstack.data_processing.v2 = + dataprocessing_node_group_template_create = saharaclient.osc.v2.node_group_templates:CreateNodeGroupTemplate + dataprocessing_node_group_template_list = saharaclient.osc.v2.node_group_templates:ListNodeGroupTemplates + dataprocessing_node_group_template_show = saharaclient.osc.v2.node_group_templates:ShowNodeGroupTemplate + dataprocessing_node_group_template_update = saharaclient.osc.v2.node_group_templates:UpdateNodeGroupTemplate + dataprocessing_node_group_template_delete = saharaclient.osc.v2.node_group_templates:DeleteNodeGroupTemplate + dataprocessing_node_group_template_import = saharaclient.osc.v2.node_group_templates:ImportNodeGroupTemplate + dataprocessing_node_group_template_export = saharaclient.osc.v2.node_group_templates:ExportNodeGroupTemplate [wheel] universal = 1 diff -Nru python-saharaclient-1.6.0/test-requirements.txt python-saharaclient-2.0.0/test-requirements.txt --- python-saharaclient-1.6.0/test-requirements.txt 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/test-requirements.txt 2018-07-26 22:34:12.000000000 +0000 @@ -7,6 +7,5 @@ coverage!=4.4,>=4.0 # Apache-2.0 mock>=2.0.0 # BSD oslotest>=3.2.0 # Apache-2.0 -os-testr>=1.0.0 # Apache-2.0 +stestr>=1.0.0 # Apache-2.0 requests-mock>=1.2.0 # Apache-2.0 -testrepository>=0.0.18 # Apache-2.0/BSD diff -Nru python-saharaclient-1.6.0/tox.ini python-saharaclient-2.0.0/tox.ini --- python-saharaclient-1.6.0/tox.ini 2018-04-19 22:19:08.000000000 +0000 +++ python-saharaclient-2.0.0/tox.ini 2018-07-26 22:34:12.000000000 +0000 @@ -14,12 +14,13 @@ -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = find . -type f -name "*.pyc" -delete - ostestr {posargs} + stestr run {posargs} whitelist_externals = find rm passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY [testenv:debug] +basepython = python3 commands = oslo_debug_helper -t saharaclient/tests/unit {posargs} [testenv:debug-py27] @@ -31,18 +32,29 @@ commands = oslo_debug_helper -t saharaclient/tests/unit {posargs} [testenv:cover] +basepython = python3 +setenv = + {[testenv]setenv} + PYTHON=coverage run --source saharaclient --parallel-mode commands = - python setup.py test --coverage --testr-args='{posargs}' - coverage report + coverage erase + find . -type f -name "*.pyc" -delete + stestr run {posargs} + coverage combine + coverage html -d cover + coverage xml -o cover/coverage.xml + coverage report [tox:jenkins] sitepackages = False [testenv:pep8] +basepython = python3 sitepackages = False commands = flake8 [testenv:doc8] +basepython = python3 deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt @@ -51,9 +63,11 @@ commands = doc8 doc/source [testenv:venv] +basepython = python3 commands = {posargs} [testenv:docs] +basepython = python3 deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt @@ -63,6 +77,7 @@ sphinx-build -W -b html doc/source doc/build/html [testenv:releasenotes] +basepython = python3 deps = -c{env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} -r{toxinidir}/requirements.txt